From bd12f3c46ef54e526f9bbf032c709f3c456c1190 Mon Sep 17 00:00:00 2001 From: daijro Date: Thu, 1 Aug 2024 04:41:03 -0500 Subject: [PATCH] Many changes, bump to v128.0.3-1 - Heavy changes to Makefile. Now uses aria2c to download the Firefox release tarball - New features in developer UI to make patch editing much easier - Modified Playwright's Juggler patches to run on Firefox release v128.0.3 - Bump Playwright Juggler module to June 2th patches - Fix viewport-hijacker and xmas-modified patches for new Firefox release --- .gitignore | 2 + Dockerfile | 12 +- Makefile | 46 +- README.md | 32 +- additions/juggler/TargetRegistry.js | 4 +- .../juggler/content/JugglerFrameChild.jsm | 64 +- additions/juggler/content/PageAgent.js | 7 +- additions/juggler/content/main.js | 19 +- patches/backport-rust-crates.bootstrap | 21484 ---------------- ...s.bootstrap => 0-playwright-updated.patch} | 533 +- patches/viewport-hijacker.patch | 6 +- patches/xmas-modified.patch | 20 +- scripts/bootstrap.py | 30 +- scripts/copy-additions.sh | 54 + scripts/developer.py | 208 +- scripts/init-patch.py | 25 - scripts/package.py | 2 +- scripts/patch.py | 47 +- upstream.sh | 8 +- 19 files changed, 676 insertions(+), 21927 deletions(-) delete mode 100644 patches/backport-rust-crates.bootstrap rename patches/playwright/{playwright-patches.bootstrap => 0-playwright-updated.patch} (87%) create mode 100644 scripts/copy-additions.sh delete mode 100644 scripts/init-patch.py diff --git a/.gitignore b/.gitignore index 369b31c..6b88073 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /camoufox-* +/firefox-* /mozilla-unified /extra-docs /.vscode @@ -10,3 +11,4 @@ launch.exe *.old __pycache__/ *.pyc +wget-log diff --git a/Dockerfile b/Dockerfile index 7ff4140..00548d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,15 +7,15 @@ COPY . /app # Install necessary packages RUN apt-get update && apt-get install -y \ - # Makefile utils - build-essential make git msitools wget unzip \ + # Mach build tools + build-essential make msitools wget unzip \ # Python python3 python3-dev python3-pip \ - # Camoufox build system utils - p7zip-full golang-go + # Camoufox build system tools + git p7zip-full golang-go aria2c # Fetch Firefox & apply initial patches -RUN make fetch && \ +RUN make setup-minimal && \ make mozbootstrap && \ mkdir /app/dist @@ -23,4 +23,4 @@ RUN make fetch && \ VOLUME /root/.mozbuild VOLUME /app/dist -ENTRYPOINT ["python3", "./multibuild.py"] \ No newline at end of file +ENTRYPOINT ["python3", "./multibuild.py"] diff --git a/Makefile b/Makefile index 3a82ad3..4937b7f 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,21 @@ include upstream.sh export cf_source_dir := camoufox-$(version)-$(release) +ff_source_tarball := firefox-$(version).source.tar.xz -debs := python3 python3-dev python3-pip p7zip-full golang-go msitools wget -rpms := python3 python3-devel p7zip golang msitools wget -pacman := python python-pip p7zip go msitools wget +debs := python3 python3-dev python3-pip p7zip-full golang-go msitools wget aria2c +rpms := python3 python3-devel p7zip golang msitools wget aria2c +pacman := python python-pip p7zip go msitools wget aria2c -.PHONY: help fetch clean distclean build package build-launcher check-arch revert edits run bootstrap mozbootstrap dir package-common package-linux package-macos package-windows +.PHONY: help fetch setup setup-minimal clean distclean build package build-launcher check-arch revert edits run bootstrap mozbootstrap dir package-common package-linux package-macos package-windows help: @echo "Available targets:" - @echo " fetch - Clone Firefox source code" + @echo " fetch - Fetch the Firefox source code" + @echo " setup - Setup Camoufox & local git repo for development" @echo " bootstrap - Set up build environment" @echo " mozbootstrap - Sets up mach" - @echo " dir - Prepare Camoufox source directory" + @echo " dir - Prepare Camoufox source directory with BUILD_TARGET" @echo " revert - Kill all working changes" @echo " edits - Camoufox developer UI" @echo " build-launcher - Build launcher" @@ -27,17 +29,35 @@ help: @echo " run - Run Camoufox" fetch: - git clone --depth 1 --branch $(BASE_BRANCH) --single-branch $(REMOTE_URL) $(cf_source_dir) - cd $(cf_source_dir) && git fetch --depth 1 origin $(BASE_REVISION) - make revert + aria2c -x16 -s16 -k1M -o $(ff_source_tarball) "https://archive.mozilla.org/pub/firefox/releases/$(version)/source/firefox-$(version).source.tar.xz"; \ + +setup-minimal: + # Note: Only docker containers are intended to run this directly. + # Run this before `make dir` or `make build` to avoid setting up a local git repo. + if [ ! -f $(ff_source_tarball) ]; then \ + make fetch; \ + fi + # Create new cf_source_dir + rm -rf $(cf_source_dir) + mkdir -p $(cf_source_dir) + tar -xJf $(ff_source_tarball) -C $(cf_source_dir) --strip-components=1 + # Copy settings & additions + cd $(cf_source_dir) && bash ../scripts/copy-additions.sh $(version) $(release) + +setup: setup-minimal + # Initialize local git repo for development + cd $(cf_source_dir) && \ + git init -b main && \ + git add -f -A && \ + git commit -m "Initial commit" && \ + git tag -a unpatched -m "Initial commit" revert: - cd $(cf_source_dir) && git reset --hard $(BASE_REVISION) - python3 scripts/init-patch.py $(version) $(release) + cd $(cf_source_dir) && git reset --hard unpatched dir: @if [ ! -d $(cf_source_dir) ]; then \ - make fetch; \ + make setup; \ fi make clean python3 scripts/patch.py $(version) $(release) @@ -61,7 +81,7 @@ clean: make revert distclean: - rm -rf $(cf_source_dir) + rm -rf $(cf_source_dir) $(ff_source_tarball) build: @if [ ! -f $(cf_source_dir)/_READY ]; then \ diff --git a/README.md b/README.md index 7b241da..08eeba7 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ Miscellaneous (WebGl spoofing, battery status, etc) #### Playwright support -- Added Playwright's Juggler patches +- A more updated version of Playwright's Juggler for the latest Firefox, maintained by me - Various config patches to evade bot detection #### Debloat/Optimizations @@ -386,9 +386,7 @@ Build artifacts will now appear written under the `dist/` folder. --- -## Development Notes - -### How to make a patch +## Development Tools This repo comes with a developer UI under scripts/developer.py: @@ -396,26 +394,20 @@ This repo comes with a developer UI under scripts/developer.py: make edits ``` -Patches can be added, removed, and new patches can be added through here. +Patches can be edited, created, removed, and managed through here. - + -To use, select "Reset workspace", make changes in the camoufox-\*/ folder, then select "Write workspace to patch". +### How to make a patch + +1. In the developer UI, click **Reset workspace**. +2. Make changes in the `camoufox-*/` folder as needed. You can test your changes with `make build` and `make run`. +3. After you're done making changes, click **Write workspace to patch** and save the patch file. ### How to work on an existing patch -1. In the developer UI, reset your workspace. -2. Then, click "Select patches", and select all **but** the one you want to edit. -3. Create a checkpoint. This will commit the current workspace to the local repo: - -```bash -make checkpoint -``` - -4. In the developer UI, click "Select patches", and _apply_ the patch you would like to edit. - - Make your changes. Test builds can be made with `make build` and `make run`. - -5. After you're done editing, hit "Write workspace to patch", and overwrite the existing patch. +1. In the developer UI, click **Edit a patch**. +2. Select the patch you'd like to edit. Your workspace will be reset to the state of the selected patch. +3. After you're done making changes, hit **Write workspace to patch** and overwrite the existing patch file. --- diff --git a/additions/juggler/TargetRegistry.js b/additions/juggler/TargetRegistry.js index ea7b2af..8c23ff8 100644 --- a/additions/juggler/TargetRegistry.js +++ b/additions/juggler/TargetRegistry.js @@ -368,7 +368,7 @@ class PageTarget { onLocationChange: (aWebProgress, aRequest, aLocation) => this._onNavigated(aLocation), }; this._eventListeners = [ - helper.addObserver(this._updateModalDialogs.bind(this), 'tabmodal-dialog-loaded'), + helper.addObserver(this._updateModalDialogs.bind(this), 'common-dialog-loaded'), helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION), helper.addEventListener(this._linkedBrowser, 'DOMModalDialogClosed', event => this._updateModalDialogs()), helper.addEventListener(this._linkedBrowser, 'WillChangeBrowserRemoteness', event => this._willChangeBrowserRemoteness()), @@ -499,7 +499,7 @@ class PageTarget { } _updateModalDialogs() { - const prompts = new Set(this._linkedBrowser.tabModalPromptBox ? this._linkedBrowser.tabModalPromptBox.listPrompts() : []); + const prompts = new Set(this._linkedBrowser.tabDialogBox.getContentDialogManager().dialogs.map(dialog => dialog.frameContentWindow.Dialog)); for (const dialog of this._dialogs.values()) { if (!prompts.has(dialog.prompt())) { this._dialogs.delete(dialog.id()); diff --git a/additions/juggler/content/JugglerFrameChild.jsm b/additions/juggler/content/JugglerFrameChild.jsm index 529f6d3..47fcabb 100644 --- a/additions/juggler/content/JugglerFrameChild.jsm +++ b/additions/juggler/content/JugglerFrameChild.jsm @@ -8,6 +8,8 @@ const helper = new Helper(); let sameProcessInstanceNumber = 0; +const topBrowingContextToAgents = new Map(); + class JugglerFrameChild extends JSWindowActorChild { constructor() { super(); @@ -16,46 +18,66 @@ class JugglerFrameChild extends JSWindowActorChild { } handleEvent(aEvent) { - if (this._agents && aEvent.type === 'DOMWillOpenModalDialog') { - this._agents.channel.pause(); + const agents = this._agents(); + if (!agents) + return; + if (aEvent.type === 'DOMWillOpenModalDialog') { + agents.channel.pause(); return; } - if (this._agents && aEvent.type === 'DOMModalDialogClosed') { - this._agents.channel.resumeSoon(); + if (aEvent.type === 'DOMModalDialogClosed') { + agents.channel.resumeSoon(); return; } - if (this._agents && aEvent.target === this.document) - this._agents.pageAgent.onWindowEvent(aEvent); - if (this._agents && aEvent.target === this.document) - this._agents.frameTree.onWindowEvent(aEvent); + if (aEvent.target === this.document) { + agents.pageAgent.onWindowEvent(aEvent); + agents.frameTree.onWindowEvent(aEvent); + } + } + + _agents() { + return topBrowingContextToAgents.get(this.browsingContext.top); } actorCreated() { this.actorName = `content::${this.browsingContext.browserId}/${this.browsingContext.id}/${++sameProcessInstanceNumber}`; this._eventListeners.push(helper.addEventListener(this.contentWindow, 'load', event => { - this._agents?.pageAgent.onWindowEvent(event); + this._agents()?.pageAgent.onWindowEvent(event); })); if (this.document.documentURI.startsWith('moz-extension://')) return; - this._agents = initialize(this.browsingContext, this.docShell, this); - } - _dispose() { - helper.removeListeners(this._eventListeners); - // We do not cleanup since agents are shared for all frames in the process. + // Child frame events will be forwarded to related top-level agents. + if (this.browsingContext.parent) + return; - // TODO: restore the cleanup. - // Reset transport so that all messages will be pending and will not throw any errors. - // this._channel.resetTransport(); - // this._agents.pageAgent.dispose(); - // this._agents.frameTree.dispose(); - // this._agents = undefined; + let agents = topBrowingContextToAgents.get(this.browsingContext); + if (!agents) { + agents = initialize(this.browsingContext, this.docShell); + topBrowingContextToAgents.set(this.browsingContext, agents); + } + agents.channel.bindToActor(this); + agents.actor = this; } didDestroy() { - this._dispose(); + helper.removeListeners(this._eventListeners); + + if (this.browsingContext.parent) + return; + + const agents = topBrowingContextToAgents.get(this.browsingContext); + // The agents are already re-bound to a new actor. + if (agents.actor !== this) + return; + + topBrowingContextToAgents.delete(this.browsingContext); + + agents.channel.resetTransport(); + agents.pageAgent.dispose(); + agents.frameTree.dispose(); } receiveMessage() { } diff --git a/additions/juggler/content/PageAgent.js b/additions/juggler/content/PageAgent.js index 6aee921..70dcf04 100644 --- a/additions/juggler/content/PageAgent.js +++ b/additions/juggler/content/PageAgent.js @@ -370,7 +370,12 @@ class PageAgent { const unsafeObject = frame.unsafeObject(objectId); if (!unsafeObject) throw new Error('Object is not input!'); - const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath))); + let nsFiles; + if (unsafeObject.webkitdirectory) { + nsFiles = await new Directory(files[0]).getFiles(true); + } else { + nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath))); + } unsafeObject.mozSetFileArray(nsFiles); const events = [ new (frame.domWindow().Event)('input', { bubbles: true, cancelable: true, composed: true }), diff --git a/additions/juggler/content/main.js b/additions/juggler/content/main.js index 022b1e3..15986bb 100644 --- a/additions/juggler/content/main.js +++ b/additions/juggler/content/main.js @@ -7,24 +7,10 @@ const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTr const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js'); const {PageAgent} = ChromeUtils.import('chrome://juggler/content/content/PageAgent.js'); -const browsingContextToAgents = new Map(); const helper = new Helper(); -function initialize(browsingContext, docShell, actor) { - if (browsingContext.parent) { - // For child frames, return agents from the main frame. - return browsingContextToAgents.get(browsingContext.top); - } - - let data = browsingContextToAgents.get(browsingContext); - if (data) { - // Rebind from one main frame actor to another one. - data.channel.bindToActor(actor); - return data; - } - - data = { channel: undefined, pageAgent: undefined, frameTree: undefined, failedToOverrideTimezone: false }; - browsingContextToAgents.set(browsingContext, data); +function initialize(browsingContext, docShell) { + const data = { channel: undefined, pageAgent: undefined, frameTree: undefined, failedToOverrideTimezone: false }; const applySetting = { geolocation: (geolocation) => { @@ -84,7 +70,6 @@ function initialize(browsingContext, docShell, actor) { data.frameTree.addBinding(worldName, name, script); data.frameTree.setInitScripts([...contextCrossProcessCookie.initScripts, ...pageCrossProcessCookie.initScripts]); data.channel = new SimpleChannel('', 'process-' + Services.appinfo.processID); - data.channel.bindToActor(actor); data.pageAgent = new PageAgent(data.channel, data.frameTree); docShell.fileInputInterceptionEnabled = !!pageCrossProcessCookie.interceptFileChooserDialog; diff --git a/patches/backport-rust-crates.bootstrap b/patches/backport-rust-crates.bootstrap deleted file mode 100644 index f1003ae..0000000 --- a/patches/backport-rust-crates.bootstrap +++ /dev/null @@ -1,21484 +0,0 @@ -diff --git a/Cargo.lock b/Cargo.lock -index 7242103ece..1406b76c63 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -816,21 +816,21 @@ checksum = "74428ae4f7f05f32f4448e9f42d371538196919c4834979f4f96d1fdebffcb47" - dependencies = [ - "winapi", - ] - - [[package]] - name = "cookie" - version = "0.16.2" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" - dependencies = [ -- "time 0.3.23", -+ "time 0.3.36", - "version_check", - ] - - [[package]] - name = "core-foundation" - version = "0.9.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" - dependencies = [ - "core-foundation-sys", -@@ -1257,20 +1257,29 @@ dependencies = [ - - [[package]] - name = "debugid" - version = "0.8.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" - dependencies = [ - "uuid", - ] - -+[[package]] -+name = "deranged" -+version = "0.3.11" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -+dependencies = [ -+ "powerfmt", -+] -+ - [[package]] - name = "derive_arbitrary" - version = "1.3.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" - dependencies = [ - "proc-macro2", - "quote", - "syn", - ] -@@ -3750,21 +3759,21 @@ dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "regex", - "scopeguard", - "semver", - "serde", - "serde_json", - "smallvec", - "syn", -- "time 0.3.23", -+ "time 0.3.36", - "tokio", - "tokio-util", - "tracing", - "url", - "uuid", - "winapi", - "windows-sys 0.52.0", - ] - - [[package]] -@@ -3880,21 +3889,21 @@ dependencies = [ - - [[package]] - name = "neqo-common" - version = "0.7.2" - source = "git+https://github.com/mozilla/neqo?tag=v0.7.2#ce5cbe4dfc2e38b238abb022c39eee4215058221" - dependencies = [ - "enum-map", - "env_logger", - "log", - "qlog", -- "time 0.3.23", -+ "time 0.3.36", - "winapi", - ] - - [[package]] - name = "neqo-crypto" - version = "0.7.2" - source = "git+https://github.com/mozilla/neqo?tag=v0.7.2#ce5cbe4dfc2e38b238abb022c39eee4215058221" - dependencies = [ - "bindgen 0.69.4", - "log", -@@ -4070,20 +4079,26 @@ dependencies = [ - "encoding_rs", - ] - - [[package]] - name = "nsstring-gtest" - version = "0.1.0" - dependencies = [ - "nsstring", - ] - -+[[package]] -+name = "num-conv" -+version = "0.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -+ - [[package]] - name = "num-derive" - version = "0.4.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" - dependencies = [ - "proc-macro2", - "quote", - "syn", - ] -@@ -4443,40 +4458,47 @@ dependencies = [ - [[package]] - name = "plist" - version = "1.3.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" - dependencies = [ - "base64 0.13.999", - "indexmap 1.9.3", - "line-wrap", - "serde", -- "time 0.3.23", -+ "time 0.3.36", - "xml-rs", - ] - -+ - [[package]] - name = "ppv-lite86" - version = "0.2.17" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - - [[package]] - name = "precomputed-hash" - version = "0.1.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - - [[package]] - name = "prefs_parser" - version = "0.0.1" - -+[[package]] -+name = "powerfmt" -+version = "0.2.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -+ - [[package]] - name = "presser" - version = "0.3.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" - - [[package]] - name = "prio" - version = "0.15.3" - source = "registry+https://github.com/rust-lang/crates.io-index" -@@ -5622,42 +5644,46 @@ version = "0.1.45" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" - dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview999", - "winapi", - ] - - [[package]] - name = "time" --version = "0.3.23" -+version = "0.3.36" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" -+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" - dependencies = [ -+ "deranged", - "itoa", -+ "num-conv", -+ "powerfmt", - "serde", - "time-core", - "time-macros", - ] - - [[package]] - name = "time-core" --version = "0.1.1" -+version = "0.1.2" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" -+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - - [[package]] - name = "time-macros" --version = "0.2.10" -+version = "0.2.18" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" -+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" - dependencies = [ -+ "num-conv", - "time-core", - ] - - [[package]] - name = "tinystr" - version = "0.7.4" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" - dependencies = [ - "displaydoc", -@@ -6320,21 +6346,21 @@ dependencies = [ - "base64 0.21.3", - "bytes", - "cookie", - "http", - "icu_segmenter", - "log", - "serde", - "serde_derive", - "serde_json", - "thiserror", -- "time 0.3.23", -+ "time 0.3.36", - "tokio", - "tokio-stream", - "url", - "warp", - ] - - [[package]] - name = "webext-storage" - version = "0.1.0" - source = "git+https://github.com/mozilla/application-services?rev=5fc8ee2f0f6950e36d4096983757bd046d55df9f#5fc8ee2f0f6950e36d4096983757bd046d55df9f" -diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml -index 31ca3fcf0f..6aadf9aef9 100644 ---- a/supply-chain/audits.toml -+++ b/supply-chain/audits.toml -@@ -1381,20 +1381,30 @@ delta = "0.14.3 -> 0.20.1" - who = "Mike Hommey " - criteria = "safe-to-deploy" - delta = "2.3.2 -> 2.3.3" - - [[audits.debugid]] - who = "Gabriele Svelto " - criteria = "safe-to-deploy" - version = "0.8.0" - notes = "This crates was written by Sentry and I've fully audited it as Firefox crash reporting machinery relies on it." - -+[[audits.deranged]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+version = "0.3.11" -+notes = """ -+This crate contains a decent bit of `unsafe` code, however all internal -+unsafety is verified with copious assertions (many are compile-time), and -+otherwise the unsafety is documented and left to the caller to verify. -+""" -+ - [[audits.derive_arbitrary]] - who = "Mike Hommey " - criteria = "safe-to-run" - delta = "1.1.0 -> 1.1.1" - - [[audits.derive_arbitrary]] - who = "Mike Hommey " - criteria = "safe-to-run" - delta = "1.1.1 -> 1.1.3" - -@@ -2739,20 +2749,29 @@ who = "Josh Stone " - criteria = "safe-to-deploy" - version = "0.4.3" - notes = "All code written or reviewed by Josh Stone." - - [[audits.num-complex]] - who = "Josh Stone " - criteria = "safe-to-deploy" - version = "0.4.2" - notes = "All code written or reviewed by Josh Stone." - -+[[audits.num-conv]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+version = "0.1.0" -+notes = """ -+Very straightforward, simple crate. No dependencies, unsafe, extern, -+side-effectful std functions, etc. -+""" -+ - [[audits.num-derive]] - who = "Josh Stone " - criteria = "safe-to-deploy" - version = "0.3.3" - notes = "All code written or reviewed by Josh Stone." - - [[audits.num-derive]] - who = "Mike Hommey " - criteria = "safe-to-deploy" - delta = "0.3.3 -> 0.4.0" -@@ -3008,20 +3027,29 @@ delta = "0.1.4 -> 0.1.5" - who = "Mike Hommey " - criteria = "safe-to-deploy" - delta = "0.3.25 -> 0.3.26" - - [[audits.plane-split]] - who = "Nicolas Silva " - criteria = "safe-to-deploy" - version = "0.18.0" - notes = "Mozilla-developed package, no unsafe code, no access to file system, network or other far reaching APIs." - -+[[audits.powerfmt]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+version = "0.2.0" -+notes = """ -+A tiny bit of unsafe code to implement functionality that isn't in stable rust -+yet, but it's all valid. Otherwise it's a pretty simple crate. -+""" -+ - [[audits.ppv-lite86]] - who = "Mike Hommey " - criteria = "safe-to-deploy" - delta = "0.2.16 -> 0.2.17" - - [[audits.precomputed-hash]] - who = "Bobby Holley " - criteria = "safe-to-deploy" - version = "0.1.1" - notes = "This is a trivial crate." -@@ -3797,50 +3825,70 @@ delta = "0.1.45 -> 0.3.17" - [[audits.time]] - who = "Mike Hommey " - criteria = "safe-to-run" - delta = "0.3.9 -> 0.3.17" - - [[audits.time]] - who = "Kershaw Chang " - criteria = "safe-to-deploy" - delta = "0.3.17 -> 0.3.23" - -+[[audits.time]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+delta = "0.3.23 -> 0.3.36" -+notes = """ -+There's a bit of new unsafe code that is self-imposed because they now assert -+that ordinals are non-zero. All unsafe code was checked to ensure that the -+invariants claimed were true. -+""" -+ - [[audits.time-core]] - who = "Kershaw Chang " - criteria = "safe-to-deploy" - version = "0.1.0" - - [[audits.time-core]] - who = "Mike Hommey " - criteria = "safe-to-run" - version = "0.1.0" - - [[audits.time-core]] - who = "Kershaw Chang " - criteria = "safe-to-deploy" - delta = "0.1.0 -> 0.1.1" - -+[[audits.time-core]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+delta = "0.1.1 -> 0.1.2" -+ - [[audits.time-macros]] - who = "Kershaw Chang " - criteria = "safe-to-deploy" - version = "0.2.6" - - [[audits.time-macros]] - who = "Mike Hommey " - criteria = "safe-to-run" - delta = "0.2.4 -> 0.2.6" - - [[audits.time-macros]] - who = "Kershaw Chang " - criteria = "safe-to-deploy" - delta = "0.2.6 -> 0.2.10" - -+[[audits.time-macros]] -+who = "Alex Franchuk " -+criteria = "safe-to-deploy" -+delta = "0.2.10 -> 0.2.18" -+ - [[audits.tinystr]] - who = "Zibi Braniecki " - criteria = "safe-to-deploy" - version = "0.3.4" - - [[audits.tinystr]] - who = "Zibi Braniecki " - criteria = "safe-to-deploy" - version = "0.6.0" - -diff --git a/third_party/rust/deranged/.cargo-checksum.json b/third_party/rust/deranged/.cargo-checksum.json -new file mode 100644 -index 0000000000..f29abcd86f ---- /dev/null -+++ b/third_party/rust/deranged/.cargo-checksum.json -@@ -0,0 +1 @@ -+{"files":{"Cargo.toml":"d1ee03b7033e382279ff580d89a70a9aaf163f977400f0899ad9624e24744e6f","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","README.md":"fc4c9482d9e5225630da44e5371d6fa3f37220e2f4da2dac076cf4cd4f9592e7","src/lib.rs":"bc4b045c160d6f28726831d83f8389d9231410ae289a99950f63436219488dbb","src/tests.rs":"235e4f158084d12b0bfe85745c444d38bb134ebe584396d0a43154260f6576a7","src/traits.rs":"e3984e763afaa23dcf8ea686b473336472953b05abebc433acb26ab5f2237257","src/unsafe_wrapper.rs":"6e57697c2cd484cd60c1a50c4f4d32cb17526447c0f387d8ea3d89a2a89db688"},"package":"b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"} -\ No newline at end of file -diff --git a/third_party/rust/deranged/Cargo.toml b/third_party/rust/deranged/Cargo.toml -new file mode 100644 -index 0000000000..ff660b538c ---- /dev/null -+++ b/third_party/rust/deranged/Cargo.toml -@@ -0,0 +1,83 @@ -+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -+# -+# When uploading crates to the registry Cargo will automatically -+# "normalize" Cargo.toml files for maximal compatibility -+# with all versions of Cargo and also rewrite `path` dependencies -+# to registry (e.g., crates.io) dependencies. -+# -+# If you are reading this file be aware that the original Cargo.toml -+# will likely look very different (and much more reasonable). -+# See Cargo.toml.orig for the original contents. -+ -+[package] -+edition = "2021" -+rust-version = "1.67.0" -+name = "deranged" -+version = "0.3.11" -+authors = ["Jacob Pratt "] -+include = [ -+ "src/**/*", -+ "LICENSE-*", -+ "README.md", -+] -+description = "Ranged integers" -+readme = "README.md" -+keywords = [ -+ "integer", -+ "int", -+ "range", -+] -+license = "MIT OR Apache-2.0" -+repository = "https://github.com/jhpratt/deranged" -+ -+[package.metadata.docs.rs] -+all-features = true -+rustdoc-args = [ -+ "--cfg", -+ "docs_rs", -+] -+targets = ["x86_64-unknown-linux-gnu"] -+ -+[dependencies.num-traits] -+version = "0.2.15" -+optional = true -+default-features = false -+ -+[dependencies.powerfmt] -+version = "0.2.0" -+optional = true -+default-features = false -+ -+[dependencies.quickcheck] -+version = "1.0.3" -+optional = true -+default-features = false -+ -+[dependencies.rand] -+version = "0.8.4" -+optional = true -+default-features = false -+ -+[dependencies.serde] -+version = "1.0.126" -+optional = true -+default-features = false -+ -+[dev-dependencies.rand] -+version = "0.8.4" -+ -+[dev-dependencies.serde_json] -+version = "1.0.86" -+ -+[features] -+alloc = [] -+default = ["std"] -+num = ["dep:num-traits"] -+powerfmt = ["dep:powerfmt"] -+quickcheck = [ -+ "dep:quickcheck", -+ "alloc", -+] -+rand = ["dep:rand"] -+serde = ["dep:serde"] -+std = ["alloc"] -diff --git a/third_party/rust/deranged/LICENSE-Apache b/third_party/rust/deranged/LICENSE-Apache -new file mode 100644 -index 0000000000..7646f21e37 ---- /dev/null -+++ b/third_party/rust/deranged/LICENSE-Apache -@@ -0,0 +1,202 @@ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright 2022 Jacob Pratt et al. -+ -+ Licensed under the Apache License, Version 2.0 (the "License"); -+ you may not use this file except in compliance with the License. -+ You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+ Unless required by applicable law or agreed to in writing, software -+ distributed under the License is distributed on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ See the License for the specific language governing permissions and -+ limitations under the License. -diff --git a/third_party/rust/deranged/LICENSE-MIT b/third_party/rust/deranged/LICENSE-MIT -new file mode 100644 -index 0000000000..a11a755732 ---- /dev/null -+++ b/third_party/rust/deranged/LICENSE-MIT -@@ -0,0 +1,19 @@ -+Copyright (c) 2022 Jacob Pratt et al. -+ -+Permission is hereby granted, free of charge, to any person obtaining a copy -+of this software and associated documentation files (the "Software"), to deal -+in the Software without restriction, including without limitation the rights -+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+copies of the Software, and to permit persons to whom the Software is -+furnished to do so, subject to the following conditions: -+ -+The above copyright notice and this permission notice shall be included in all -+copies or substantial portions of the Software. -+ -+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+SOFTWARE. -diff --git a/third_party/rust/deranged/README.md b/third_party/rust/deranged/README.md -new file mode 100644 -index 0000000000..eddc4b99af ---- /dev/null -+++ b/third_party/rust/deranged/README.md -@@ -0,0 +1,3 @@ -+# Deranged -+ -+This crate is a proof-of-concept implementation of ranged integers. -diff --git a/third_party/rust/deranged/src/lib.rs b/third_party/rust/deranged/src/lib.rs -new file mode 100644 -index 0000000000..aee3ea99f6 ---- /dev/null -+++ b/third_party/rust/deranged/src/lib.rs -@@ -0,0 +1,1474 @@ -+#![cfg_attr(docs_rs, feature(doc_auto_cfg))] -+#![cfg_attr(not(feature = "std"), no_std)] -+#![deny( -+ anonymous_parameters, -+ clippy::all, -+ clippy::missing_safety_doc, -+ clippy::missing_safety_doc, -+ clippy::undocumented_unsafe_blocks, -+ illegal_floating_point_literal_pattern, -+ late_bound_lifetime_arguments, -+ patterns_in_fns_without_body, -+ rust_2018_idioms, -+ trivial_casts, -+ trivial_numeric_casts, -+ unreachable_pub, -+ unsafe_op_in_unsafe_fn, -+ unused_extern_crates -+)] -+#![warn( -+ clippy::dbg_macro, -+ clippy::decimal_literal_representation, -+ clippy::get_unwrap, -+ clippy::nursery, -+ clippy::pedantic, -+ clippy::todo, -+ clippy::unimplemented, -+ clippy::unwrap_used, -+ clippy::use_debug, -+ missing_copy_implementations, -+ missing_debug_implementations, -+ unused_qualifications, -+ variant_size_differences -+)] -+#![allow( -+ path_statements, // used for static assertions -+ clippy::inline_always, -+ clippy::missing_errors_doc, -+ clippy::must_use_candidate, -+ clippy::redundant_pub_crate, -+)] -+#![doc(test(attr(deny(warnings))))] -+ -+#[cfg(test)] -+mod tests; -+mod traits; -+mod unsafe_wrapper; -+ -+#[cfg(feature = "alloc")] -+#[allow(unused_extern_crates)] -+extern crate alloc; -+ -+use core::borrow::Borrow; -+use core::cmp::Ordering; -+use core::fmt; -+use core::num::IntErrorKind; -+use core::str::FromStr; -+#[cfg(feature = "std")] -+use std::error::Error; -+ -+#[cfg(feature = "powerfmt")] -+use powerfmt::smart_display; -+ -+use crate::unsafe_wrapper::Unsafe; -+ -+#[derive(Debug, Clone, Copy, PartialEq, Eq)] -+pub struct TryFromIntError; -+ -+impl fmt::Display for TryFromIntError { -+ #[inline] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.write_str("out of range integral type conversion attempted") -+ } -+} -+#[cfg(feature = "std")] -+impl Error for TryFromIntError {} -+ -+#[derive(Debug, Clone, PartialEq, Eq)] -+pub struct ParseIntError { -+ kind: IntErrorKind, -+} -+ -+impl ParseIntError { -+ /// Outputs the detailed cause of parsing an integer failing. -+ // This function is not const because the counterpart of stdlib isn't -+ #[allow(clippy::missing_const_for_fn)] -+ #[inline(always)] -+ pub fn kind(&self) -> &IntErrorKind { -+ &self.kind -+ } -+} -+ -+impl fmt::Display for ParseIntError { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ match self.kind { -+ IntErrorKind::Empty => "cannot parse integer from empty string", -+ IntErrorKind::InvalidDigit => "invalid digit found in string", -+ IntErrorKind::PosOverflow => "number too large to fit in target type", -+ IntErrorKind::NegOverflow => "number too small to fit in target type", -+ IntErrorKind::Zero => "number would be zero for non-zero type", -+ _ => "Unknown Int error kind", -+ } -+ .fmt(f) -+ } -+} -+ -+#[cfg(feature = "std")] -+impl Error for ParseIntError {} -+ -+macro_rules! const_try_opt { -+ ($e:expr) => { -+ match $e { -+ Some(value) => value, -+ None => return None, -+ } -+ }; -+} -+ -+macro_rules! if_signed { -+ (true $($x:tt)*) => { $($x)*}; -+ (false $($x:tt)*) => {}; -+} -+ -+macro_rules! if_unsigned { -+ (true $($x:tt)*) => {}; -+ (false $($x:tt)*) => { $($x)* }; -+} -+ -+macro_rules! article { -+ (true) => { -+ "An" -+ }; -+ (false) => { -+ "A" -+ }; -+} -+ -+macro_rules! unsafe_unwrap_unchecked { -+ ($e:expr) => {{ -+ let opt = $e; -+ debug_assert!(opt.is_some()); -+ match $e { -+ Some(value) => value, -+ None => core::hint::unreachable_unchecked(), -+ } -+ }}; -+} -+ -+/// Informs the optimizer that a condition is always true. If the condition is false, the behavior -+/// is undefined. -+/// -+/// # Safety -+/// -+/// `b` must be `true`. -+#[inline] -+const unsafe fn assume(b: bool) { -+ debug_assert!(b); -+ if !b { -+ // Safety: The caller must ensure that `b` is true. -+ unsafe { core::hint::unreachable_unchecked() } -+ } -+} -+ -+macro_rules! impl_ranged { -+ ($( -+ $type:ident { -+ mod_name: $mod_name:ident -+ internal: $internal:ident -+ signed: $is_signed:ident -+ unsigned: $unsigned_type:ident -+ optional: $optional_type:ident -+ } -+ )*) => {$( -+ #[doc = concat!( -+ article!($is_signed), -+ " `", -+ stringify!($internal), -+ "` that is known to be in the range `MIN..=MAX`.", -+ )] -+ #[repr(transparent)] -+ #[derive(Clone, Copy, Eq, Ord, Hash)] -+ pub struct $type( -+ Unsafe<$internal>, -+ ); -+ -+ #[doc = concat!( -+ "A `", -+ stringify!($type), -+ "` that is optional. Equivalent to [`Option<", -+ stringify!($type), -+ ">`] with niche value optimization.", -+ )] -+ /// -+ #[doc = concat!( -+ "If `MIN` is [`", -+ stringify!($internal), -+ "::MIN`] _and_ `MAX` is [`", -+ stringify!($internal) -+ ,"::MAX`] then compilation will fail. This is because there is no way to represent \ -+ the niche value.", -+ )] -+ /// -+ /// This type is useful when you need to store an optional ranged value in a struct, but -+ /// do not want the overhead of an `Option` type. This reduces the size of the struct -+ /// overall, and is particularly useful when you have a large number of optional fields. -+ /// Note that most operations must still be performed on the [`Option`] type, which is -+ #[doc = concat!("obtained with [`", stringify!($optional_type), "::get`].")] -+ #[repr(transparent)] -+ #[derive(Clone, Copy, Eq, Hash)] -+ pub struct $optional_type( -+ $internal, -+ ); -+ -+ impl $type<0, 0> { -+ #[inline(always)] -+ pub const fn exact() -> $type { -+ // Safety: The value is the only one in range. -+ unsafe { $type::new_unchecked(VALUE) } -+ } -+ } -+ -+ impl $type { -+ /// The smallest value that can be represented by this type. -+ // Safety: `MIN` is in range by definition. -+ pub const MIN: Self = Self::new_static::(); -+ -+ /// The largest value that can be represented by this type. -+ // Safety: `MAX` is in range by definition. -+ pub const MAX: Self = Self::new_static::(); -+ -+ /// Creates a ranged integer without checking the value. -+ /// -+ /// # Safety -+ /// -+ /// The value must be within the range `MIN..=MAX`. -+ #[inline(always)] -+ pub const unsafe fn new_unchecked(value: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the value is in range. -+ unsafe { -+ $crate::assume(MIN <= value && value <= MAX); -+ Self(Unsafe::new(value)) -+ } -+ } -+ -+ /// Returns the value as a primitive type. -+ #[inline(always)] -+ pub const fn get(self) -> $internal { -+ ::ASSERT; -+ // Safety: A stored value is always in range. -+ unsafe { $crate::assume(MIN <= *self.0.get() && *self.0.get() <= MAX) }; -+ *self.0.get() -+ } -+ -+ #[inline(always)] -+ pub(crate) const fn get_ref(&self) -> &$internal { -+ ::ASSERT; -+ let value = self.0.get(); -+ // Safety: A stored value is always in range. -+ unsafe { $crate::assume(MIN <= *value && *value <= MAX) }; -+ value -+ } -+ -+ /// Creates a ranged integer if the given value is in the range `MIN..=MAX`. -+ #[inline(always)] -+ pub const fn new(value: $internal) -> Option { -+ ::ASSERT; -+ if value < MIN || value > MAX { -+ None -+ } else { -+ // Safety: The value is in range. -+ Some(unsafe { Self::new_unchecked(value) }) -+ } -+ } -+ -+ /// Creates a ranged integer with a statically known value. **Fails to compile** if the -+ /// value is not in range. -+ #[inline(always)] -+ pub const fn new_static() -> Self { -+ <($type, $type) as $crate::traits::StaticIsValid>::ASSERT; -+ // Safety: The value is in range. -+ unsafe { Self::new_unchecked(VALUE) } -+ } -+ -+ /// Creates a ranged integer with the given value, saturating if it is out of range. -+ #[inline] -+ pub const fn new_saturating(value: $internal) -> Self { -+ ::ASSERT; -+ if value < MIN { -+ Self::MIN -+ } else if value > MAX { -+ Self::MAX -+ } else { -+ // Safety: The value is in range. -+ unsafe { Self::new_unchecked(value) } -+ } -+ } -+ -+ /// Expand the range that the value may be in. **Fails to compile** if the new range is -+ /// not a superset of the current range. -+ pub const fn expand( -+ self, -+ ) -> $type { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <($type, $type) as $crate::traits::ExpandIsValid> -+ ::ASSERT; -+ // Safety: The range is widened. -+ unsafe { $type::new_unchecked(self.get()) } -+ } -+ -+ /// Attempt to narrow the range that the value may be in. Returns `None` if the value -+ /// is outside the new range. **Fails to compile** if the new range is not a subset of -+ /// the current range. -+ pub const fn narrow< -+ const NEW_MIN: $internal, -+ const NEW_MAX: $internal, -+ >(self) -> Option<$type> { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <($type, $type) as $crate::traits::NarrowIsValid> -+ ::ASSERT; -+ $type::::new(self.get()) -+ } -+ -+ /// Converts a string slice in a given base to an integer. -+ /// -+ /// The string is expected to be an optional `+` or `-` sign followed by digits. Leading -+ /// and trailing whitespace represent an error. Digits are a subset of these characters, -+ /// depending on `radix`: -+ /// -+ /// - `0-9` -+ /// - `a-z` -+ /// - `A-Z` -+ /// -+ /// # Panics -+ /// -+ /// Panics if `radix` is not in the range `2..=36`. -+ /// -+ /// # Examples -+ /// -+ /// Basic usage: -+ /// -+ /// ```rust -+ #[doc = concat!("# use deranged::", stringify!($type), ";")] -+ #[doc = concat!( -+ "assert_eq!(", -+ stringify!($type), -+ "::<5, 10>::from_str_radix(\"A\", 16), Ok(", -+ stringify!($type), -+ "::new_static::<10>()));", -+ )] -+ /// ``` -+ #[inline] -+ pub fn from_str_radix(src: &str, radix: u32) -> Result { -+ ::ASSERT; -+ match $internal::from_str_radix(src, radix) { -+ Ok(value) if value > MAX => { -+ Err(ParseIntError { kind: IntErrorKind::PosOverflow }) -+ } -+ Ok(value) if value < MIN => { -+ Err(ParseIntError { kind: IntErrorKind::NegOverflow }) -+ } -+ // Safety: If the value was out of range, it would have been caught in a -+ // previous arm. -+ Ok(value) => Ok(unsafe { Self::new_unchecked(value) }), -+ Err(e) => Err(ParseIntError { kind: e.kind().clone() }), -+ } -+ } -+ -+ /// Checked integer addition. Computes `self + rhs`, returning `None` if the resulting -+ /// value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_add(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_add(rhs))) -+ } -+ -+ /// Unchecked integer addition. Computes `self + rhs`, assuming that the result is in -+ /// range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self + rhs` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_add(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_add(rhs))) -+ } -+ } -+ -+ /// Checked integer addition. Computes `self - rhs`, returning `None` if the resulting -+ /// value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_sub(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_sub(rhs))) -+ } -+ -+ /// Unchecked integer subtraction. Computes `self - rhs`, assuming that the result is in -+ /// range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self - rhs` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_sub(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_sub(rhs))) -+ } -+ } -+ -+ /// Checked integer addition. Computes `self * rhs`, returning `None` if the resulting -+ /// value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_mul(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_mul(rhs))) -+ } -+ -+ /// Unchecked integer multiplication. Computes `self * rhs`, assuming that the result is -+ /// in range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self * rhs` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_mul(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_mul(rhs))) -+ } -+ } -+ -+ /// Checked integer addition. Computes `self / rhs`, returning `None` if `rhs == 0` or -+ /// if the resulting value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_div(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_div(rhs))) -+ } -+ -+ /// Unchecked integer division. Computes `self / rhs`, assuming that `rhs != 0` and that -+ /// the result is in range. -+ /// -+ /// # Safety -+ /// -+ /// `self` must not be zero and the result of `self / rhs` must be in the range -+ /// `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_div(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range and that `rhs` is not -+ // zero. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_div(rhs))) -+ } -+ } -+ -+ /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` if -+ /// `rhs == 0` or if the resulting value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_div_euclid(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_div_euclid(rhs))) -+ } -+ -+ /// Unchecked Euclidean division. Computes `self.div_euclid(rhs)`, assuming that -+ /// `rhs != 0` and that the result is in range. -+ /// -+ /// # Safety -+ /// -+ /// `self` must not be zero and the result of `self.div_euclid(rhs)` must be in the -+ /// range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_div_euclid(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range and that `rhs` is not -+ // zero. -+ unsafe { -+ Self::new_unchecked( -+ unsafe_unwrap_unchecked!(self.get().checked_div_euclid(rhs)) -+ ) -+ } -+ } -+ -+ if_unsigned!($is_signed -+ /// Remainder. Computes `self % rhs`, statically guaranteeing that the returned value -+ /// is in range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn rem( -+ self, -+ rhs: $type, -+ ) -> $type<0, RHS_VALUE> { -+ ::ASSERT; -+ // Safety: The result is guaranteed to be in range due to the nature of remainder on -+ // unsigned integers. -+ unsafe { $type::new_unchecked(self.get() % rhs.get()) } -+ }); -+ -+ /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0` or -+ /// if the resulting value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_rem(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_rem(rhs))) -+ } -+ -+ /// Unchecked remainder. Computes `self % rhs`, assuming that `rhs != 0` and that the -+ /// result is in range. -+ /// -+ /// # Safety -+ /// -+ /// `self` must not be zero and the result of `self % rhs` must be in the range -+ /// `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_rem(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range and that `rhs` is not -+ // zero. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_rem(rhs))) -+ } -+ } -+ -+ /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` if -+ /// `rhs == 0` or if the resulting value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_rem_euclid(self, rhs: $internal) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_rem_euclid(rhs))) -+ } -+ -+ /// Unchecked Euclidean remainder. Computes `self.rem_euclid(rhs)`, assuming that -+ /// `rhs != 0` and that the result is in range. -+ /// -+ /// # Safety -+ /// -+ /// `self` must not be zero and the result of `self.rem_euclid(rhs)` must be in the -+ /// range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_rem_euclid(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range and that `rhs` is not -+ // zero. -+ unsafe { -+ Self::new_unchecked( -+ unsafe_unwrap_unchecked!(self.get().checked_rem_euclid(rhs)) -+ ) -+ } -+ } -+ -+ /// Checked negation. Computes `-self`, returning `None` if the resulting value is out -+ /// of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_neg(self) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_neg())) -+ } -+ -+ /// Unchecked negation. Computes `-self`, assuming that `-self` is in range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `-self` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_neg(self) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_neg())) } -+ } -+ -+ /// Negation. Computes `self.neg()`, **failing to compile** if the result is not -+ /// guaranteed to be in range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const fn neg(self) -> Self { -+ ::ASSERT; -+ ::ASSERT; -+ // Safety: The compiler asserts that the result is in range. -+ unsafe { self.unchecked_neg() } -+ } -+ -+ /// Checked shift left. Computes `self << rhs`, returning `None` if the resulting value -+ /// is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_shl(self, rhs: u32) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_shl(rhs))) -+ } -+ -+ /// Unchecked shift left. Computes `self << rhs`, assuming that the result is in range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self << rhs` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shl(rhs))) -+ } -+ } -+ -+ /// Checked shift right. Computes `self >> rhs`, returning `None` if -+ /// the resulting value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_shr(self, rhs: u32) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_shr(rhs))) -+ } -+ -+ /// Unchecked shift right. Computes `self >> rhs`, assuming that the result is in range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self >> rhs` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_shr(rhs))) -+ } -+ } -+ -+ if_signed!($is_signed -+ /// Checked absolute value. Computes `self.abs()`, returning `None` if the resulting -+ /// value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_abs(self) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_abs())) -+ } -+ -+ /// Unchecked absolute value. Computes `self.abs()`, assuming that the result is in -+ /// range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self.abs()` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_abs(self) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_abs())) } -+ } -+ -+ /// Absolute value. Computes `self.abs()`, **failing to compile** if the result is not -+ /// guaranteed to be in range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const fn abs(self) -> Self { -+ ::ASSERT; -+ ::ASSERT; -+ // Safety: The compiler asserts that the result is in range. -+ unsafe { self.unchecked_abs() } -+ }); -+ -+ /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if the resulting -+ /// value is out of range. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn checked_pow(self, exp: u32) -> Option { -+ ::ASSERT; -+ Self::new(const_try_opt!(self.get().checked_pow(exp))) -+ } -+ -+ /// Unchecked exponentiation. Computes `self.pow(exp)`, assuming that the result is in -+ /// range. -+ /// -+ /// # Safety -+ /// -+ /// The result of `self.pow(exp)` must be in the range `MIN..=MAX`. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline(always)] -+ pub const unsafe fn unchecked_pow(self, exp: u32) -> Self { -+ ::ASSERT; -+ // Safety: The caller must ensure that the result is in range. -+ unsafe { -+ Self::new_unchecked(unsafe_unwrap_unchecked!(self.get().checked_pow(exp))) -+ } -+ } -+ -+ /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_add(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_add(rhs)) -+ } -+ -+ /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_sub(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_sub(rhs)) -+ } -+ -+ if_signed!($is_signed -+ /// Saturating integer negation. Computes `self - rhs`, saturating at the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_neg(self) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_neg()) -+ }); -+ -+ if_signed!($is_signed -+ /// Saturating absolute value. Computes `self.abs()`, saturating at the numeric bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_abs(self) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_abs()) -+ }); -+ -+ /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_mul(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_mul(rhs)) -+ } -+ -+ /// Saturating integer exponentiation. Computes `self.pow(exp)`, saturating at the -+ /// numeric bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ pub const fn saturating_pow(self, exp: u32) -> Self { -+ ::ASSERT; -+ Self::new_saturating(self.get().saturating_pow(exp)) -+ } -+ -+ /// Compute the `rem_euclid` of this type with its unsigned type equivalent -+ // Not public because it doesn't match stdlib's "method_unsigned implemented only for signed type" tradition. -+ // Also because this isn't implemented for normal types in std. -+ // TODO maybe make public anyway? It is useful. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned -+ const fn rem_euclid_unsigned( -+ rhs: $internal, -+ range_len: $unsigned_type -+ ) -> $unsigned_type { -+ #[allow(unused_comparisons)] -+ if rhs >= 0 { -+ (rhs as $unsigned_type) % range_len -+ } else { -+ // Let ux refer to an n bit unsigned and ix refer to an n bit signed integer. -+ // Can't write -ux or ux::abs() method. This gets around compilation error. -+ // `wrapping_sub` is to handle rhs = ix::MIN since ix::MIN = -ix::MAX-1 -+ let rhs_abs = ($internal::wrapping_sub(0, rhs)) as $unsigned_type; -+ // Largest multiple of range_len <= type::MAX is lowest if range_len * 2 > ux::MAX -> range_len >= ux::MAX / 2 + 1 -+ // Also = 0 in mod range_len arithmetic. -+ // Sub from this large number rhs_abs (same as sub -rhs = -(-rhs) = add rhs) to get rhs % range_len -+ // ix::MIN = -2^(n-1) so 0 <= rhs_abs <= 2^(n-1) -+ // ux::MAX / 2 + 1 = 2^(n-1) so this subtraction will always be a >= 0 after subtraction -+ // Thus converting rhs signed negative to equivalent positive value in mod range_len arithmetic -+ ((($unsigned_type::MAX / range_len) * range_len) - (rhs_abs)) % range_len -+ } -+ } -+ -+ /// Wrapping integer addition. Computes `self + rhs`, wrapping around the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned -+ pub const fn wrapping_add(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Forward to internal type's impl if same as type. -+ if MIN == $internal::MIN && MAX == $internal::MAX { -+ // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. -+ return unsafe { Self::new_unchecked(self.get().wrapping_add(rhs)) } -+ } -+ -+ let inner = self.get(); -+ -+ // Won't overflow because of std impl forwarding. -+ let range_len = MAX.abs_diff(MIN) + 1; -+ -+ // Calculate the offset with proper handling for negative rhs -+ let offset = Self::rem_euclid_unsigned(rhs, range_len); -+ -+ let greater_vals = MAX.abs_diff(inner); -+ // No wrap -+ if offset <= greater_vals { -+ // Safety: -+ // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) -+ // if inner < 0: Same as >=0 with caveat: -+ // `(signed as unsigned).wrapping_add(unsigned) as signed` is the same as -+ // `signed::checked_add_unsigned(unsigned).unwrap()` or `wrapping_add_unsigned` -+ // (the difference doesn't matter since it won't overflow), -+ // but unsigned integers don't have either method so it won't compile that way. -+ unsafe { Self::new_unchecked( -+ ((inner as $unsigned_type).wrapping_add(offset)) as $internal -+ ) } -+ } -+ // Wrap -+ else { -+ // Safety: -+ // - offset < range_len by rem_euclid (MIN + ... safe) -+ // - offset > greater_vals from if statement (offset - (greater_vals + 1) safe) -+ // -+ // again using `(signed as unsigned).wrapping_add(unsigned) as signed` = `checked_add_unsigned` trick -+ unsafe { Self::new_unchecked( -+ ((MIN as $unsigned_type).wrapping_add( -+ offset - (greater_vals + 1) -+ )) as $internal -+ ) } -+ } -+ } -+ -+ /// Wrapping integer subtraction. Computes `self - rhs`, wrapping around the numeric -+ /// bounds. -+ #[must_use = "this returns the result of the operation, without modifying the original"] -+ #[inline] -+ #[allow(trivial_numeric_casts)] // needed since some casts have to send unsigned -> unsigned to handle signed -> unsigned -+ pub const fn wrapping_sub(self, rhs: $internal) -> Self { -+ ::ASSERT; -+ // Forward to internal type's impl if same as type. -+ if MIN == $internal::MIN && MAX == $internal::MAX { -+ // Safety: std's wrapping methods match ranged arithmetic when the range is the internal datatype's range. -+ return unsafe { Self::new_unchecked(self.get().wrapping_sub(rhs)) } -+ } -+ -+ let inner = self.get(); -+ -+ // Won't overflow because of std impl forwarding. -+ let range_len = MAX.abs_diff(MIN) + 1; -+ -+ // Calculate the offset with proper handling for negative rhs -+ let offset = Self::rem_euclid_unsigned(rhs, range_len); -+ -+ let lesser_vals = MIN.abs_diff(inner); -+ // No wrap -+ if offset <= lesser_vals { -+ // Safety: -+ // if inner >= 0 -> No overflow beyond range (offset <= greater_vals) -+ // if inner < 0: Same as >=0 with caveat: -+ // `(signed as unsigned).wrapping_sub(unsigned) as signed` is the same as -+ // `signed::checked_sub_unsigned(unsigned).unwrap()` or `wrapping_sub_unsigned` -+ // (the difference doesn't matter since it won't overflow below 0), -+ // but unsigned integers don't have either method so it won't compile that way. -+ unsafe { Self::new_unchecked( -+ ((inner as $unsigned_type).wrapping_sub(offset)) as $internal -+ ) } -+ } -+ // Wrap -+ else { -+ // Safety: -+ // - offset < range_len by rem_euclid (MAX - ... safe) -+ // - offset > lesser_vals from if statement (offset - (lesser_vals + 1) safe) -+ // -+ // again using `(signed as unsigned).wrapping_sub(unsigned) as signed` = `checked_sub_unsigned` trick -+ unsafe { Self::new_unchecked( -+ ((MAX as $unsigned_type).wrapping_sub( -+ offset - (lesser_vals + 1) -+ )) as $internal -+ ) } -+ } -+ } -+ } -+ -+ impl $optional_type { -+ /// The value used as the niche. Must not be in the range `MIN..=MAX`. -+ const NICHE: $internal = match (MIN, MAX) { -+ ($internal::MIN, $internal::MAX) => panic!("type has no niche"), -+ ($internal::MIN, _) => $internal::MAX, -+ (_, _) => $internal::MIN, -+ }; -+ -+ /// An optional ranged value that is not present. -+ #[allow(non_upper_case_globals)] -+ pub const None: Self = Self(Self::NICHE); -+ -+ /// Creates an optional ranged value that is present. -+ #[allow(non_snake_case)] -+ #[inline(always)] -+ pub const fn Some(value: $type) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Self(value.get()) -+ } -+ -+ /// Returns the value as the standard library's [`Option`] type. -+ #[inline(always)] -+ pub const fn get(self) -> Option<$type> { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ if self.0 == Self::NICHE { -+ None -+ } else { -+ // Safety: A stored value that is not the niche is always in range. -+ Some(unsafe { $type::new_unchecked(self.0) }) -+ } -+ } -+ -+ /// Creates an optional ranged integer without checking the value. -+ /// -+ /// # Safety -+ /// -+ /// The value must be within the range `MIN..=MAX`. As the value used for niche -+ /// value optimization is unspecified, the provided value must not be the niche -+ /// value. -+ #[inline(always)] -+ pub const unsafe fn some_unchecked(value: $internal) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ // Safety: The caller must ensure that the value is in range. -+ unsafe { $crate::assume(MIN <= value && value <= MAX) }; -+ Self(value) -+ } -+ -+ /// Obtain the inner value of the struct. This is useful for comparisons. -+ #[inline(always)] -+ pub(crate) const fn inner(self) -> $internal { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.0 -+ } -+ -+ #[inline(always)] -+ pub const fn get_primitive(self) -> Option<$internal> { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Some(const_try_opt!(self.get()).get()) -+ } -+ -+ /// Returns `true` if the value is the niche value. -+ #[inline(always)] -+ pub const fn is_none(self) -> bool { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get().is_none() -+ } -+ -+ /// Returns `true` if the value is not the niche value. -+ #[inline(always)] -+ pub const fn is_some(self) -> bool { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get().is_some() -+ } -+ } -+ -+ impl fmt::Debug for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::Debug for $optional_type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::Display for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ #[cfg(feature = "powerfmt")] -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > smart_display::SmartDisplay for $type { -+ type Metadata = <$internal as smart_display::SmartDisplay>::Metadata; -+ -+ #[inline(always)] -+ fn metadata( -+ &self, -+ f: smart_display::FormatterOptions, -+ ) -> smart_display::Metadata<'_, Self> { -+ ::ASSERT; -+ self.get_ref().metadata(f).reuse() -+ } -+ -+ #[inline(always)] -+ fn fmt_with_metadata( -+ &self, -+ f: &mut fmt::Formatter<'_>, -+ metadata: smart_display::Metadata<'_, Self>, -+ ) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt_with_metadata(f, metadata.reuse()) -+ } -+ } -+ -+ impl Default for $optional_type { -+ #[inline(always)] -+ fn default() -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Self::None -+ } -+ } -+ -+ impl AsRef<$internal> for $type { -+ #[inline(always)] -+ fn as_ref(&self) -> &$internal { -+ ::ASSERT; -+ &self.get_ref() -+ } -+ } -+ -+ impl Borrow<$internal> for $type { -+ #[inline(always)] -+ fn borrow(&self) -> &$internal { -+ ::ASSERT; -+ &self.get_ref() -+ } -+ } -+ -+ impl< -+ const MIN_A: $internal, -+ const MAX_A: $internal, -+ const MIN_B: $internal, -+ const MAX_B: $internal, -+ > PartialEq<$type> for $type { -+ #[inline(always)] -+ fn eq(&self, other: &$type) -> bool { -+ ::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get() == other.get() -+ } -+ } -+ -+ impl< -+ const MIN_A: $internal, -+ const MAX_A: $internal, -+ const MIN_B: $internal, -+ const MAX_B: $internal, -+ > PartialEq<$optional_type> for $optional_type { -+ #[inline(always)] -+ fn eq(&self, other: &$optional_type) -> bool { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.inner() == other.inner() -+ } -+ } -+ -+ impl< -+ const MIN_A: $internal, -+ const MAX_A: $internal, -+ const MIN_B: $internal, -+ const MAX_B: $internal, -+ > PartialOrd<$type> for $type { -+ #[inline(always)] -+ fn partial_cmp(&self, other: &$type) -> Option { -+ ::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get().partial_cmp(&other.get()) -+ } -+ } -+ -+ impl< -+ const MIN_A: $internal, -+ const MAX_A: $internal, -+ const MIN_B: $internal, -+ const MAX_B: $internal, -+ > PartialOrd<$optional_type> for $optional_type { -+ #[inline] -+ fn partial_cmp(&self, other: &$optional_type) -> Option { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ if self.is_none() && other.is_none() { -+ Some(Ordering::Equal) -+ } else if self.is_none() { -+ Some(Ordering::Less) -+ } else if other.is_none() { -+ Some(Ordering::Greater) -+ } else { -+ self.inner().partial_cmp(&other.inner()) -+ } -+ } -+ } -+ -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > Ord for $optional_type { -+ #[inline] -+ fn cmp(&self, other: &Self) -> Ordering { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ if self.is_none() && other.is_none() { -+ Ordering::Equal -+ } else if self.is_none() { -+ Ordering::Less -+ } else if other.is_none() { -+ Ordering::Greater -+ } else { -+ self.inner().cmp(&other.inner()) -+ } -+ } -+ } -+ -+ impl fmt::Binary for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::LowerHex for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::UpperHex for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::LowerExp for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::UpperExp for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl fmt::Octal for $type { -+ #[inline(always)] -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ ::ASSERT; -+ self.get().fmt(f) -+ } -+ } -+ -+ impl From<$type> for $internal { -+ #[inline(always)] -+ fn from(value: $type) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ value.get() -+ } -+ } -+ -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > From<$type> for $optional_type { -+ #[inline(always)] -+ fn from(value: $type) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Self::Some(value) -+ } -+ } -+ -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > From>> for $optional_type { -+ #[inline(always)] -+ fn from(value: Option<$type>) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ match value { -+ Some(value) => Self::Some(value), -+ None => Self::None, -+ } -+ } -+ } -+ -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > From<$optional_type> for Option<$type> { -+ #[inline(always)] -+ fn from(value: $optional_type) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ value.get() -+ } -+ } -+ -+ impl TryFrom<$internal> for $type { -+ type Error = TryFromIntError; -+ -+ #[inline] -+ fn try_from(value: $internal) -> Result { -+ ::ASSERT; -+ Self::new(value).ok_or(TryFromIntError) -+ } -+ } -+ -+ impl FromStr for $type { -+ type Err = ParseIntError; -+ -+ #[inline] -+ fn from_str(s: &str) -> Result { -+ ::ASSERT; -+ let value = s.parse::<$internal>().map_err(|e| ParseIntError { -+ kind: e.kind().clone() -+ })?; -+ if value < MIN { -+ Err(ParseIntError { kind: IntErrorKind::NegOverflow }) -+ } else if value > MAX { -+ Err(ParseIntError { kind: IntErrorKind::PosOverflow }) -+ } else { -+ // Safety: The value was previously checked for validity. -+ Ok(unsafe { Self::new_unchecked(value) }) -+ } -+ } -+ } -+ -+ #[cfg(feature = "serde")] -+ impl serde::Serialize for $type { -+ #[inline(always)] -+ fn serialize(&self, serializer: S) -> Result { -+ ::ASSERT; -+ self.get().serialize(serializer) -+ } -+ } -+ -+ #[cfg(feature = "serde")] -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > serde::Serialize for $optional_type { -+ #[inline(always)] -+ fn serialize(&self, serializer: S) -> Result { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ self.get().serialize(serializer) -+ } -+ } -+ -+ #[cfg(feature = "serde")] -+ impl< -+ 'de, -+ const MIN: $internal, -+ const MAX: $internal, -+ > serde::Deserialize<'de> for $type { -+ #[inline] -+ fn deserialize>(deserializer: D) -> Result { -+ ::ASSERT; -+ let internal = <$internal>::deserialize(deserializer)?; -+ Self::new(internal).ok_or_else(|| ::invalid_value( -+ serde::de::Unexpected::Other("integer"), -+ #[cfg(feature = "std")] { -+ &format!("an integer in the range {}..={}", MIN, MAX).as_ref() -+ }, -+ #[cfg(not(feature = "std"))] { -+ &"an integer in the valid range" -+ } -+ )) -+ } -+ } -+ -+ #[cfg(feature = "serde")] -+ impl< -+ 'de, -+ const MIN: $internal, -+ const MAX: $internal, -+ > serde::Deserialize<'de> for $optional_type { -+ #[inline] -+ fn deserialize>(deserializer: D) -> Result { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Ok(Self::Some($type::::deserialize(deserializer)?)) -+ } -+ } -+ -+ #[cfg(feature = "rand")] -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > rand::distributions::Distribution<$type> for rand::distributions::Standard { -+ #[inline] -+ fn sample(&self, rng: &mut R) -> $type { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ $type::new(rng.gen_range(MIN..=MAX)).expect("rand failed to generate a valid value") -+ } -+ } -+ -+ #[cfg(feature = "rand")] -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > rand::distributions::Distribution<$optional_type> -+ for rand::distributions::Standard { -+ #[inline] -+ fn sample(&self, rng: &mut R) -> $optional_type { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ rng.gen::>>().into() -+ } -+ } -+ -+ #[cfg(feature = "num")] -+ impl num_traits::Bounded for $type { -+ #[inline(always)] -+ fn min_value() -> Self { -+ ::ASSERT; -+ Self::MIN -+ } -+ -+ #[inline(always)] -+ fn max_value() -> Self { -+ ::ASSERT; -+ Self::MAX -+ } -+ } -+ -+ #[cfg(feature = "quickcheck")] -+ impl quickcheck::Arbitrary for $type { -+ #[inline] -+ fn arbitrary(g: &mut quickcheck::Gen) -> Self { -+ ::ASSERT; -+ // Safety: The `rem_euclid` call and addition ensure that the value is in range. -+ unsafe { -+ Self::new_unchecked($internal::arbitrary(g).rem_euclid(MAX - MIN + 1) + MIN) -+ } -+ } -+ -+ #[inline] -+ fn shrink(&self) -> ::alloc::boxed::Box> { -+ ::alloc::boxed::Box::new( -+ self.get() -+ .shrink() -+ .filter_map(Self::new) -+ ) -+ } -+ } -+ -+ #[cfg(feature = "quickcheck")] -+ impl< -+ const MIN: $internal, -+ const MAX: $internal, -+ > quickcheck::Arbitrary for $optional_type { -+ #[inline] -+ fn arbitrary(g: &mut quickcheck::Gen) -> Self { -+ <$type as $crate::traits::RangeIsValid>::ASSERT; -+ Option::<$type>::arbitrary(g).into() -+ } -+ -+ #[inline] -+ fn shrink(&self) -> ::alloc::boxed::Box> { -+ ::alloc::boxed::Box::new(self.get().shrink().map(Self::from)) -+ } -+ } -+ )*}; -+} -+ -+impl_ranged! { -+ RangedU8 { -+ mod_name: ranged_u8 -+ internal: u8 -+ signed: false -+ unsigned: u8 -+ optional: OptionRangedU8 -+ } -+ RangedU16 { -+ mod_name: ranged_u16 -+ internal: u16 -+ signed: false -+ unsigned: u16 -+ optional: OptionRangedU16 -+ } -+ RangedU32 { -+ mod_name: ranged_u32 -+ internal: u32 -+ signed: false -+ unsigned: u32 -+ optional: OptionRangedU32 -+ } -+ RangedU64 { -+ mod_name: ranged_u64 -+ internal: u64 -+ signed: false -+ unsigned: u64 -+ optional: OptionRangedU64 -+ } -+ RangedU128 { -+ mod_name: ranged_u128 -+ internal: u128 -+ signed: false -+ unsigned: u128 -+ optional: OptionRangedU128 -+ } -+ RangedUsize { -+ mod_name: ranged_usize -+ internal: usize -+ signed: false -+ unsigned: usize -+ optional: OptionRangedUsize -+ } -+ RangedI8 { -+ mod_name: ranged_i8 -+ internal: i8 -+ signed: true -+ unsigned: u8 -+ optional: OptionRangedI8 -+ } -+ RangedI16 { -+ mod_name: ranged_i16 -+ internal: i16 -+ signed: true -+ unsigned: u16 -+ optional: OptionRangedI16 -+ } -+ RangedI32 { -+ mod_name: ranged_i32 -+ internal: i32 -+ signed: true -+ unsigned: u32 -+ optional: OptionRangedI32 -+ } -+ RangedI64 { -+ mod_name: ranged_i64 -+ internal: i64 -+ signed: true -+ unsigned: u64 -+ optional: OptionRangedI64 -+ } -+ RangedI128 { -+ mod_name: ranged_i128 -+ internal: i128 -+ signed: true -+ unsigned: u128 -+ optional: OptionRangedI128 -+ } -+ RangedIsize { -+ mod_name: ranged_isize -+ internal: isize -+ signed: true -+ unsigned: usize -+ optional: OptionRangedIsize -+ } -+} -diff --git a/third_party/rust/deranged/src/tests.rs b/third_party/rust/deranged/src/tests.rs -new file mode 100644 -index 0000000000..e9a6ff7827 ---- /dev/null -+++ b/third_party/rust/deranged/src/tests.rs -@@ -0,0 +1,688 @@ -+use std::hash::Hash; -+ -+use crate::{ -+ IntErrorKind, OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI64, -+ OptionRangedI8, OptionRangedIsize, OptionRangedU128, OptionRangedU16, OptionRangedU32, -+ OptionRangedU64, OptionRangedU8, OptionRangedUsize, ParseIntError, RangedI128, RangedI16, -+ RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, RangedU32, RangedU64, -+ RangedU8, RangedUsize, TryFromIntError, -+}; -+ -+macro_rules! if_signed { -+ (signed $($x:tt)*) => { $($x)* }; -+ (unsigned $($x:tt)*) => {}; -+} -+ -+macro_rules! if_unsigned { -+ (signed $($x:tt)*) => {}; -+ (unsigned $($x:tt)*) => { $($x)* }; -+} -+ -+#[test] -+fn errors() { -+ assert_eq!( -+ TryFromIntError.to_string(), -+ "out of range integral type conversion attempted" -+ ); -+ assert_eq!(TryFromIntError.clone(), TryFromIntError); -+ assert_eq!(format!("{TryFromIntError:?}"), "TryFromIntError"); -+ -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::Empty, -+ } -+ .to_string(), -+ "cannot parse integer from empty string" -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::InvalidDigit, -+ } -+ .to_string(), -+ "invalid digit found in string" -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::PosOverflow, -+ } -+ .to_string(), -+ "number too large to fit in target type" -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::NegOverflow, -+ } -+ .to_string(), -+ "number too small to fit in target type" -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::Zero, -+ } -+ .to_string(), -+ "number would be zero for non-zero type" -+ ); -+ assert_eq!( -+ format!( -+ "{:?}", -+ ParseIntError { -+ kind: IntErrorKind::Empty -+ } -+ ), -+ "ParseIntError { kind: Empty }" -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::Empty -+ } -+ .clone(), -+ ParseIntError { -+ kind: IntErrorKind::Empty -+ } -+ ); -+ assert_eq!( -+ ParseIntError { -+ kind: IntErrorKind::Empty -+ } -+ .kind(), -+ &IntErrorKind::Empty -+ ); -+} -+ -+macro_rules! tests { -+ ($($signed:ident $opt:ident $t:ident $inner:ident),* $(,)?) => { -+ #[test] -+ fn derives() {$( -+ assert_eq!($t::<5, 10>::MIN.clone(), $t::<5, 10>::MIN); -+ let mut hasher = std::collections::hash_map::DefaultHasher::new(); -+ $t::<5, 10>::MIN.hash(&mut hasher); -+ assert_eq!( -+ $t::<5, 10>::MIN.cmp(&$t::<5, 10>::MAX), -+ std::cmp::Ordering::Less -+ ); -+ -+ assert_eq!($opt::<5, 10>::None.clone(), $opt::<5, 10>::None); -+ $opt::<5, 10>::None.hash(&mut hasher); -+ )*} -+ -+ #[test] -+ fn expand() {$( -+ let expanded: $t::<0, 20> = $t::<5, 10>::MAX.expand(); -+ assert_eq!(expanded, $t::<0, 20>::new_static::<10>()); -+ )*} -+ -+ #[test] -+ fn narrow() {$( -+ let narrowed: Option<$t::<10, 20>> = $t::<0, 20>::new_static::<10>().narrow(); -+ assert_eq!(narrowed, Some($t::<10, 20>::MIN)); -+ )*} -+ -+ #[test] -+ fn new() {$( -+ assert!($t::<5, 10>::new(10).is_some()); -+ assert!($t::<5, 10>::new(11).is_none()); -+ )*} -+ -+ #[test] -+ fn new_static() {$( -+ let six: $t::<5, 10> = $t::<5, 10>::new_static::<6>(); -+ assert_eq!(Some(six), $t::<5, 10>::new(6)); -+ )*} -+ -+ #[test] -+ fn some_unchecked() {$( -+ // Safety: The value is in range. -+ unsafe { -+ assert_eq!($opt::<5, 10>::some_unchecked(10), $opt::Some($t::<5, 10>::MAX)); -+ } -+ )*} -+ -+ #[test] -+ fn is_some() {$( -+ assert!($opt::<5, 10>::Some($t::<5, 10>::MAX).is_some()); -+ )*} -+ -+ #[test] -+ fn is_none() {$( -+ assert!($opt::<5, 10>::None.is_none()); -+ )*} -+ -+ #[test] -+ fn default() {$( -+ assert_eq!($opt::<5, 10>::default(), $opt::<5, 10>::None); -+ )*} -+ -+ #[test] -+ fn get() {$( -+ assert_eq!($t::<5, 10>::MAX.get(), 10); -+ assert_eq!($opt::<5, 10>::None.get(), None); -+ assert_eq!($opt::Some($t::<5, 10>::MAX).get(), Some($t::<5, 10>::MAX)); -+ )*} -+ -+ #[test] -+ fn get_primitive() {$( -+ assert_eq!($opt::Some($t::<5, 10>::MAX).get_primitive(), Some(10)); -+ assert_eq!($opt::<5, 10>::None.get_primitive(), None); -+ )*} -+ -+ #[test] -+ fn get_ref() {$( -+ assert_eq!($t::<5, 10>::MAX.get_ref(), &10); -+ )*} -+ -+ #[test] -+ fn new_saturating() {$( -+ assert_eq!($t::<5, 10>::new_saturating(11), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::new_saturating(0), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::new_saturating(9), $t::<5, 10>::new_static::<9>()); -+ )*} -+ -+ #[test] -+ fn from_str_radix() {$( -+ assert_eq!($t::<5, 10>::from_str_radix("10", 10), Ok($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::from_str_radix("5", 10), Ok($t::<5, 10>::MIN)); -+ assert_eq!( -+ $t::<5, 10>::from_str_radix("4", 10), -+ Err(ParseIntError { kind: IntErrorKind::NegOverflow }), -+ ); -+ assert_eq!( -+ $t::<5, 10>::from_str_radix("11", 10), -+ Err(ParseIntError { kind: IntErrorKind::PosOverflow }), -+ ); -+ assert_eq!( -+ $t::<5, 10>::from_str_radix("", 10), -+ Err(ParseIntError { kind: IntErrorKind::Empty }), -+ ); -+ )*} -+ -+ #[test] -+ fn checked_add() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_add(1), None); -+ assert_eq!($t::<5, 10>::MAX.checked_add(0), Some($t::<5, 10>::MAX)); -+ )*} -+ -+ #[test] -+ fn unchecked_add() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MIN.unchecked_add(5), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_sub() {$( -+ assert_eq!($t::<5, 10>::MIN.checked_sub(1), None); -+ assert_eq!($t::<5, 10>::MIN.checked_sub(0), Some($t::<5, 10>::MIN)); -+ )*} -+ -+ #[test] -+ fn unchecked_sub() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_sub(5), $t::<5, 10>::MIN); -+ } -+ )*} -+ -+ #[test] -+ fn checked_mul() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_mul(2), None); -+ assert_eq!($t::<5, 10>::MAX.checked_mul(1), Some($t::<5, 10>::MAX)); -+ )*} -+ -+ #[test] -+ fn unchecked_mul() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_mul(1), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_div() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_div(3), None); -+ assert_eq!($t::<5, 10>::MAX.checked_div(2), $t::<5, 10>::new(5)); -+ assert_eq!($t::<5, 10>::MAX.checked_div(1), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MAX.checked_div(0), None); -+ )*} -+ -+ #[test] -+ fn unchecked_div() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_div(1), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_div_euclid() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_div_euclid(3), None); -+ assert_eq!($t::<5, 10>::MAX.checked_div_euclid(2), $t::<5, 10>::new(5)); -+ assert_eq!($t::<5, 10>::MAX.checked_div_euclid(1), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MAX.checked_div_euclid(0), None); -+ )*} -+ -+ #[test] -+ fn unchecked_div_euclid() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_div_euclid(1), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn rem() {$(if_unsigned! { $signed -+ assert_eq!($t::<5, 10>::MAX.rem($t::exact::<3>()), $t::<0, 3>::new_static::<1>()); -+ assert_eq!($t::<5, 10>::MAX.rem($t::exact::<5>()), $t::<0, 5>::MIN); -+ })*} -+ -+ #[test] -+ fn checked_rem() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_rem(11), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MAX.checked_rem(5), None); -+ )*} -+ -+ #[test] -+ fn unchecked_rem() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_rem(11), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_rem_euclid() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(11), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MAX.checked_rem_euclid(5), None); -+ )*} -+ -+ #[test] -+ fn unchecked_rem_euclid() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_rem_euclid(11), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_neg() {$( -+ assert_eq!($t::<5, 10>::MIN.checked_neg(), None); -+ assert_eq!($t::<0, 10>::MIN.checked_neg(), Some($t::<0, 10>::MIN)); -+ )*} -+ -+ #[test] -+ fn unchecked_neg() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<0, 10>::MIN.unchecked_neg(), $t::<0, 10>::MIN); -+ } -+ )*} -+ -+ #[test] -+ fn neg() {$( if_signed! { $signed -+ assert_eq!($t::<-10, 10>::MIN.neg(), $t::<-10, 10>::MAX); -+ })*} -+ -+ #[test] -+ fn checked_shl() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_shl(1), None); -+ assert_eq!($t::<5, 10>::MAX.checked_shl(0), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MIN.checked_shl(1), Some($t::<5, 10>::MAX)); -+ )*} -+ -+ #[test] -+ fn unchecked_shl() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_shl(0), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::MIN.unchecked_shl(1), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_shr() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_shr(2), None); -+ assert_eq!($t::<5, 10>::MAX.checked_shr(1), Some($t::<5, 10>::MIN)); -+ assert_eq!($t::<5, 10>::MAX.checked_shr(0), Some($t::<5, 10>::MAX)); -+ )*} -+ -+ #[test] -+ fn unchecked_shr() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_shr(1), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MAX.unchecked_shr(0), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn checked_abs() {$( if_signed! { $signed -+ assert_eq!($t::<5, 10>::MAX.checked_abs(), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<-10, 10>::MIN.checked_abs(), Some($t::<-10, 10>::MAX)); -+ assert_eq!($t::<-10, 0>::MIN.checked_abs(), None); -+ })*} -+ -+ #[test] -+ fn unchecked_abs() { $(if_signed! { $signed -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_abs(), $t::<5, 10>::MAX); -+ assert_eq!($t::<-10, 10>::MIN.unchecked_abs(), $t::<-10, 10>::MAX); -+ } -+ })*} -+ -+ #[test] -+ fn abs() { $(if_signed! { $signed -+ assert_eq!($t::<-5, 10>::MIN.abs().get(), 5); -+ })*} -+ -+ #[test] -+ fn checked_pow() {$( -+ assert_eq!($t::<5, 10>::MAX.checked_pow(0), None); -+ assert_eq!($t::<5, 10>::MAX.checked_pow(1), Some($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::MAX.checked_pow(2), None); -+ )*} -+ -+ #[test] -+ fn unchecked_pow() {$( -+ // Safety: The result is in range. -+ unsafe { -+ assert_eq!($t::<5, 10>::MAX.unchecked_pow(1), $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[test] -+ fn saturating_add() {$( -+ assert_eq!($t::<5, 10>::MAX.saturating_add(0), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::MAX.saturating_add(1), $t::<5, 10>::MAX); -+ )*} -+ -+ #[test] -+ fn wrapping_add() { -+ $( -+ assert_eq!($t::<5, 10>::MAX.wrapping_add(0), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::MAX.wrapping_add(1), $t::<5, 10>::MIN); -+ assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MAX.wrapping_add(1), -+ $t::<{ $inner::MIN }, { $inner::MAX }>::MIN); -+ for i in 1..127 { -+ assert_eq!( -+ $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::MAX.wrapping_add(i), -+ $t::<{ $inner::MIN}, { $inner::MAX - 1 }>::new($inner::MIN + i - 1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, $inner::MAX + i )) -+ ); -+ } -+ )* -+ $(if_signed! { $signed -+ for i in 1..=127 { -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(126-i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(-5+i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); -+ } -+ for i in -127..=-1 { -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::new(126+i+1).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::new(-5-i).unwrap_or_else(|| panic!("adding {i}+{} does not yield {}", $inner::MIN, 126-i+1))); -+ } -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(-128), $t::<-5,126>::new(-1).unwrap_or_else(|| panic!("adding 128+{} does not yield -1", $inner::MIN))); -+ assert_eq!($t::<-5, 10>::MAX.wrapping_add(0), $t::<-5, 10>::MAX); -+ assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-3), $t::<-5, -3>::MAX); -+ assert_eq!($t::<-5, -3>::MIN.wrapping_add(-1-30), $t::<-5, -3>::MAX); -+ assert_eq!($t::<-5, -3>::MIN.wrapping_add(30), $t::<-5, -3>::MIN); -+ assert_eq!($t::<-5, -3>::MIN.wrapping_add(-30), $t::<-5, -3>::MIN); -+ assert_eq!($t::<-5, 10>::MAX.wrapping_add(25), $t::<-5, 10>::MIN.wrapping_add(24)); -+ assert_eq!($t::<-5, 10>::MIN.wrapping_add(24), $t::<-5, 10>::MIN.wrapping_add(8)); -+ assert_eq!($t::<-5, 10>::MAX.wrapping_add(1), $t::<-5, 10>::MIN); -+ assert_eq!($t::<-5, 10>::MIN.wrapping_add(-1), $t::<-5, 10>::MAX); -+ assert_eq!($t::<-5, 127>::MIN.wrapping_add(-1), $t::<-5, 127>::MAX); -+ assert_eq!($t::<-127, 126>::MIN.wrapping_add(-1), $t::<-127, 126>::MAX); -+ assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_add(-1), -+ $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); -+ })* -+ } -+ -+ #[test] -+ fn wrapping_sub() { -+ $( -+ assert_eq!($t::<5, 10>::MIN.wrapping_sub(0), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.wrapping_sub(1), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::new(5 + 1).unwrap().wrapping_sub(1), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MAX.wrapping_sub(1), $t::<5, 10>::new(10 - 1).unwrap()); -+ assert_eq!($t::<{ $inner::MIN }, { $inner::MAX }>::MIN.wrapping_sub(1), -+ $t::<{ $inner::MIN }, { $inner::MAX }>::MAX); -+ for i in 1..127 { -+ assert_eq!( -+ $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::MIN.wrapping_sub(i), -+ $t::<{ $inner::MIN + 1 }, { $inner::MAX }>::new($inner::MAX - i + 1).unwrap_or_else(|| panic!("failed test at iteration {i}")) -+ ); -+ } -+ )* -+ $(if_signed! { $signed -+ for i in -127..=127 { -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(i), $t::<-5,126>::MIN.wrapping_sub(-i), "failed test at {i}"); -+ assert_eq!($t::<-5, 126>::MIN.wrapping_add(-i), $t::<-5,126>::MIN.wrapping_sub(i), "failed test at {i}"); -+ } -+ assert_eq!( -+ $t::<-5, 126>::MIN.wrapping_add(127).wrapping_add(1), -+ $t::<-5,126>::MIN.wrapping_sub(-128) -+ ); -+ assert_eq!( -+ $t::<-5, 126>::MIN.wrapping_add(-128), -+ $t::<-5,126>::MIN.wrapping_sub(127).wrapping_sub(1) -+ ); -+ })* -+ } -+ -+ #[test] -+ fn saturating_sub() {$( -+ assert_eq!($t::<5, 10>::MIN.saturating_sub(0), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.saturating_sub(1), $t::<5, 10>::MIN); -+ )*} -+ -+ #[test] -+ fn saturating_neg() {$(if_signed! { $signed -+ assert_eq!($t::<5, 10>::MIN.saturating_neg(), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MAX.saturating_neg(), $t::<5, 10>::MIN); -+ assert_eq!($t::<-10, 0>::MIN.saturating_neg(), $t::<-10, 0>::MAX); -+ assert_eq!($t::<-10, 0>::MAX.saturating_neg(), $t::<-10, 0>::MAX); -+ })*} -+ -+ #[test] -+ fn saturating_abs() {$(if_signed! { $signed -+ assert_eq!($t::<5, 10>::MIN.saturating_abs(), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MAX.saturating_abs(), $t::<5, 10>::MAX); -+ assert_eq!($t::<-10, 0>::MIN.saturating_abs(), $t::<-10, 0>::MAX); -+ assert_eq!($t::<-10, 0>::MAX.saturating_abs(), $t::<-10, 0>::MAX); -+ })*} -+ -+ #[test] -+ fn saturating_mul() {$( -+ assert_eq!($t::<5, 10>::MIN.saturating_mul(0), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.saturating_mul(1), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.saturating_mul(2), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::MIN.saturating_mul(3), $t::<5, 10>::MAX); -+ )*} -+ -+ #[test] -+ fn saturating_pow() {$( -+ assert_eq!($t::<5, 10>::MIN.saturating_pow(0), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.saturating_pow(1), $t::<5, 10>::MIN); -+ assert_eq!($t::<5, 10>::MIN.saturating_pow(2), $t::<5, 10>::MAX); -+ assert_eq!($t::<5, 10>::MIN.saturating_pow(3), $t::<5, 10>::MAX); -+ )*} -+ -+ #[test] -+ fn as_ref() {$( -+ assert_eq!($t::<5, 10>::MIN.as_ref(), &5); -+ assert_eq!($t::<5, 10>::MAX.as_ref(), &10); -+ )*} -+ -+ #[test] -+ fn borrow() { -+ use std::borrow::Borrow; -+ $( -+ assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MIN), &5); -+ assert_eq!(Borrow::<$inner>::borrow(&$t::<5, 10>::MAX), &10); -+ )* -+ } -+ -+ #[test] -+ fn formatting() {$( -+ let val = $t::<5, 10>::MAX; -+ assert_eq!(format!("{}", val), "10"); -+ assert_eq!(format!("{:?}", val), "10"); -+ assert_eq!(format!("{:b}", val), "1010"); -+ assert_eq!(format!("{:o}", val), "12"); -+ assert_eq!(format!("{:x}", val), "a"); -+ assert_eq!(format!("{:X}", val), "A"); -+ assert_eq!(format!("{:e}", val), "1e1"); -+ assert_eq!(format!("{:E}", val), "1E1"); -+ -+ assert_eq!(format!("{:?}", $opt::Some($t::<5, 10>::MAX)), "Some(10)"); -+ assert_eq!(format!("{:?}", $opt::<5, 10>::None), "None"); -+ )*} -+ -+ #[test] -+ fn ord() {$( -+ assert!($t::<5, 10>::MIN < $t::<5, 10>::MAX); -+ assert!($t::<5, 10>::MIN <= $t::<5, 10>::MAX); -+ assert!($t::<5, 10>::MAX > $t::<5, 10>::MIN); -+ assert!($t::<5, 10>::MAX >= $t::<5, 10>::MIN); -+ -+ let none = $opt::<5, 10>::None; -+ let five = $opt::Some($t::<5, 10>::MIN); -+ let ten = $opt::Some($t::<5, 10>::MAX); -+ -+ assert_eq!(none.cmp(&none), std::cmp::Ordering::Equal); -+ assert_eq!(five.cmp(&five), std::cmp::Ordering::Equal); -+ assert_eq!(ten.cmp(&ten), std::cmp::Ordering::Equal); -+ assert_eq!(none.cmp(&five), std::cmp::Ordering::Less); -+ assert_eq!(five.cmp(&ten), std::cmp::Ordering::Less); -+ assert_eq!(none.cmp(&ten), std::cmp::Ordering::Less); -+ assert_eq!(ten.cmp(&none), std::cmp::Ordering::Greater); -+ -+ let none = $opt::<0, 10>::None; -+ let zero = $opt::Some($t::<0, 10>::MIN); -+ let ten = $opt::Some($t::<0, 10>::MAX); -+ -+ assert_eq!(none.partial_cmp(&none), Some(std::cmp::Ordering::Equal)); -+ assert_eq!(none.partial_cmp(&zero), Some(std::cmp::Ordering::Less)); -+ assert_eq!(zero.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); -+ assert_eq!(none.partial_cmp(&ten), Some(std::cmp::Ordering::Less)); -+ assert_eq!(ten.partial_cmp(&none), Some(std::cmp::Ordering::Greater)); -+ )*} -+ -+ #[test] -+ fn from() {$( -+ assert_eq!($inner::from($t::<5, 10>::MAX), 10); -+ assert_eq!($inner::from($t::<5, 10>::MIN), 5); -+ -+ assert_eq!($opt::from($t::<5, 10>::MAX), $opt::Some($t::<5, 10>::MAX)); -+ assert_eq!($opt::from(Some($t::<5, 10>::MAX)), $opt::Some($t::<5, 10>::MAX)); -+ assert_eq!($opt::<5, 10>::from(None), $opt::<5, 10>::None); -+ assert_eq!(Option::from($opt::Some($t::<5, 10>::MAX)), Some($t::<5, 10>::MAX)); -+ assert_eq!(Option::<$t<5, 10>>::from($opt::<5, 10>::None), None); -+ )*} -+ -+ #[test] -+ fn try_from() {$( -+ assert_eq!($t::<5, 10>::try_from(10), Ok($t::<5, 10>::MAX)); -+ assert_eq!($t::<5, 10>::try_from(5), Ok($t::<5, 10>::MIN)); -+ assert_eq!($t::<5, 10>::try_from(4), Err(TryFromIntError)); -+ assert_eq!($t::<5, 10>::try_from(11), Err(TryFromIntError)); -+ )*} -+ -+ #[test] -+ fn from_str() {$( -+ assert_eq!("10".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MAX)); -+ assert_eq!("5".parse::<$t<5, 10>>(), Ok($t::<5, 10>::MIN)); -+ assert_eq!("4".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::NegOverflow })); -+ assert_eq!("11".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::PosOverflow })); -+ assert_eq!("".parse::<$t<5, 10>>(), Err(ParseIntError { kind: IntErrorKind::Empty })); -+ )*} -+ -+ #[cfg(feature = "serde")] -+ #[test] -+ fn serde() -> serde_json::Result<()> { -+ $( -+ let val = $t::<5, 10>::MAX; -+ let serialized = serde_json::to_string(&val)?; -+ assert_eq!(serialized, "10"); -+ let deserialized: $t<5, 10> = serde_json::from_str(&serialized)?; -+ assert_eq!(deserialized, val); -+ -+ assert!(serde_json::from_str::<$t<5, 10>>("").is_err()); -+ assert!(serde_json::from_str::<$t<5, 10>>("4").is_err()); -+ assert!(serde_json::from_str::<$t<5, 10>>("11").is_err()); -+ -+ let val = $opt::<5, 10>::Some($t::<5, 10>::MAX); -+ let serialized = serde_json::to_string(&val)?; -+ assert_eq!(serialized, "10"); -+ let deserialized: $opt<5, 10> = serde_json::from_str(&serialized)?; -+ assert_eq!(deserialized, val); -+ -+ assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); -+ assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); -+ assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); -+ -+ let val = $opt::<5, 10>::None; -+ let serialized = serde_json::to_string(&val)?; -+ assert_eq!(serialized, "null"); -+ -+ assert!(serde_json::from_str::<$opt<5, 10>>("").is_err()); -+ assert!(serde_json::from_str::<$opt<5, 10>>("4").is_err()); -+ assert!(serde_json::from_str::<$opt<5, 10>>("11").is_err()); -+ )* -+ Ok(()) -+ } -+ -+ #[cfg(feature = "rand")] -+ #[test] -+ fn rand() {$( -+ let rand_val: $t<5, 10> = rand::random(); -+ assert!(rand_val >= $t::<5, 10>::MIN); -+ assert!(rand_val <= $t::<5, 10>::MAX); -+ -+ let rand: $opt<5, 10> = rand::random(); -+ if let Some(rand) = rand.get() { -+ assert!(rand >= $t::<5, 10>::MIN); -+ assert!(rand <= $t::<5, 10>::MAX); -+ } -+ )*} -+ -+ #[cfg(feature = "num")] -+ #[test] -+ fn num() {$( -+ assert_eq!(<$t<5, 10> as num_traits::Bounded>::min_value(), $t::<5, 10>::MIN); -+ assert_eq!(<$t<5, 10> as num_traits::Bounded>::max_value(), $t::<5, 10>::MAX); -+ )*} -+ -+ #[cfg(feature = "quickcheck")] -+ #[test] -+ fn quickcheck() {$( -+ #[allow(trivial_casts)] -+ quickcheck::quickcheck((|val| { -+ val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX -+ }) as fn($t<5, 10>) -> bool); -+ -+ #[allow(trivial_casts)] -+ quickcheck::quickcheck((|val| { -+ if let Some(val) = val.get() { -+ val >= $t::<5, 10>::MIN && val <= $t::<5, 10>::MAX -+ } else { -+ true -+ } -+ }) as fn($opt<5, 10>) -> bool); -+ )*} -+ }; -+} -+ -+tests![ -+ signed OptionRangedI8 RangedI8 i8, -+ signed OptionRangedI16 RangedI16 i16, -+ signed OptionRangedI32 RangedI32 i32, -+ signed OptionRangedI64 RangedI64 i64, -+ signed OptionRangedI128 RangedI128 i128, -+ signed OptionRangedIsize RangedIsize isize, -+ unsigned OptionRangedU8 RangedU8 u8, -+ unsigned OptionRangedU16 RangedU16 u16, -+ unsigned OptionRangedU32 RangedU32 u32, -+ unsigned OptionRangedU64 RangedU64 u64, -+ unsigned OptionRangedU128 RangedU128 u128, -+ unsigned OptionRangedUsize RangedUsize usize, -+]; -diff --git a/third_party/rust/deranged/src/traits.rs b/third_party/rust/deranged/src/traits.rs -new file mode 100644 -index 0000000000..d1b69ac01f ---- /dev/null -+++ b/third_party/rust/deranged/src/traits.rs -@@ -0,0 +1,117 @@ -+use crate::{ -+ RangedI128, RangedI16, RangedI32, RangedI64, RangedI8, RangedIsize, RangedU128, RangedU16, -+ RangedU32, RangedU64, RangedU8, RangedUsize, -+}; -+ -+macro_rules! declare_traits { -+ ($($trait_name:ident),* $(,)?) => {$( -+ pub(crate) trait $trait_name { -+ const ASSERT: (); -+ } -+ )*}; -+} -+ -+macro_rules! impl_traits_for_all { -+ ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( -+ impl RangeIsValid for $ranged_ty { -+ const ASSERT: () = assert!(MIN <= MAX); -+ } -+ -+ impl< -+ const CURRENT_MIN: $inner_ty, -+ const CURRENT_MAX: $inner_ty, -+ const NEW_MIN: $inner_ty, -+ const NEW_MAX: $inner_ty, -+ > ExpandIsValid for ($ranged_ty, $ranged_ty) { -+ const ASSERT: () = { -+ assert!(NEW_MIN <= CURRENT_MIN); -+ assert!(NEW_MAX >= CURRENT_MAX); -+ }; -+ } -+ -+ impl< -+ const CURRENT_MIN: $inner_ty, -+ const CURRENT_MAX: $inner_ty, -+ const NEW_MIN: $inner_ty, -+ const NEW_MAX: $inner_ty, -+ > NarrowIsValid for ($ranged_ty, $ranged_ty) { -+ const ASSERT: () = { -+ assert!(NEW_MIN >= CURRENT_MIN); -+ assert!(NEW_MAX <= CURRENT_MAX); -+ }; -+ } -+ -+ impl< -+ const VALUE: $inner_ty, -+ const MIN: $inner_ty, -+ const MAX: $inner_ty, -+ > StaticIsValid for ($ranged_ty, $ranged_ty) { -+ const ASSERT: () = { -+ assert!(VALUE >= MIN); -+ assert!(VALUE <= MAX); -+ }; -+ } -+ )*}; -+} -+ -+macro_rules! impl_traits_for_signed { -+ ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( -+ impl AbsIsSafe for $ranged_ty { -+ const ASSERT: () = { -+ assert!(MIN != <$inner_ty>::MIN); -+ assert!(-MIN <= MAX); -+ }; -+ } -+ -+ impl NegIsSafe for $ranged_ty { -+ const ASSERT: () = { -+ assert!(MIN != <$inner_ty>::MIN); -+ assert!(-MIN <= MAX); -+ assert!(-MAX >= MIN); -+ }; -+ } -+ -+ impl_traits_for_all!($ranged_ty $inner_ty); -+ )*}; -+} -+ -+macro_rules! impl_traits_for_unsigned { -+ ($($ranged_ty:ident $inner_ty:ident),* $(,)?) => {$( -+ impl AbsIsSafe for $ranged_ty { -+ const ASSERT: () = (); -+ } -+ -+ impl NegIsSafe for $ranged_ty { -+ const ASSERT: () = assert!(MAX == 0); -+ } -+ -+ impl_traits_for_all!($ranged_ty $inner_ty); -+ )*}; -+} -+ -+declare_traits![ -+ RangeIsValid, -+ AbsIsSafe, -+ NegIsSafe, -+ ExpandIsValid, -+ NarrowIsValid, -+ StaticIsValid, -+]; -+ -+impl_traits_for_signed! { -+ RangedI8 i8, -+ RangedI16 i16, -+ RangedI32 i32, -+ RangedI64 i64, -+ RangedI128 i128, -+ RangedIsize isize, -+} -+ -+impl_traits_for_unsigned! { -+ RangedU8 u8, -+ RangedU16 u16, -+ RangedU32 u32, -+ RangedU64 u64, -+ RangedU128 u128, -+ RangedUsize usize, -+} -diff --git a/third_party/rust/deranged/src/unsafe_wrapper.rs b/third_party/rust/deranged/src/unsafe_wrapper.rs -new file mode 100644 -index 0000000000..8620e12176 ---- /dev/null -+++ b/third_party/rust/deranged/src/unsafe_wrapper.rs -@@ -0,0 +1,26 @@ -+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -+pub(crate) struct Unsafe(T); -+ -+impl core::fmt::Debug for Unsafe { -+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { -+ self.0.fmt(f) -+ } -+} -+ -+impl Unsafe { -+ pub(crate) const unsafe fn new(value: T) -> Self { -+ Self(value) -+ } -+ -+ pub(crate) const fn get(&self) -> &T { -+ &self.0 -+ } -+} -+ -+impl core::ops::Deref for Unsafe { -+ type Target = T; -+ -+ fn deref(&self) -> &Self::Target { -+ &self.0 -+ } -+} -diff --git a/third_party/rust/num-conv/.cargo-checksum.json b/third_party/rust/num-conv/.cargo-checksum.json -new file mode 100644 -index 0000000000..3886e69781 ---- /dev/null -+++ b/third_party/rust/num-conv/.cargo-checksum.json -@@ -0,0 +1 @@ -+{"files":{"Cargo.toml":"c1d8999190f493d43b84b07eaffd2a9144e16be197bb3ef13eb69305e2f23047","LICENSE-Apache":"c0fd5f9df8d17e13587f8fe403d2326b835e60d532817d0b42ae4aea44209251","LICENSE-MIT":"af85fff507d80e6c7ff242acfc4b0a7f5de9a72286bb3c883c782772ca4b4402","src/lib.rs":"ab6c4b28902164204179f5c31473753fbe5220a4b23082e227478e19c2aa47ca"},"package":"51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"} -\ No newline at end of file -diff --git a/third_party/rust/num-conv/Cargo.toml b/third_party/rust/num-conv/Cargo.toml -new file mode 100644 -index 0000000000..0b8a9a6c01 ---- /dev/null -+++ b/third_party/rust/num-conv/Cargo.toml -@@ -0,0 +1,55 @@ -+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -+# -+# When uploading crates to the registry Cargo will automatically -+# "normalize" Cargo.toml files for maximal compatibility -+# with all versions of Cargo and also rewrite `path` dependencies -+# to registry (e.g., crates.io) dependencies. -+# -+# If you are reading this file be aware that the original Cargo.toml -+# will likely look very different (and much more reasonable). -+# See Cargo.toml.orig for the original contents. -+ -+[package] -+edition = "2021" -+rust-version = "1.57.0" -+name = "num-conv" -+version = "0.1.0" -+authors = ["Jacob Pratt "] -+include = [ -+ "src/**/*", -+ "LICENSE-*", -+] -+description = """ -+`num_conv` is a crate to convert between integer types without using `as` casts. This provides -+better certainty when refactoring, makes the exact behavior of code more explicit, and allows using -+turbofish syntax. -+""" -+readme = "README.md" -+keywords = [ -+ "cast", -+ "extend", -+ "truncate", -+ "convert", -+ "integer", -+] -+categories = [ -+ "no-std", -+ "no-std::no-alloc", -+ "rust-patterns", -+] -+license = "MIT OR Apache-2.0" -+repository = "https://github.com/jhpratt/num-conv" -+ -+[package.metadata.docs.rs] -+rustdoc-args = ["--generate-link-to-definition"] -+ -+[features] -+ -+[lints.clippy] -+alloc-instead-of-core = "deny" -+std-instead-of-core = "deny" -+ -+[lints.rust] -+missing-docs = "warn" -+unreachable-pub = "warn" -+unused = "warn" -diff --git a/third_party/rust/num-conv/LICENSE-Apache b/third_party/rust/num-conv/LICENSE-Apache -new file mode 100644 -index 0000000000..d8bca8b9f1 ---- /dev/null -+++ b/third_party/rust/num-conv/LICENSE-Apache -@@ -0,0 +1,202 @@ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright 2023 Jacob Pratt -+ -+ Licensed under the Apache License, Version 2.0 (the "License"); -+ you may not use this file except in compliance with the License. -+ You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+ Unless required by applicable law or agreed to in writing, software -+ distributed under the License is distributed on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ See the License for the specific language governing permissions and -+ limitations under the License. -diff --git a/third_party/rust/num-conv/LICENSE-MIT b/third_party/rust/num-conv/LICENSE-MIT -new file mode 100644 -index 0000000000..7c7f78a5ed ---- /dev/null -+++ b/third_party/rust/num-conv/LICENSE-MIT -@@ -0,0 +1,19 @@ -+Copyright (c) 2023 Jacob Pratt -+ -+Permission is hereby granted, free of charge, to any person obtaining a copy -+of this software and associated documentation files (the "Software"), to deal -+in the Software without restriction, including without limitation the rights -+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+copies of the Software, and to permit persons to whom the Software is -+furnished to do so, subject to the following conditions: -+ -+The above copyright notice and this permission notice shall be included in all -+copies or substantial portions of the Software. -+ -+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+SOFTWARE. -diff --git a/third_party/rust/num-conv/src/lib.rs b/third_party/rust/num-conv/src/lib.rs -new file mode 100644 -index 0000000000..cbf3ff21d6 ---- /dev/null -+++ b/third_party/rust/num-conv/src/lib.rs -@@ -0,0 +1,329 @@ -+//! `num_conv` is a crate to convert between integer types without using `as` casts. This provides -+//! better certainty when refactoring, makes the exact behavior of code more explicit, and allows -+//! using turbofish syntax. -+ -+#![no_std] -+ -+/// Anonymously import all extension traits. -+/// -+/// This allows you to use the methods without worrying about polluting the namespace or importing -+/// them individually. -+/// -+/// ```rust -+/// use num_conv::prelude::*; -+/// ``` -+pub mod prelude { -+ pub use crate::{CastSigned as _, CastUnsigned as _, Extend as _, Truncate as _}; -+} -+ -+mod sealed { -+ pub trait Integer {} -+ -+ macro_rules! impl_integer { -+ ($($t:ty)*) => {$( -+ impl Integer for $t {} -+ )*}; -+ } -+ -+ impl_integer! { -+ u8 u16 u32 u64 u128 usize -+ i8 i16 i32 i64 i128 isize -+ } -+ -+ pub trait ExtendTargetSealed { -+ fn extend(self) -> T; -+ } -+ -+ pub trait TruncateTargetSealed { -+ fn truncate(self) -> T; -+ } -+} -+ -+/// Cast to a signed integer of the same size. -+/// -+/// This trait is implemented for all integers. Unsigned to signed casts are equivalent to -+/// `0.wrapping_add_signed(value)`, while signed to signed casts are an identity conversion. -+/// -+/// ```rust -+/// # use num_conv::CastSigned; -+/// assert_eq!(u8::MAX.cast_signed(), -1_i8); -+/// assert_eq!(u16::MAX.cast_signed(), -1_i16); -+/// assert_eq!(u32::MAX.cast_signed(), -1_i32); -+/// assert_eq!(u64::MAX.cast_signed(), -1_i64); -+/// assert_eq!(u128::MAX.cast_signed(), -1_i128); -+/// assert_eq!(usize::MAX.cast_signed(), -1_isize); -+/// ``` -+/// -+/// ```rust -+/// # use num_conv::CastSigned; -+/// assert_eq!(0_i8.cast_signed(), 0_i8); -+/// assert_eq!(0_i16.cast_signed(), 0_i16); -+/// assert_eq!(0_i32.cast_signed(), 0_i32); -+/// assert_eq!(0_i64.cast_signed(), 0_i64); -+/// assert_eq!(0_i128.cast_signed(), 0_i128); -+/// assert_eq!(0_isize.cast_signed(), 0_isize); -+/// ``` -+pub trait CastSigned: sealed::Integer { -+ /// The signed integer type with the same size as `Self`. -+ type Signed; -+ -+ /// Cast an integer to the signed integer of the same size. -+ fn cast_signed(self) -> Self::Signed; -+} -+ -+/// Cast to an unsigned integer of the same size. -+/// -+/// This trait is implemented for all integers. Signed to unsigned casts are equivalent to -+/// `0.wrapping_add_unsigned(value)`, while unsigned to unsigned casts are an identity conversion. -+/// -+/// ```rust -+/// # use num_conv::CastUnsigned; -+/// assert_eq!((-1_i8).cast_unsigned(), u8::MAX); -+/// assert_eq!((-1_i16).cast_unsigned(), u16::MAX); -+/// assert_eq!((-1_i32).cast_unsigned(), u32::MAX); -+/// assert_eq!((-1_i64).cast_unsigned(), u64::MAX); -+/// assert_eq!((-1_i128).cast_unsigned(), u128::MAX); -+/// assert_eq!((-1_isize).cast_unsigned(), usize::MAX); -+/// ``` -+/// -+/// ```rust -+/// # use num_conv::CastUnsigned; -+/// assert_eq!(0_u8.cast_unsigned(), 0_u8); -+/// assert_eq!(0_u16.cast_unsigned(), 0_u16); -+/// assert_eq!(0_u32.cast_unsigned(), 0_u32); -+/// assert_eq!(0_u64.cast_unsigned(), 0_u64); -+/// assert_eq!(0_u128.cast_unsigned(), 0_u128); -+/// assert_eq!(0_usize.cast_unsigned(), 0_usize); -+/// ``` -+pub trait CastUnsigned: sealed::Integer { -+ /// The unsigned integer type with the same size as `Self`. -+ type Unsigned; -+ -+ /// Cast an integer to the unsigned integer of the same size. -+ fn cast_unsigned(self) -> Self::Unsigned; -+} -+ -+/// A type that can be used with turbofish syntax in [`Extend::extend`]. -+/// -+/// It is unlikely that you will want to use this trait directly. You are probably looking for the -+/// [`Extend`] trait. -+pub trait ExtendTarget: sealed::ExtendTargetSealed {} -+ -+/// A type that can be used with turbofish syntax in [`Truncate::truncate`]. -+/// -+/// It is unlikely that you will want to use this trait directly. You are probably looking for the -+/// [`Truncate`] trait. -+pub trait TruncateTarget: sealed::TruncateTargetSealed {} -+ -+/// Extend to an integer of the same size or larger, preserving its value. -+/// -+/// ```rust -+/// # use num_conv::Extend; -+/// assert_eq!(0_u8.extend::(), 0_u16); -+/// assert_eq!(0_u16.extend::(), 0_u32); -+/// assert_eq!(0_u32.extend::(), 0_u64); -+/// assert_eq!(0_u64.extend::(), 0_u128); -+/// ``` -+/// -+/// ```rust -+/// # use num_conv::Extend; -+/// assert_eq!((-1_i8).extend::(), -1_i16); -+/// assert_eq!((-1_i16).extend::(), -1_i32); -+/// assert_eq!((-1_i32).extend::(), -1_i64); -+/// assert_eq!((-1_i64).extend::(), -1_i128); -+/// ``` -+pub trait Extend: sealed::Integer { -+ /// Extend an integer to an integer of the same size or larger, preserving its value. -+ fn extend(self) -> T -+ where -+ Self: ExtendTarget; -+} -+ -+impl Extend for T { -+ fn extend(self) -> U -+ where -+ T: ExtendTarget, -+ { -+ sealed::ExtendTargetSealed::extend(self) -+ } -+} -+ -+/// Truncate to an integer of the same size or smaller, preserving the least significant bits. -+/// -+/// ```rust -+/// # use num_conv::Truncate; -+/// assert_eq!(u16::MAX.truncate::(), u8::MAX); -+/// assert_eq!(u32::MAX.truncate::(), u16::MAX); -+/// assert_eq!(u64::MAX.truncate::(), u32::MAX); -+/// assert_eq!(u128::MAX.truncate::(), u64::MAX); -+/// ``` -+/// -+/// ```rust -+/// # use num_conv::Truncate; -+/// assert_eq!((-1_i16).truncate::(), -1_i8); -+/// assert_eq!((-1_i32).truncate::(), -1_i16); -+/// assert_eq!((-1_i64).truncate::(), -1_i32); -+/// assert_eq!((-1_i128).truncate::(), -1_i64); -+/// ``` -+pub trait Truncate: sealed::Integer { -+ /// Truncate an integer to an integer of the same size or smaller, preserving the least -+ /// significant bits. -+ fn truncate(self) -> T -+ where -+ Self: TruncateTarget; -+} -+ -+impl Truncate for T { -+ fn truncate(self) -> U -+ where -+ T: TruncateTarget, -+ { -+ sealed::TruncateTargetSealed::truncate(self) -+ } -+} -+ -+macro_rules! impl_cast_signed { -+ ($($($from:ty),+ => $to:ty;)*) => {$($( -+ const _: () = assert!( -+ core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), -+ concat!( -+ "cannot cast ", -+ stringify!($from), -+ " to ", -+ stringify!($to), -+ " because they are different sizes" -+ ) -+ ); -+ -+ impl CastSigned for $from { -+ type Signed = $to; -+ fn cast_signed(self) -> Self::Signed { -+ self as _ -+ } -+ } -+ )+)*}; -+} -+ -+macro_rules! impl_cast_unsigned { -+ ($($($from:ty),+ => $to:ty;)*) => {$($( -+ const _: () = assert!( -+ core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), -+ concat!( -+ "cannot cast ", -+ stringify!($from), -+ " to ", -+ stringify!($to), -+ " because they are different sizes" -+ ) -+ ); -+ -+ impl CastUnsigned for $from { -+ type Unsigned = $to; -+ fn cast_unsigned(self) -> Self::Unsigned { -+ self as _ -+ } -+ } -+ )+)*}; -+} -+ -+macro_rules! impl_extend { -+ ($($from:ty => $($to:ty),+;)*) => {$($( -+ const _: () = assert!( -+ core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(), -+ concat!( -+ "cannot extend ", -+ stringify!($from), -+ " to ", -+ stringify!($to), -+ " because ", -+ stringify!($from), -+ " is larger than ", -+ stringify!($to) -+ ) -+ ); -+ -+ impl sealed::ExtendTargetSealed<$to> for $from { -+ fn extend(self) -> $to { -+ self as _ -+ } -+ } -+ -+ impl ExtendTarget<$to> for $from {} -+ )+)*}; -+} -+ -+macro_rules! impl_truncate { -+ ($($($from:ty),+ => $to:ty;)*) => {$($( -+ const _: () = assert!( -+ core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(), -+ concat!( -+ "cannot truncate ", -+ stringify!($from), -+ " to ", -+ stringify!($to), -+ " because ", -+ stringify!($from), -+ " is smaller than ", -+ stringify!($to) -+ ) -+ ); -+ -+ impl sealed::TruncateTargetSealed<$to> for $from { -+ fn truncate(self) -> $to { -+ self as _ -+ } -+ } -+ -+ impl TruncateTarget<$to> for $from {} -+ )+)*}; -+} -+ -+impl_cast_signed! { -+ u8, i8 => i8; -+ u16, i16 => i16; -+ u32, i32 => i32; -+ u64, i64 => i64; -+ u128, i128 => i128; -+ usize, isize => isize; -+} -+ -+impl_cast_unsigned! { -+ u8, i8 => u8; -+ u16, i16 => u16; -+ u32, i32 => u32; -+ u64, i64 => u64; -+ u128, i128 => u128; -+ usize, isize => usize; -+} -+ -+impl_extend! { -+ u8 => u8, u16, u32, u64, u128, usize; -+ u16 => u16, u32, u64, u128, usize; -+ u32 => u32, u64, u128; -+ u64 => u64, u128; -+ u128 => u128; -+ usize => usize; -+ -+ i8 => i8, i16, i32, i64, i128, isize; -+ i16 => i16, i32, i64, i128, isize; -+ i32 => i32, i64, i128; -+ i64 => i64, i128; -+ i128 => i128; -+ isize => isize; -+} -+ -+impl_truncate! { -+ u8, u16, u32, u64, u128, usize => u8; -+ u16, u32, u64, u128, usize => u16; -+ u32, u64, u128 => u32; -+ u64, u128 => u64; -+ u128 => u128; -+ usize => usize; -+ -+ i8, i16, i32, i64, i128, isize => i8; -+ i16, i32, i64, i128, isize => i16; -+ i32, i64, i128 => i32; -+ i64, i128 => i64; -+ i128 => i128; -+ isize => isize; -+} -diff --git a/third_party/rust/powerfmt/.cargo-checksum.json b/third_party/rust/powerfmt/.cargo-checksum.json -new file mode 100644 -index 0000000000..15fec13022 ---- /dev/null -+++ b/third_party/rust/powerfmt/.cargo-checksum.json -@@ -0,0 +1 @@ -+{"files":{"Cargo.toml":"59fa10abb1a34f70e61c97022938b02ec77ea0b161524f4a2599440bd3190c3b","LICENSE-Apache":"155420c6403d4e0fca34105e3c03fdd6939b64c393c7ec6f95f5b72c5474eab0","LICENSE-MIT":"070dbc7dda03a29296f2d58bdb9b7331af90f2abc9f31df22875d1eabaf29852","README.md":"188fa8a1323086828b9eeaf5a2031d66f066b617fc7dec318835a685d7c2e3c7","src/buf.rs":"b76bcb3daff67ed24e3e5fd958d98565c753107d368c3204ae0d70f9ca7394d4","src/ext.rs":"e6e5063f0006bfe92b59712032d4c6dfe1e1d302d8c92596f3eb7b42f747b9b4","src/lib.rs":"b7a6d061f8d79ed7d78088edefb5946d8eb911b1b523ef849f5f989533955b03","src/smart_display.rs":"44d89db6dbefc1b90e2e3e42279d9ae58f77baeab27a30cafebdb84bfdfaf03c","src/smart_display_impls.rs":"833c5dcb851f7979b222f4736e704ab50bba4d42ef264253dc57237381ef0e5b"},"package":"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"} -\ No newline at end of file -diff --git a/third_party/rust/powerfmt/Cargo.toml b/third_party/rust/powerfmt/Cargo.toml -new file mode 100644 -index 0000000000..f7acec3944 ---- /dev/null -+++ b/third_party/rust/powerfmt/Cargo.toml -@@ -0,0 +1,59 @@ -+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -+# -+# When uploading crates to the registry Cargo will automatically -+# "normalize" Cargo.toml files for maximal compatibility -+# with all versions of Cargo and also rewrite `path` dependencies -+# to registry (e.g., crates.io) dependencies. -+# -+# If you are reading this file be aware that the original Cargo.toml -+# will likely look very different (and much more reasonable). -+# See Cargo.toml.orig for the original contents. -+ -+[package] -+edition = "2021" -+rust-version = "1.67.0" -+name = "powerfmt" -+version = "0.2.0" -+authors = ["Jacob Pratt "] -+description = """ -+ `powerfmt` is a library that provides utilities for formatting values. This crate makes it -+ significantly easier to support filling to a minimum width with alignment, avoid heap -+ allocation, and avoid repetitive calculations. -+""" -+readme = "README.md" -+keywords = [ -+ "display", -+ "format", -+ "fmt", -+ "formatter", -+ "extension", -+] -+categories = [ -+ "no-std", -+ "no-std::no-alloc", -+ "rust-patterns", -+] -+license = "MIT OR Apache-2.0" -+repository = "https://github.com/jhpratt/powerfmt" -+ -+[package.metadata.docs.rs] -+all-features = true -+rustdoc-args = [ -+ "--cfg", -+ "__powerfmt_docs", -+ "--generate-link-to-definition", -+] -+targets = ["x86_64-unknown-linux-gnu"] -+ -+[dependencies.powerfmt-macros] -+version = "=0.1.0" -+optional = true -+ -+[features] -+alloc = [] -+default = [ -+ "std", -+ "macros", -+] -+macros = ["dep:powerfmt-macros"] -+std = ["alloc"] -diff --git a/third_party/rust/powerfmt/LICENSE-Apache b/third_party/rust/powerfmt/LICENSE-Apache -new file mode 100644 -index 0000000000..ddde1f9a0f ---- /dev/null -+++ b/third_party/rust/powerfmt/LICENSE-Apache -@@ -0,0 +1,202 @@ -+ -+ Apache License -+ Version 2.0, January 2004 -+ http://www.apache.org/licenses/ -+ -+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -+ -+ 1. Definitions. -+ -+ "License" shall mean the terms and conditions for use, reproduction, -+ and distribution as defined by Sections 1 through 9 of this document. -+ -+ "Licensor" shall mean the copyright owner or entity authorized by -+ the copyright owner that is granting the License. -+ -+ "Legal Entity" shall mean the union of the acting entity and all -+ other entities that control, are controlled by, or are under common -+ control with that entity. For the purposes of this definition, -+ "control" means (i) the power, direct or indirect, to cause the -+ direction or management of such entity, whether by contract or -+ otherwise, or (ii) ownership of fifty percent (50%) or more of the -+ outstanding shares, or (iii) beneficial ownership of such entity. -+ -+ "You" (or "Your") shall mean an individual or Legal Entity -+ exercising permissions granted by this License. -+ -+ "Source" form shall mean the preferred form for making modifications, -+ including but not limited to software source code, documentation -+ source, and configuration files. -+ -+ "Object" form shall mean any form resulting from mechanical -+ transformation or translation of a Source form, including but -+ not limited to compiled object code, generated documentation, -+ and conversions to other media types. -+ -+ "Work" shall mean the work of authorship, whether in Source or -+ Object form, made available under the License, as indicated by a -+ copyright notice that is included in or attached to the work -+ (an example is provided in the Appendix below). -+ -+ "Derivative Works" shall mean any work, whether in Source or Object -+ form, that is based on (or derived from) the Work and for which the -+ editorial revisions, annotations, elaborations, or other modifications -+ represent, as a whole, an original work of authorship. For the purposes -+ of this License, Derivative Works shall not include works that remain -+ separable from, or merely link (or bind by name) to the interfaces of, -+ the Work and Derivative Works thereof. -+ -+ "Contribution" shall mean any work of authorship, including -+ the original version of the Work and any modifications or additions -+ to that Work or Derivative Works thereof, that is intentionally -+ submitted to Licensor for inclusion in the Work by the copyright owner -+ or by an individual or Legal Entity authorized to submit on behalf of -+ the copyright owner. For the purposes of this definition, "submitted" -+ means any form of electronic, verbal, or written communication sent -+ to the Licensor or its representatives, including but not limited to -+ communication on electronic mailing lists, source code control systems, -+ and issue tracking systems that are managed by, or on behalf of, the -+ Licensor for the purpose of discussing and improving the Work, but -+ excluding communication that is conspicuously marked or otherwise -+ designated in writing by the copyright owner as "Not a Contribution." -+ -+ "Contributor" shall mean Licensor and any individual or Legal Entity -+ on behalf of whom a Contribution has been received by Licensor and -+ subsequently incorporated within the Work. -+ -+ 2. Grant of Copyright License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ copyright license to reproduce, prepare Derivative Works of, -+ publicly display, publicly perform, sublicense, and distribute the -+ Work and such Derivative Works in Source or Object form. -+ -+ 3. Grant of Patent License. Subject to the terms and conditions of -+ this License, each Contributor hereby grants to You a perpetual, -+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable -+ (except as stated in this section) patent license to make, have made, -+ use, offer to sell, sell, import, and otherwise transfer the Work, -+ where such license applies only to those patent claims licensable -+ by such Contributor that are necessarily infringed by their -+ Contribution(s) alone or by combination of their Contribution(s) -+ with the Work to which such Contribution(s) was submitted. If You -+ institute patent litigation against any entity (including a -+ cross-claim or counterclaim in a lawsuit) alleging that the Work -+ or a Contribution incorporated within the Work constitutes direct -+ or contributory patent infringement, then any patent licenses -+ granted to You under this License for that Work shall terminate -+ as of the date such litigation is filed. -+ -+ 4. Redistribution. You may reproduce and distribute copies of the -+ Work or Derivative Works thereof in any medium, with or without -+ modifications, and in Source or Object form, provided that You -+ meet the following conditions: -+ -+ (a) You must give any other recipients of the Work or -+ Derivative Works a copy of this License; and -+ -+ (b) You must cause any modified files to carry prominent notices -+ stating that You changed the files; and -+ -+ (c) You must retain, in the Source form of any Derivative Works -+ that You distribute, all copyright, patent, trademark, and -+ attribution notices from the Source form of the Work, -+ excluding those notices that do not pertain to any part of -+ the Derivative Works; and -+ -+ (d) If the Work includes a "NOTICE" text file as part of its -+ distribution, then any Derivative Works that You distribute must -+ include a readable copy of the attribution notices contained -+ within such NOTICE file, excluding those notices that do not -+ pertain to any part of the Derivative Works, in at least one -+ of the following places: within a NOTICE text file distributed -+ as part of the Derivative Works; within the Source form or -+ documentation, if provided along with the Derivative Works; or, -+ within a display generated by the Derivative Works, if and -+ wherever such third-party notices normally appear. The contents -+ of the NOTICE file are for informational purposes only and -+ do not modify the License. You may add Your own attribution -+ notices within Derivative Works that You distribute, alongside -+ or as an addendum to the NOTICE text from the Work, provided -+ that such additional attribution notices cannot be construed -+ as modifying the License. -+ -+ You may add Your own copyright statement to Your modifications and -+ may provide additional or different license terms and conditions -+ for use, reproduction, or distribution of Your modifications, or -+ for any such Derivative Works as a whole, provided Your use, -+ reproduction, and distribution of the Work otherwise complies with -+ the conditions stated in this License. -+ -+ 5. Submission of Contributions. Unless You explicitly state otherwise, -+ any Contribution intentionally submitted for inclusion in the Work -+ by You to the Licensor shall be under the terms and conditions of -+ this License, without any additional terms or conditions. -+ Notwithstanding the above, nothing herein shall supersede or modify -+ the terms of any separate license agreement you may have executed -+ with Licensor regarding such Contributions. -+ -+ 6. Trademarks. This License does not grant permission to use the trade -+ names, trademarks, service marks, or product names of the Licensor, -+ except as required for reasonable and customary use in describing the -+ origin of the Work and reproducing the content of the NOTICE file. -+ -+ 7. Disclaimer of Warranty. Unless required by applicable law or -+ agreed to in writing, Licensor provides the Work (and each -+ Contributor provides its Contributions) on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -+ implied, including, without limitation, any warranties or conditions -+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -+ PARTICULAR PURPOSE. You are solely responsible for determining the -+ appropriateness of using or redistributing the Work and assume any -+ risks associated with Your exercise of permissions under this License. -+ -+ 8. Limitation of Liability. In no event and under no legal theory, -+ whether in tort (including negligence), contract, or otherwise, -+ unless required by applicable law (such as deliberate and grossly -+ negligent acts) or agreed to in writing, shall any Contributor be -+ liable to You for damages, including any direct, indirect, special, -+ incidental, or consequential damages of any character arising as a -+ result of this License or out of the use or inability to use the -+ Work (including but not limited to damages for loss of goodwill, -+ work stoppage, computer failure or malfunction, or any and all -+ other commercial damages or losses), even if such Contributor -+ has been advised of the possibility of such damages. -+ -+ 9. Accepting Warranty or Additional Liability. While redistributing -+ the Work or Derivative Works thereof, You may choose to offer, -+ and charge a fee for, acceptance of support, warranty, indemnity, -+ or other liability obligations and/or rights consistent with this -+ License. However, in accepting such obligations, You may act only -+ on Your own behalf and on Your sole responsibility, not on behalf -+ of any other Contributor, and only if You agree to indemnify, -+ defend, and hold each Contributor harmless for any liability -+ incurred by, or claims asserted against, such Contributor by reason -+ of your accepting any such warranty or additional liability. -+ -+ END OF TERMS AND CONDITIONS -+ -+ APPENDIX: How to apply the Apache License to your work. -+ -+ To apply the Apache License to your work, attach the following -+ boilerplate notice, with the fields enclosed by brackets "[]" -+ replaced with your own identifying information. (Don't include -+ the brackets!) The text should be enclosed in the appropriate -+ comment syntax for the file format. We also recommend that a -+ file or class name and description of purpose be included on the -+ same "printed page" as the copyright notice for easier -+ identification within third-party archives. -+ -+ Copyright 2023 Jacob Pratt et al. -+ -+ Licensed under the Apache License, Version 2.0 (the "License"); -+ you may not use this file except in compliance with the License. -+ You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+ Unless required by applicable law or agreed to in writing, software -+ distributed under the License is distributed on an "AS IS" BASIS, -+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ See the License for the specific language governing permissions and -+ limitations under the License. -diff --git a/third_party/rust/powerfmt/LICENSE-MIT b/third_party/rust/powerfmt/LICENSE-MIT -new file mode 100644 -index 0000000000..89c1f78cb4 ---- /dev/null -+++ b/third_party/rust/powerfmt/LICENSE-MIT -@@ -0,0 +1,19 @@ -+Copyright (c) 2023 Jacob Pratt et al. -+ -+Permission is hereby granted, free of charge, to any person obtaining a copy -+of this software and associated documentation files (the "Software"), to deal -+in the Software without restriction, including without limitation the rights -+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+copies of the Software, and to permit persons to whom the Software is -+furnished to do so, subject to the following conditions: -+ -+The above copyright notice and this permission notice shall be included in all -+copies or substantial portions of the Software. -+ -+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+SOFTWARE. -diff --git a/third_party/rust/powerfmt/README.md b/third_party/rust/powerfmt/README.md -new file mode 100644 -index 0000000000..c22a3e2fe0 ---- /dev/null -+++ b/third_party/rust/powerfmt/README.md -@@ -0,0 +1,45 @@ -+# `powerfmt` -+ -+[![minimum rustc: 1.65](https://img.shields.io/badge/minimum%20rustc-1.65-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com) -+[![version](https://img.shields.io/crates/v/powerfmt?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/powerfmt) -+[![build status](https://img.shields.io/github/actions/workflow/status/jhpratt/powerfmt/build.yaml?branch=main&style=flat-square)](https://github.com/jhpratt/powerfmt/actions) -+ -+Documentation is available [on docs.rs](https://docs.rs/powerfmt). -+ -+## Minimum Rust version policy -+ -+`powerfmt` is guaranteed to compile with the latest stable release of Rust in addition to the two -+prior minor releases. For example, if the latest stable Rust release is 1.70, then `powerfmt` is -+guaranteed to compile with Rust 1.68, 1.69, and 1.70. -+ -+The minimum supported Rust version may be increased to one of the aforementioned versions if doing -+so provides the end user a benefit. However, the minimum supported Rust version may also be bumped -+to a version four minor releases prior to the most recent stable release if doing so improves code -+quality or maintainability. -+ -+For interoperability with third-party crates, it is guaranteed that there exists a version of that -+crate that supports the minimum supported Rust version of `powerfmt`. This does not mean that the -+latest version of the third-party crate supports the minimum supported Rust version of `powerfmt`. -+ -+## Contributing -+ -+Contributions are always welcome! If you have an idea, it's best to float it by me before working on -+it to ensure no effort is wasted. If there's already an open issue for it, knock yourself out. -+ -+If you have any questions, feel free to use [Discussions]. Don't hesitate to ask questions — that's -+what I'm here for! -+ -+[Discussions]: https://github.com/jhpratt/powerfmt/discussions -+ -+## License -+ -+This project is licensed under either of -+ -+- [Apache License, Version 2.0](https://github.com/jhpratt/powerfmt/blob/main/LICENSE-Apache) -+- [MIT license](https://github.com/jhpratt/powerfmt/blob/main/LICENSE-MIT) -+ -+at your option. -+ -+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in -+time by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any -+additional terms or conditions. -diff --git a/third_party/rust/powerfmt/src/buf.rs b/third_party/rust/powerfmt/src/buf.rs -new file mode 100644 -index 0000000000..5a57a60a35 ---- /dev/null -+++ b/third_party/rust/powerfmt/src/buf.rs -@@ -0,0 +1,198 @@ -+//! A buffer for constructing a string while avoiding heap allocation. -+ -+use core::hash::{Hash, Hasher}; -+use core::mem::MaybeUninit; -+use core::{fmt, str}; -+ -+use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay}; -+ -+/// A buffer for construct a string while avoiding heap allocation. -+/// -+/// The only requirement is that the buffer is large enough to hold the formatted string. -+pub struct WriteBuffer { -+ buf: [MaybeUninit; SIZE], -+ len: usize, -+} -+ -+impl fmt::Debug for WriteBuffer { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.debug_struct("DisplayBuffer") -+ .field("buf", &self.as_str()) -+ .field("remaining_capacity", &self.remaining_capacity()) -+ .finish() -+ } -+} -+ -+impl WriteBuffer { -+ /// Creates an empty buffer. -+ pub const fn new() -> Self { -+ Self { -+ buf: maybe_uninit_uninit_array::<_, SIZE>(), -+ len: 0, -+ } -+ } -+ -+ /// Obtain the contents of the buffer as a string. -+ pub fn as_str(&self) -> &str { -+ self -+ } -+ -+ /// Determine how many bytes are remaining in the buffer. -+ pub const fn remaining_capacity(&self) -> usize { -+ SIZE - self.len -+ } -+} -+ -+impl Default for WriteBuffer { -+ fn default() -> Self { -+ Self::new() -+ } -+} -+ -+impl PartialOrd> -+ for WriteBuffer -+{ -+ fn partial_cmp(&self, other: &WriteBuffer) -> Option { -+ self.as_str().partial_cmp(other.as_str()) -+ } -+} -+ -+impl PartialEq> -+ for WriteBuffer -+{ -+ fn eq(&self, other: &WriteBuffer) -> bool { -+ self.as_str() == other.as_str() -+ } -+} -+ -+impl Eq for WriteBuffer {} -+ -+impl Ord for WriteBuffer { -+ fn cmp(&self, other: &Self) -> core::cmp::Ordering { -+ self.as_str().cmp(other.as_str()) -+ } -+} -+ -+impl Hash for WriteBuffer { -+ fn hash(&self, state: &mut H) { -+ self.as_str().hash(state) -+ } -+} -+ -+impl AsRef for WriteBuffer { -+ fn as_ref(&self) -> &str { -+ self -+ } -+} -+ -+impl AsRef<[u8]> for WriteBuffer { -+ fn as_ref(&self) -> &[u8] { -+ self.as_bytes() -+ } -+} -+ -+impl core::borrow::Borrow for WriteBuffer { -+ fn borrow(&self) -> &str { -+ self -+ } -+} -+ -+impl core::ops::Deref for WriteBuffer { -+ type Target = str; -+ -+ fn deref(&self) -> &Self::Target { -+ // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation which -+ // writes a valid UTF-8 string to `buf` and correctly sets `len`. -+ unsafe { -+ let s = maybe_uninit_slice_assume_init_ref(&self.buf[..self.len]); -+ str::from_utf8_unchecked(s) -+ } -+ } -+} -+ -+impl fmt::Display for WriteBuffer { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.write_str(self) -+ } -+} -+ -+impl SmartDisplay for WriteBuffer { -+ type Metadata = (); -+ -+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+ Metadata::new(self.len, self, ()) -+ } -+ -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ f.pad(self) -+ } -+} -+ -+impl fmt::Write for WriteBuffer { -+ fn write_str(&mut self, s: &str) -> fmt::Result { -+ let bytes = s.as_bytes(); -+ -+ if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { -+ maybe_uninit_write_slice(buf, bytes); -+ self.len += bytes.len(); -+ Ok(()) -+ } else { -+ Err(fmt::Error) -+ } -+ } -+} -+ -+/// Equivalent of [`MaybeUninit::uninit_array`] that compiles on stable. -+#[must_use] -+#[inline(always)] -+const fn maybe_uninit_uninit_array() -> [MaybeUninit; N] { -+ // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. -+ unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() } -+} -+ -+/// Equivalent of [`MaybeUninit::write_slice`] that compiles on stable. -+fn maybe_uninit_write_slice<'a, T>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] -+where -+ T: Copy, -+{ -+ #[allow(trivial_casts)] -+ // SAFETY: T and MaybeUninit have the same layout -+ let uninit_src = unsafe { &*(src as *const [T] as *const [MaybeUninit]) }; -+ -+ this.copy_from_slice(uninit_src); -+ -+ // SAFETY: Valid elements have just been copied into `this` so it is initialized -+ unsafe { maybe_uninit_slice_assume_init_mut(this) } -+} -+ -+/// Equivalent of [`MaybeUninit::slice_assume_init_mut`] that compiles on stable. -+/// -+/// # Safety -+/// -+/// See [`MaybeUninit::slice_assume_init_mut`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_mut). -+#[inline(always)] -+unsafe fn maybe_uninit_slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [U] { -+ #[allow(trivial_casts)] -+ // SAFETY: similar to safety notes for `slice_get_ref`, but we have a mutable reference which is -+ // also guaranteed to be valid for writes. -+ unsafe { -+ &mut *(slice as *mut [MaybeUninit] as *mut [U]) -+ } -+} -+ -+/// Equivalent of [`MaybeUninit::slice_assume_init_ref`] that compiles on stable. -+/// -+/// # Safety -+/// -+/// See [`MaybeUninit::slice_assume_init_ref`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref). -+#[inline(always)] -+const unsafe fn maybe_uninit_slice_assume_init_ref(slice: &[MaybeUninit]) -> &[T] { -+ #[allow(trivial_casts)] -+ // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that `slice` is -+ // initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. The pointer -+ // obtained is valid since it refers to memory owned by `slice` which is a reference and thus -+ // guaranteed to be valid for reads. -+ unsafe { -+ &*(slice as *const [MaybeUninit] as *const [T]) -+ } -+} -diff --git a/third_party/rust/powerfmt/src/ext.rs b/third_party/rust/powerfmt/src/ext.rs -new file mode 100644 -index 0000000000..20af7c0aab ---- /dev/null -+++ b/third_party/rust/powerfmt/src/ext.rs -@@ -0,0 +1,54 @@ -+//! Extension traits. -+ -+use core::fmt::{Alignment, Arguments, Formatter, Result, Write}; -+ -+mod sealed { -+ pub trait Sealed {} -+ -+ impl Sealed for core::fmt::Formatter<'_> {} -+} -+ -+/// An extension trait for [`core::fmt::Formatter`]. -+pub trait FormatterExt: sealed::Sealed { -+ /// Writes the given arguments to the formatter, padding them with the given width. If `width` -+ /// is incorrect, the resulting output will not be the requested width. -+ fn pad_with_width(&mut self, width: usize, args: Arguments<'_>) -> Result; -+} -+ -+impl FormatterExt for Formatter<'_> { -+ fn pad_with_width(&mut self, args_width: usize, args: Arguments<'_>) -> Result { -+ let Some(final_width) = self.width() else { -+ // The caller has not requested a width. Write the arguments as-is. -+ return self.write_fmt(args); -+ }; -+ let Some(fill_width @ 1..) = final_width.checked_sub(args_width) else { -+ // No padding will be present. Write the arguments as-is. -+ return self.write_fmt(args); -+ }; -+ -+ let alignment = self.align().unwrap_or(Alignment::Left); -+ let fill = self.fill(); -+ -+ let left_fill_width = match alignment { -+ Alignment::Left => 0, -+ Alignment::Right => fill_width, -+ Alignment::Center => fill_width / 2, -+ }; -+ let right_fill_width = match alignment { -+ Alignment::Left => fill_width, -+ Alignment::Right => 0, -+ // When the fill is not even on both sides, the extra fill goes on the right. -+ Alignment::Center => (fill_width + 1) / 2, -+ }; -+ -+ for _ in 0..left_fill_width { -+ self.write_char(fill)?; -+ } -+ self.write_fmt(args)?; -+ for _ in 0..right_fill_width { -+ self.write_char(fill)?; -+ } -+ -+ Ok(()) -+ } -+} -diff --git a/third_party/rust/powerfmt/src/lib.rs b/third_party/rust/powerfmt/src/lib.rs -new file mode 100644 -index 0000000000..0cd6f7cbb3 ---- /dev/null -+++ b/third_party/rust/powerfmt/src/lib.rs -@@ -0,0 +1,15 @@ -+//! `powerfmt` is a library that provides utilities for formatting values. Specifically, it makes it -+//! significantly easier to support filling to a minimum width with alignment, avoid heap -+//! allocation, and avoid repetitive calculations. -+ -+#![cfg_attr(not(feature = "std"), no_std)] -+#![cfg_attr(__powerfmt_docs, feature(doc_auto_cfg, rustc_attrs))] -+#![cfg_attr(__powerfmt_docs, allow(internal_features))] -+ -+#[cfg(feature = "alloc")] -+extern crate alloc; -+ -+pub mod buf; -+pub mod ext; -+pub mod smart_display; -+mod smart_display_impls; -diff --git a/third_party/rust/powerfmt/src/smart_display.rs b/third_party/rust/powerfmt/src/smart_display.rs -new file mode 100644 -index 0000000000..bb55554b87 ---- /dev/null -+++ b/third_party/rust/powerfmt/src/smart_display.rs -@@ -0,0 +1,695 @@ -+//! Definition of [`SmartDisplay`] and its related items. -+//! -+//! [`SmartDisplay`] is a trait that allows authors to provide additional information to both the -+//! formatter and other users. This information is provided in the form of a metadata type. The only -+//! required piece of metadata is the width of the value. This is _before_ it is passed to the -+//! formatter (i.e. it does not include any padding added by the formatter). Other information -+//! can be stored in a custom metadata type as needed. This information may be made available to -+//! downstream users, but it is not required. -+//! -+//! This module contains the [`SmartDisplay`] and associated items. -+//! -+//! # Example -+//! -+//! ```rust -+//! use std::fmt; -+//! -+//! use powerfmt::ext::FormatterExt as _; -+//! use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; -+//! -+//! #[derive(Debug)] -+//! struct User { -+//! id: usize, -+//! } -+//! -+//! // If you try to use `UserMetadata` in the `SmartDisplay` implementation, you will get a -+//! // compiler error about a private type being used publicly. To avoid this, use this attribute to -+//! // declare a private metadata type. You shouldn't need to worry about how this works, but be -+//! // aware that any public fields or methods remain usable by downstream users. -+//! #[smart_display::private_metadata] -+//! struct UserMetadata { -+//! username: String, -+//! legal_name: String, -+//! } -+//! -+//! // This attribute can be applied to `SmartDisplay` implementations. It will generate an -+//! // implementation of `Display` that delegates to `SmartDisplay`, avoiding the need to write -+//! // boilerplate. -+//! #[smart_display::delegate] -+//! impl SmartDisplay for User { -+//! type Metadata = UserMetadata; -+//! -+//! fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+//! // This could be obtained from a database, for example. -+//! let legal_name = "John Doe".to_owned(); -+//! let username = "jdoe".to_owned(); -+//! -+//! // Note that this must be kept in sync with the implementation of `fmt_with_metadata`. -+//! let width = smart_display::padded_width_of!(username, " (", legal_name, ")",); -+//! -+//! Metadata::new( -+//! width, -+//! self, -+//! UserMetadata { -+//! username, -+//! legal_name, -+//! }, -+//! ) -+//! } -+//! -+//! // Use the now-generated metadata to format the value. Here we use the `pad_with_width` -+//! // method to use the alignment and desired width from the formatter. -+//! fn fmt_with_metadata( -+//! &self, -+//! f: &mut fmt::Formatter<'_>, -+//! metadata: Metadata, -+//! ) -> fmt::Result { -+//! f.pad_with_width( -+//! metadata.unpadded_width(), -+//! format_args!("{} ({})", metadata.username, metadata.legal_name), -+//! ) -+//! } -+//! } -+//! -+//! let user = User { id: 42 }; -+//! assert_eq!(user.to_string(), "jdoe (John Doe)"); -+//! assert_eq!(format!("{user:>20}"), " jdoe (John Doe)"); -+//! ``` -+ -+use core::cmp; -+use core::convert::Infallible; -+use core::fmt::{Alignment, Debug, Display, Formatter, Result}; -+use core::marker::PhantomData; -+use core::mem::MaybeUninit; -+use core::ops::Deref; -+ -+/// Compute the width of multiple items while optionally declaring the options for each item. -+/// -+/// ```rust -+/// # use powerfmt::smart_display; -+/// let alpha = 0; -+/// let beta = 1; -+/// let gamma = 100; -+/// -+/// let width = smart_display::padded_width_of!( -+/// alpha, // use the default options -+/// beta => width(2), // use the specified options -+/// gamma => width(2) sign_plus(true), // use multiple options -+/// ); -+/// assert_eq!(width, 7); -+/// -+/// let formatted = format!("{alpha}{beta:2}{gamma:+2}"); -+/// assert_eq!(formatted.len(), width); -+/// ``` -+/// -+/// Supported options are: -+/// -+/// Option | Method called -+/// --- | --- -+/// `fill(char)` | [`FormatterOptions::with_fill`] -+/// `sign_plus(bool)` | [`FormatterOptions::with_sign_plus`] -+/// `sign_minus(bool)` | [`FormatterOptions::with_sign_minus`] -+/// `align(Alignment)` | [`FormatterOptions::with_align`] -+/// `width(usize)` | [`FormatterOptions::with_width`] -+/// `precision(usize)` | [`FormatterOptions::with_precision`] -+/// `alternate(bool)` | [`FormatterOptions::with_alternate`] -+/// `sign_aware_zero_pad(bool)` | [`FormatterOptions::with_sign_aware_zero_pad`] -+/// -+/// If there are future additions to [`FormatterOptions`], they will be added to this macro as well. -+/// -+/// Options may be provided in any order and will be called in the order they are provided. The -+/// ordering matters if providing both `sign_plus` and `sign_minus`. -+#[cfg(doc)] -+#[doc(hidden)] // Don't show at crate root. -+#[macro_export] -+macro_rules! padded_width_of { -+ ($($t:tt)*) => {}; -+} -+ -+#[cfg(not(doc))] -+#[allow(missing_docs)] // This is done with `#[cfg(doc)]` to avoid showing the various rules. -+#[macro_export] -+macro_rules! __not_public_at_root__padded_width_of { -+ // Base case -+ (@inner [] [$($output:tt)+]) => { $($output)+ }; -+ (@inner [$e:expr $(, $($remaining:tt)*)?] [$($expansion:tt)+]) => { -+ $crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [ -+ $($expansion)+ + $crate::smart_display::Metadata::padded_width_of( -+ &$e, -+ $crate::smart_display::padded_width_of!(@options) -+ ) -+ ]) -+ }; -+ (@inner -+ [$e:expr => $($call:ident($call_expr:expr))+ $(, $($remaining:tt)*)?] -+ [$($expansion:tt)+] -+ ) => { -+ $crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [ -+ $($expansion)+ + $crate::smart_display::Metadata::padded_width_of( -+ &$e, -+ *$crate::smart_display::padded_width_of!(@options $($call($call_expr))+) -+ ) -+ ]) -+ }; -+ -+ // Options base case -+ (@options_inner [] [$($output:tt)+]) => { $($output)+ }; -+ (@options_inner [fill($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_fill($e) -+ ]) -+ }; -+ (@options_inner [sign_plus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_sign_plus($e) -+ ]) -+ }; -+ (@options_inner [sign_minus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_sign_minus($e) -+ ]) -+ }; -+ (@options_inner [align($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_align(Some($e)) -+ ]) -+ }; -+ (@options_inner [width($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_width(Some($e)) -+ ]) -+ }; -+ (@options_inner [precision($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_precision(Some($e)) -+ ]) -+ }; -+ (@options_inner [alternate($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_width($e) -+ ]) -+ }; -+ (@options_inner [sign_aware_zero_pad($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [ -+ $($expansion)*.with_sign_aware_zero_pad($e) -+ ]) -+ }; -+ // Options entry point -+ (@options $($e:tt)*) => { -+ $crate::smart_display::padded_width_of!(@options_inner [$($e)*] [ -+ $crate::smart_display::FormatterOptions::default() -+ ]) -+ }; -+ -+ // Entry point -+ ($($t:tt)*) => { -+ $crate::smart_display::padded_width_of!( -+ @inner [$($t)*] [0] -+ ) -+ }; -+} -+ -+#[cfg(not(doc))] -+pub use __not_public_at_root__padded_width_of as padded_width_of; -+#[cfg(doc)] -+#[doc(inline)] // Show in this module. -+pub use padded_width_of; -+/// Implement [`Display`] for a type by using its implementation of [`SmartDisplay`]. -+/// -+/// This attribute is applied to the `SmartDisplay` implementation. -+/// -+/// ```rust,no_run -+/// # use powerfmt::smart_display::{self, SmartDisplay, Metadata, FormatterOptions}; -+/// # struct Foo; -+/// #[smart_display::delegate] -+/// impl SmartDisplay for Foo { -+/// # type Metadata = (); -+/// # fn metadata(&self, f: FormatterOptions) -> Metadata { -+/// # todo!() -+/// # } -+/// // ... -+/// } -+/// ``` -+#[cfg(feature = "macros")] -+pub use powerfmt_macros::smart_display_delegate as delegate; -+/// Declare a private metadata type for `SmartDisplay`. -+/// -+/// Use this attribute if you want to provide metadata for a type that is not public. Doing -+/// this will avoid a compiler error about a private type being used publicly. Keep in mind -+/// that any public fields, public methods, and trait implementations _will_ be able to be used -+/// by downstream users. -+/// -+/// To avoid accidentally exposing details, such as when all fields are public or if the type -+/// is a unit struct, the type is annotated with `#[non_exhaustive]` automatically. -+/// -+/// ```rust,no_run -+/// # use powerfmt::smart_display; -+/// /// Metadata for `Foo` -+/// #[smart_display::private_metadata] -+/// #[derive(Debug)] -+/// pub(crate) struct FooMetadata { -+/// pub(crate) expensive_to_calculate: usize, -+/// } -+/// ``` -+#[cfg(feature = "macros")] -+pub use powerfmt_macros::smart_display_private_metadata as private_metadata; -+ -+#[derive(Debug)] -+enum FlagBit { -+ SignPlus, -+ SignMinus, -+ Alternate, -+ SignAwareZeroPad, -+ WidthIsInitialized, -+ PrecisionIsInitialized, -+} -+ -+/// Configuration for formatting. -+/// -+/// This struct is obtained from a [`Formatter`]. It provides the same functionality as that of a -+/// reference to a `Formatter`. However, it is not possible to construct a `Formatter`, which is -+/// necessary for some use cases of [`SmartDisplay`]. `FormatterOptions` implements [`Default`] and -+/// has builder methods to alleviate this. -+#[derive(Clone, Copy)] -+pub struct FormatterOptions { -+ flags: u8, -+ fill: char, -+ align: Option, -+ width: MaybeUninit, -+ precision: MaybeUninit, -+} -+ -+impl Debug for FormatterOptions { -+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { -+ f.debug_struct("FormatterOptions") -+ .field("fill", &self.fill) -+ .field("align", &self.align()) -+ .field("width", &self.width()) -+ .field("precision", &self.precision()) -+ .field("sign_plus", &self.sign_plus()) -+ .field("sign_minus", &self.sign_minus()) -+ .field("alternate", &self.alternate()) -+ .field("sign_aware_zero_pad", &self.sign_aware_zero_pad()) -+ .finish() -+ } -+} -+ -+impl Default for FormatterOptions { -+ #[inline] -+ fn default() -> Self { -+ Self { -+ flags: 0, -+ fill: ' ', -+ align: None, -+ width: MaybeUninit::uninit(), -+ precision: MaybeUninit::uninit(), -+ } -+ } -+} -+ -+impl FormatterOptions { -+ /// Sets the fill character to use whenever there is alignment. -+ #[inline] -+ pub fn with_fill(&mut self, c: char) -> &mut Self { -+ self.fill = c; -+ self -+ } -+ -+ /// Set whether the `+` flag is specified. -+ #[inline] -+ pub fn with_sign_plus(&mut self, b: bool) -> &mut Self { -+ if b { -+ self.flags |= 1 << FlagBit::SignPlus as u8; -+ self.flags &= !(1 << FlagBit::SignMinus as u8); -+ } else { -+ self.flags &= !(1 << FlagBit::SignPlus as u8); -+ } -+ self -+ } -+ -+ /// Set whether the `-` flag is specified. -+ #[inline] -+ pub fn with_sign_minus(&mut self, b: bool) -> &mut Self { -+ if b { -+ self.flags |= 1 << FlagBit::SignMinus as u8; -+ self.flags &= !(1 << FlagBit::SignPlus as u8); -+ } else { -+ self.flags &= !(1 << FlagBit::SignMinus as u8); -+ } -+ self -+ } -+ -+ /// Set the flag indicating what form of alignment is requested, if any. -+ #[inline] -+ pub fn with_align(&mut self, align: Option) -> &mut Self { -+ self.align = align; -+ self -+ } -+ -+ /// Set the optional integer width that the output should be. -+ #[inline] -+ pub fn with_width(&mut self, width: Option) -> &mut Self { -+ if let Some(width) = width { -+ self.flags |= 1 << FlagBit::WidthIsInitialized as u8; -+ self.width = MaybeUninit::new(width); -+ } else { -+ self.flags &= !(1 << FlagBit::WidthIsInitialized as u8); -+ } -+ self -+ } -+ -+ /// Set the optional precision for numeric types. Alternatively, the maximum width for string -+ /// types. -+ #[inline] -+ pub fn with_precision(&mut self, precision: Option) -> &mut Self { -+ if let Some(precision) = precision { -+ self.flags |= 1 << FlagBit::PrecisionIsInitialized as u8; -+ self.precision = MaybeUninit::new(precision); -+ } else { -+ self.flags &= !(1 << FlagBit::PrecisionIsInitialized as u8); -+ } -+ self -+ } -+ -+ /// Set whether the `#` flag is specified. -+ #[inline] -+ pub fn with_alternate(&mut self, b: bool) -> &mut Self { -+ if b { -+ self.flags |= 1 << FlagBit::Alternate as u8; -+ } else { -+ self.flags &= !(1 << FlagBit::Alternate as u8); -+ } -+ self -+ } -+ -+ /// Set whether the `0` flag is specified. -+ #[inline] -+ pub fn with_sign_aware_zero_pad(&mut self, b: bool) -> &mut Self { -+ if b { -+ self.flags |= 1 << FlagBit::SignAwareZeroPad as u8; -+ } else { -+ self.flags &= !(1 << FlagBit::SignAwareZeroPad as u8); -+ } -+ self -+ } -+} -+ -+impl FormatterOptions { -+ /// Character used as 'fill' whenever there is alignment. -+ #[inline] -+ #[must_use] -+ pub const fn fill(&self) -> char { -+ self.fill -+ } -+ -+ /// Flag indicating what form of alignment was requested. -+ #[inline] -+ #[must_use] -+ pub const fn align(&self) -> Option { -+ self.align -+ } -+ -+ /// Optionally specified integer width that the output should be. -+ #[inline] -+ #[must_use] -+ pub const fn width(&self) -> Option { -+ if (self.flags >> FlagBit::WidthIsInitialized as u8) & 1 == 1 { -+ // Safety: `width` is initialized if the flag is set. -+ Some(unsafe { self.width.assume_init() }) -+ } else { -+ None -+ } -+ } -+ -+ /// Optionally specified precision for numeric types. Alternatively, the maximum width for -+ /// string types. -+ #[inline] -+ #[must_use] -+ pub const fn precision(&self) -> Option { -+ if (self.flags >> FlagBit::PrecisionIsInitialized as u8) & 1 == 1 { -+ // Safety: `precision` is initialized if the flag is set. -+ Some(unsafe { self.precision.assume_init() }) -+ } else { -+ None -+ } -+ } -+ -+ /// Determines if the `+` flag was specified. -+ #[inline] -+ #[must_use] -+ pub const fn sign_plus(&self) -> bool { -+ (self.flags >> FlagBit::SignPlus as u8) & 1 == 1 -+ } -+ -+ /// Determines if the `-` flag was specified. -+ #[inline] -+ #[must_use] -+ pub const fn sign_minus(&self) -> bool { -+ (self.flags >> FlagBit::SignMinus as u8) & 1 == 1 -+ } -+ -+ /// Determines if the `#` flag was specified. -+ #[inline] -+ #[must_use] -+ pub const fn alternate(&self) -> bool { -+ (self.flags >> FlagBit::Alternate as u8) & 1 == 1 -+ } -+ -+ /// Determines if the `0` flag was specified. -+ #[inline] -+ #[must_use] -+ pub const fn sign_aware_zero_pad(&self) -> bool { -+ (self.flags >> FlagBit::SignAwareZeroPad as u8) & 1 == 1 -+ } -+} -+ -+impl From<&Formatter<'_>> for FormatterOptions { -+ fn from(value: &Formatter<'_>) -> Self { -+ *Self::default() -+ .with_fill(value.fill()) -+ .with_sign_plus(value.sign_plus()) -+ .with_sign_minus(value.sign_minus()) -+ .with_align(value.align()) -+ .with_width(value.width()) -+ .with_precision(value.precision()) -+ .with_alternate(value.alternate()) -+ .with_sign_aware_zero_pad(value.sign_aware_zero_pad()) -+ } -+} -+ -+impl From<&mut Formatter<'_>> for FormatterOptions { -+ #[inline] -+ fn from(value: &mut Formatter<'_>) -> Self { -+ (&*value).into() -+ } -+} -+ -+/// Information used to format a value. This is returned by [`SmartDisplay::metadata`]. -+/// -+/// This type is generic over any user-provided type. This allows the author to store any -+/// information that is needed. For example, a type's implementation of [`SmartDisplay`] may need -+/// to calculate something before knowing its width. This calculation can be performed, with the -+/// result being stored in the custom metadata type. -+/// -+/// Note that `Metadata` _always_ contains the width of the type. Authors do not need to store this -+/// information in their custom metadata type. -+/// -+/// Generally speaking, a type should be able to be formatted using only its metadata, fields, and -+/// the formatter. Any other information should be stored in the metadata type. -+pub struct Metadata<'a, T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ unpadded_width: usize, -+ metadata: T::Metadata, -+ _value: PhantomData<&'a T>, // variance -+} -+ -+// manual impls for bounds -+impl Debug for Metadata<'_, T> -+where -+ T: SmartDisplay, -+ T::Metadata: Debug, -+{ -+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { -+ f.debug_struct("Metadata") -+ .field("unpadded_width", &self.unpadded_width) -+ .field("metadata", &self.metadata) -+ .finish() -+ } -+} -+ -+impl Clone for Metadata<'_, T> -+where -+ T: SmartDisplay, -+ T::Metadata: Clone, -+{ -+ fn clone(&self) -> Self { -+ Self { -+ unpadded_width: self.unpadded_width, -+ metadata: self.metadata.clone(), -+ _value: self._value, -+ } -+ } -+} -+ -+impl Copy for Metadata<'_, T> -+where -+ T: SmartDisplay, -+ T::Metadata: Copy, -+{ -+} -+ -+impl<'a, T> Metadata<'a, T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ /// Creates a new `Metadata` with the given width and metadata. While the width _should_ be -+ /// exact, this is not a requirement for soundness. -+ pub const fn new(unpadded_width: usize, _value: &T, metadata: T::Metadata) -> Self { -+ Self { -+ unpadded_width, -+ metadata, -+ _value: PhantomData, -+ } -+ } -+ -+ /// Reuse the metadata for another type. This is useful when implementing [`SmartDisplay`] for a -+ /// type that wraps another type. Both type's metadata type must be the same. -+ pub fn reuse<'b, U>(self) -> Metadata<'b, U> -+ where -+ 'a: 'b, -+ U: SmartDisplay + ?Sized, -+ { -+ Metadata { -+ unpadded_width: self.unpadded_width, -+ metadata: self.metadata, -+ _value: PhantomData, -+ } -+ } -+ -+ /// Obtain the width of the value before padding. -+ pub const fn unpadded_width(&self) -> usize { -+ self.unpadded_width -+ } -+ -+ /// Obtain the width of the value after padding. -+ pub fn padded_width(&self, f: FormatterOptions) -> usize { -+ match f.width() { -+ Some(requested_width) => cmp::max(self.unpadded_width(), requested_width), -+ None => self.unpadded_width(), -+ } -+ } -+} -+ -+impl Metadata<'_, Infallible> { -+ /// Obtain the width of the value before padding, given the formatter options. -+ pub fn unpadded_width_of(value: T, f: FormatterOptions) -> usize -+ where -+ T: SmartDisplay, -+ { -+ value.metadata(f).unpadded_width -+ } -+ -+ /// Obtain the width of the value after padding, given the formatter options. -+ pub fn padded_width_of(value: T, f: FormatterOptions) -> usize -+ where -+ T: SmartDisplay, -+ { -+ value.metadata(f).padded_width(f) -+ } -+} -+ -+/// Permit using `Metadata` as a smart pointer to the user-provided metadata. -+impl Deref for Metadata<'_, T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Target = T::Metadata; -+ -+ fn deref(&self) -> &T::Metadata { -+ &self.metadata -+ } -+} -+ -+/// Format trait that allows authors to provide additional information. -+/// -+/// This trait is similar to [`Display`], but allows the author to provide additional information -+/// to the formatter. This information is provided in the form of a custom metadata type. -+/// -+/// The only required piece of metadata is the width of the value. This is _before_ it is passed to -+/// the formatter (i.e. it does not include any padding added by the formatter). Other information -+/// can be stored in a custom metadata type as needed. This information may be made available to -+/// downstream users, but it is not required. -+/// -+/// **Note**: While both `fmt_with_metadata` and `fmt` have default implementations, it is strongly -+/// recommended to implement only `fmt_with_metadata`. `fmt` should be implemented if and only if -+/// the type does not require any of the calculated metadata. In that situation, `fmt_with_metadata` -+/// should be omitted. -+#[cfg_attr(__powerfmt_docs, rustc_must_implement_one_of(fmt, fmt_with_metadata))] -+pub trait SmartDisplay: Display { -+ /// User-provided metadata type. -+ type Metadata; -+ -+ /// Compute any information needed to format the value. This must, at a minimum, determine the -+ /// width of the value before any padding is added by the formatter. -+ /// -+ /// If the type uses other types that implement `SmartDisplay` verbatim, the inner types should -+ /// have their metadata calculated and included in the returned value. -+ /// -+ /// # Lifetimes -+ /// -+ /// This method's return type contains a lifetime to `self`. This ensures that the metadata will -+ /// neither outlive the value nor be invalidated by a mutation of the value (barring interior -+ /// mutability). -+ /// -+ /// ```rust,compile_fail -+ /// # use std::fmt; -+ /// # use std::fmt::Write; -+ /// # use powerfmt::buf::WriteBuffer; -+ /// # use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; -+ /// #[derive(Debug)] -+ /// struct WrappedBuffer(WriteBuffer<128>); -+ /// -+ /// #[smart_display::delegate] -+ /// impl SmartDisplay for WrappedBuffer { -+ /// type Metadata = (); -+ /// -+ /// fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+ /// Metadata::new(self.0.len(), self, ()) -+ /// } -+ /// -+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ /// f.pad(self.0.as_str()) -+ /// } -+ /// } -+ /// -+ /// let mut buf = WrappedBuffer(WriteBuffer::new()); -+ /// let metadata = buf.metadata(FormatterOptions::default()); -+ /// // We cannot mutate the buffer while it is borrowed and use its previous metadata on the -+ /// // following line. -+ /// write!(buf.0, "Hello, world!")?; -+ /// assert_eq!(metadata.width(), 13); -+ /// # Ok::<(), Box>(()) -+ /// ``` -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self>; -+ -+ /// Format the value using the given formatter and metadata. The formatted output should have -+ /// the width indicated by the metadata. This is before any padding is added by the -+ /// formatter. -+ /// -+ /// If the metadata is not needed, you should implement the `fmt` method instead. -+ fn fmt_with_metadata(&self, f: &mut Formatter<'_>, _metadata: Metadata<'_, Self>) -> Result { -+ SmartDisplay::fmt(self, f) -+ } -+ -+ /// Format the value using the given formatter. This is the same as [`Display::fmt`]. -+ /// -+ /// The default implementation of this method calls `fmt_with_metadata` with the result of -+ /// `metadata`. Generally speaking, this method should not be implemented. You should implement -+ /// the `fmt_with_metadata` method instead. -+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { -+ let metadata = self.metadata(f.into()); -+ self.fmt_with_metadata(f, metadata) -+ } -+} -diff --git a/third_party/rust/powerfmt/src/smart_display_impls.rs b/third_party/rust/powerfmt/src/smart_display_impls.rs -new file mode 100644 -index 0000000000..dc82395f29 ---- /dev/null -+++ b/third_party/rust/powerfmt/src/smart_display_impls.rs -@@ -0,0 +1,303 @@ -+//! Implementation of [`SmartDisplay`] for various types. -+ -+#[cfg(feature = "alloc")] -+use alloc::borrow::{Cow, ToOwned}; -+#[cfg(feature = "alloc")] -+use alloc::boxed::Box; -+#[cfg(feature = "alloc")] -+use alloc::rc::Rc; -+#[cfg(feature = "alloc")] -+use alloc::string::String; -+#[cfg(feature = "alloc")] -+use alloc::sync::Arc; -+use core::cell::{Ref, RefMut}; -+use core::cmp::min; -+use core::convert::Infallible; -+use core::fmt::{self, Display, Formatter}; -+use core::num::Wrapping; -+use core::pin::Pin; -+ -+use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay}; -+ -+impl SmartDisplay for Infallible { -+ type Metadata = Self; -+ -+ #[inline] -+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+ match *self {} -+ } -+ -+ #[inline] -+ fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result { -+ match *self {} -+ } -+} -+ -+impl SmartDisplay for bool { -+ type Metadata = (); -+ -+ #[inline] -+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+ Metadata::new(if *self { 4 } else { 5 }, self, ()) -+ } -+ -+ #[inline] -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+} -+ -+impl SmartDisplay for str { -+ type Metadata = (); -+ -+ #[inline] -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ Metadata::new( -+ match f.precision() { -+ Some(max_len) => min(self.len(), max_len), -+ None => self.len(), -+ }, -+ self, -+ (), -+ ) -+ } -+ -+ #[inline] -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+} -+ -+#[cfg(feature = "alloc")] -+impl SmartDisplay for String { -+ type Metadata = (); -+ -+ #[inline] -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ #[inline] -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+} -+ -+#[cfg(feature = "alloc")] -+impl<'a, B, O> SmartDisplay for Cow<'a, B> -+where -+ B: SmartDisplay + ToOwned + ?Sized, -+ O: SmartDisplay + 'a, -+{ -+ type Metadata = B::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ match *self { -+ Cow::Borrowed(ref b) => b.metadata(f).reuse(), -+ Cow::Owned(ref o) => o.metadata(f).reuse(), -+ } -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+} -+ -+impl SmartDisplay for Pin<&T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ self.get_ref().metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(self.get_ref(), f) -+ } -+} -+ -+impl SmartDisplay for &T -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(*self, f) -+ } -+} -+ -+impl SmartDisplay for &mut T -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(*self, f) -+ } -+} -+ -+impl SmartDisplay for Ref<'_, T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&**self, f) -+ } -+} -+ -+impl SmartDisplay for RefMut<'_, T> -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&**self, f) -+ } -+} -+ -+impl SmartDisplay for Wrapping -+where -+ T: SmartDisplay, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ self.0.metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&self.0, f) -+ } -+} -+ -+#[cfg(feature = "alloc")] -+impl SmartDisplay for Rc -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&**self, f) -+ } -+} -+ -+#[cfg(feature = "alloc")] -+impl SmartDisplay for Arc -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&**self, f) -+ } -+} -+ -+#[cfg(feature = "alloc")] -+impl SmartDisplay for Box -+where -+ T: SmartDisplay + ?Sized, -+{ -+ type Metadata = T::Metadata; -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ (**self).metadata(f).reuse() -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ SmartDisplay::fmt(&**self, f) -+ } -+} -+ -+/// Implement [`SmartDisplay`] for unsigned integers. -+macro_rules! impl_uint { -+ ($($t:ty)*) => {$( -+ impl SmartDisplay for $t { -+ type Metadata = (); -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ let mut width = self.checked_ilog10().map_or(1, |n| n as usize + 1); -+ if f.sign_plus() || f.sign_minus() { -+ width += 1; -+ } -+ Metadata::new(width, self, ()) -+ } -+ -+ #[inline] -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+ } -+ )*}; -+} -+ -+impl_uint![u8 u16 u32 u64 u128 usize]; -+ -+/// Implement [`SmartDisplay`] for signed integers. -+macro_rules! impl_int { -+ ($($t:ty)*) => {$( -+ impl SmartDisplay for $t { -+ type Metadata = (); -+ -+ fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self> { -+ let mut width = if f.sign_plus() || *self < 0 { 1 } else { 0 }; -+ width += self.unsigned_abs().checked_ilog10().map_or(1, |n| n as usize + 1); -+ Metadata::new(width, self, ()) -+ } -+ -+ #[inline] -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+ } -+ )*}; -+} -+ -+impl_int![i8 i16 i32 i64 i128 isize]; -+ -+impl SmartDisplay for char { -+ type Metadata = (); -+ -+ fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { -+ let mut buf = [0; 4]; -+ let c = self.encode_utf8(&mut buf); -+ -+ Metadata::new(c.len(), self, ()) -+ } -+ -+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { -+ Display::fmt(self, f) -+ } -+} -diff --git a/third_party/rust/time-core/.cargo-checksum.json b/third_party/rust/time-core/.cargo-checksum.json -index 239ee76621..32d6e08d1f 100644 ---- a/third_party/rust/time-core/.cargo-checksum.json -+++ b/third_party/rust/time-core/.cargo-checksum.json -@@ -1 +1 @@ --{"files":{"Cargo.toml":"032c780eaf4ddfde703d5a6b260ad7bad35a5a1ee57a33cacf503f5e47dff6a9","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"59566933f2977d62abbfe39b20be16a85df00db8627211471ccfe182dbbe684c","src/lib.rs":"18020c914b1cd561465e624ef3ea3eef980bd82bc93847e2543bce12da28b043","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"} -\ No newline at end of file -+{"files":{"Cargo.toml":"d90c41d20f37fc3dbc3d88f7715cacafb5aea973030f498e9b2833decdbe63f0","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/convert.rs":"354a1b05e8bb1e92eda5dcdecf33dc6cf2ce72b11115ae4cb0909dcd51d2b294","src/lib.rs":"461b752a45b0f819284e8d8e6b2f49d52b3b661026ab84ee64bf04f4daa0a2d2","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"} -\ No newline at end of file -diff --git a/third_party/rust/time-core/Cargo.toml b/third_party/rust/time-core/Cargo.toml -index bbc8a325e3..3d6555ebd5 100644 ---- a/third_party/rust/time-core/Cargo.toml -+++ b/third_party/rust/time-core/Cargo.toml -@@ -4,29 +4,32 @@ - # "normalize" Cargo.toml files for maximal compatibility - # with all versions of Cargo and also rewrite `path` dependencies - # to registry (e.g., crates.io) dependencies. - # - # If you are reading this file be aware that the original Cargo.toml - # will likely look very different (and much more reasonable). - # See Cargo.toml.orig for the original contents. - - [package] - edition = "2021" --rust-version = "1.65.0" -+rust-version = "1.67.0" - name = "time-core" --version = "0.1.1" -+version = "0.1.2" - authors = [ - "Jacob Pratt ", - "Time contributors", - ] - description = "This crate is an implementation detail and should not be relied upon directly." - keywords = [ - "date", - "time", - "calendar", - "duration", - ] - categories = ["date-and-time"] - license = "MIT OR Apache-2.0" - repository = "https://github.com/time-rs/time" - -+[package.metadata.docs.rs] -+rustdoc-args = ["--generate-link-to-definition"] -+ - [dependencies] -diff --git a/third_party/rust/time-core/src/convert.rs b/third_party/rust/time-core/src/convert.rs -index dae77e9afa..9c28f02638 100644 ---- a/third_party/rust/time-core/src/convert.rs -+++ b/third_party/rust/time-core/src/convert.rs -@@ -1,88 +1,104 @@ --#![allow(clippy::missing_docs_in_private_items)] // TODO temporary -+//! Conversion between units of time. - --macro_rules! declare_structs { -- ($($t:ident)*) => {$( -- #[derive(Debug, Copy, Clone)] -+use self::sealed::Per; -+ -+mod sealed { -+ /// A trait for defining the ratio of two units of time. -+ /// -+ /// This trait is used to implement the `per` method on the various structs. -+ pub trait Per { -+ /// The smallest unsigned integer type that can represent [`VALUE`](Self::VALUE). -+ type Output; -+ -+ /// The number of one unit of time in the other. -+ const VALUE: Self::Output; -+ } -+} -+ -+/// Declare and implement `Per` for all relevant types. Identity implementations are automatic. -+macro_rules! impl_per { -+ ($($t:ident ($str:literal) per {$( -+ $larger:ident : $output:ty = $value:expr -+ )*})*) => {$( -+ #[doc = concat!("A unit of time representing exactly one ", $str, ".")] -+ #[derive(Debug, Clone, Copy)] - pub struct $t; - - impl $t { -- pub const fn per(self, _: T) -> <(Self, T) as Per>::Output -+ #[doc = concat!("Obtain the number of times `", stringify!($t), "` can fit into `T`.")] -+ #[doc = concat!("If `T` is smaller than `", stringify!($t), "`, the code will fail to")] -+ /// compile. The return type is the smallest unsigned integer type that can represent -+ /// the value. -+ /// -+ /// Valid calls: -+ /// -+ #[doc = concat!(" - `", stringify!($t), "::per(", stringify!($t), ")` (returns `u8`)")] -+ $(#[doc = concat!(" - `", stringify!($t), "::per(", stringify!($larger), ")` (returns `", stringify!($output), "`)")])* -+ pub const fn per(_larger: T) -> >::Output - where -- (Self, T): Per, -+ Self: Per, - T: Copy, - { -- <(Self, T)>::VALUE -+ Self::VALUE - } - } -- )*}; --} -- --declare_structs! { -- Nanosecond -- Microsecond -- Millisecond -- Second -- Minute -- Hour -- Day -- Week --} -- --mod sealed { -- pub trait Sealed {} --} - --pub trait Per: sealed::Sealed { -- type Output; -+ impl Per<$t> for $t { -+ type Output = u8; - -- const VALUE: Self::Output; --} -- --macro_rules! impl_per { -- ($($t:ty : $x:ident in $y:ident = $val:expr)*) => {$( -- impl sealed::Sealed for ($x, $y) {} -+ const VALUE: u8 = 1; -+ } - -- impl Per for ($x, $y) { -- type Output = $t; -+ $(impl Per<$larger> for $t { -+ type Output = $output; - -- const VALUE: $t = $val; -- } -+ const VALUE: $output = $value; -+ })* - )*}; - } - - impl_per! { -- u16: Nanosecond in Microsecond = 1_000 -- u32: Nanosecond in Millisecond = 1_000_000 -- u32: Nanosecond in Second = 1_000_000_000 -- u64: Nanosecond in Minute = 60_000_000_000 -- u64: Nanosecond in Hour = 3_600_000_000_000 -- u64: Nanosecond in Day = 86_400_000_000_000 -- u64: Nanosecond in Week = 604_800_000_000_000 -- -- u16: Microsecond in Millisecond = 1_000 -- u32: Microsecond in Second = 1_000_000 -- u32: Microsecond in Minute = 60_000_000 -- u32: Microsecond in Hour = 3_600_000_000 -- u64: Microsecond in Day = 86_400_000_000 -- u64: Microsecond in Week = 604_800_000_000 -- -- u16: Millisecond in Second = 1_000 -- u16: Millisecond in Minute = 60_000 -- u32: Millisecond in Hour = 3_600_000 -- u32: Millisecond in Day = 86_400_000 -- u32: Millisecond in Week = 604_800_000 -- -- u8: Second in Minute = 60 -- u16: Second in Hour = 3_600 -- u32: Second in Day = 86_400 -- u32: Second in Week = 604_800 -- -- u8: Minute in Hour = 60 -- u16: Minute in Day = 1_440 -- u16: Minute in Week = 10_080 -- -- u8: Hour in Day = 24 -- u8: Hour in Week = 168 -- -- u8: Day in Week = 7 -+ Nanosecond ("nanosecond") per { -+ Microsecond: u16 = 1_000 -+ Millisecond: u32 = 1_000_000 -+ Second: u32 = 1_000_000_000 -+ Minute: u64 = 60_000_000_000 -+ Hour: u64 = 3_600_000_000_000 -+ Day: u64 = 86_400_000_000_000 -+ Week: u64 = 604_800_000_000_000 -+ } -+ Microsecond ("microsecond") per { -+ Millisecond: u16 = 1_000 -+ Second: u32 = 1_000_000 -+ Minute: u32 = 60_000_000 -+ Hour: u32 = 3_600_000_000 -+ Day: u64 = 86_400_000_000 -+ Week: u64 = 604_800_000_000 -+ } -+ Millisecond ("millisecond") per { -+ Second: u16 = 1_000 -+ Minute: u16 = 60_000 -+ Hour: u32 = 3_600_000 -+ Day: u32 = 86_400_000 -+ Week: u32 = 604_800_000 -+ } -+ Second ("second") per { -+ Minute: u8 = 60 -+ Hour: u16 = 3_600 -+ Day: u32 = 86_400 -+ Week: u32 = 604_800 -+ } -+ Minute ("minute") per { -+ Hour: u8 = 60 -+ Day: u16 = 1_440 -+ Week: u16 = 10_080 -+ } -+ Hour ("hour") per { -+ Day: u8 = 24 -+ Week: u8 = 168 -+ } -+ Day ("day") per { -+ Week: u8 = 7 -+ } -+ Week ("week") per {} - } -diff --git a/third_party/rust/time-core/src/lib.rs b/third_party/rust/time-core/src/lib.rs -index 4f1c53b12e..41c3547869 100644 ---- a/third_party/rust/time-core/src/lib.rs -+++ b/third_party/rust/time-core/src/lib.rs -@@ -1,52 +1,11 @@ - //! Core items for `time`. - //! - //! This crate is an implementation detail of `time` and should not be relied upon directly. - - #![no_std] --#![deny( -- anonymous_parameters, -- clippy::all, -- clippy::alloc_instead_of_core, -- clippy::explicit_auto_deref, -- clippy::obfuscated_if_else, -- clippy::std_instead_of_core, -- clippy::undocumented_unsafe_blocks, -- illegal_floating_point_literal_pattern, -- late_bound_lifetime_arguments, -- path_statements, -- patterns_in_fns_without_body, -- rust_2018_idioms, -- trivial_casts, -- trivial_numeric_casts, -- unreachable_pub, -- unsafe_op_in_unsafe_fn, -- unused_extern_crates, -- rustdoc::broken_intra_doc_links, -- rustdoc::private_intra_doc_links --)] --#![warn( -- clippy::dbg_macro, -- clippy::decimal_literal_representation, -- clippy::get_unwrap, -- clippy::missing_docs_in_private_items, -- clippy::nursery, -- clippy::print_stdout, -- clippy::todo, -- clippy::unimplemented, -- clippy::unnested_or_patterns, -- clippy::unwrap_in_result, -- clippy::unwrap_used, -- clippy::use_debug, -- deprecated_in_future, -- missing_copy_implementations, -- missing_debug_implementations, -- unused_qualifications, -- variant_size_differences --)] --#![allow(clippy::redundant_pub_crate)] - #![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")] - #![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")] - #![doc(test(attr(deny(warnings))))] - - pub mod convert; - pub mod util; -diff --git a/third_party/rust/time-macros/.cargo-checksum.json b/third_party/rust/time-macros/.cargo-checksum.json -index 6f3847898a..ab81b00599 100644 ---- a/third_party/rust/time-macros/.cargo-checksum.json -+++ b/third_party/rust/time-macros/.cargo-checksum.json -@@ -1 +1 @@ --{"files":{"Cargo.toml":"97dbc36d7e8c8658e151c1cfe57397a116135a0d0efc97aacd339142da5d1c96","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ffcd3d0998ec67abb43a3f8eccc6104172f5061b855312b89d53bb82fece2460","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"8ba87e3249766b89c42b040f623d3134aeec46b78208fdfee825ed0eeeb4591a","src/format_description/format_item.rs":"03ff10699383e5ad08fe690199d45288f13363337abbc811a70b03a8b1703ab1","src/format_description/lexer.rs":"e7db7b6431f00c81b8d15a162088a1622ecd65bfb58d4e642c3c93a8dd5ae4ad","src/format_description/mod.rs":"f48c0ff590bc74529f06a98f60a6af5814bc30d1456bf0b81ac334c0b3f41bba","src/format_description/public/component.rs":"e2c2c8a189e2eb9f9354ff1d9d8edeafa34303e91dc58457df373e7e61c38b78","src/format_description/public/mod.rs":"5260592b310ea9e30808d30c92ea94c7bf1bdb171250a1342279e927d2528d73","src/format_description/public/modifier.rs":"37661e1f7cd9fd11a82f5a1ce6d5971686afa91e6feebc7b9d32df297e8b667f","src/helpers/mod.rs":"a8f8ed59a72b239d7a530357d212873f2e75ea924ec19a6d5d6e24a2baa8100c","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"200678edc14d5920abc0516717b8e010667e58da8bdc65c1cb583fdde0353089","src/offset.rs":"4b9c001a954c1f121a572f5675073f7a4e46d00cc9eb77736bfea2df94ffd05b","src/quote.rs":"634a12b95236e4ab2b8ab70a1a4a2629113c3ce3cf6defefc7ffeb81544c1d89","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"d762e8f22f749d9546d5d2a78b8a0380510be27b4cd3fed375695d7982d8396e","src/to_tokens.rs":"6636ea489c7484bad9b39f72d6956a04c95ce82d8462b12079cc03db778fd263"},"package":"96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"} -\ No newline at end of file -+{"files":{"Cargo.toml":"3330436e81a4de8b20b9a2931f9e857b7974a8423462d928b04cff55ad531cff","LICENSE-Apache":"edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376","LICENSE-MIT":"231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b","src/date.rs":"be197c8a2ed37e8b3123a798a91697b0e61cf9b60e7b1898a0e1b458fe8e3ef1","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b3dea92631092068dd73e57e1cbf548f7ae85762826dcdea7fd6454bf357a50a","src/format_description/ast.rs":"697d5ce506b5386092d706bfe5bf4f81f50e1130796cb17c2fc61457fb165307","src/format_description/format_item.rs":"02d12976209c7af83c2aa4a3221a1a65420fae8c8ba12a28933fb738a2872ff9","src/format_description/lexer.rs":"e2c75f3dda5773a0c8301fdfc0d58a0b833923ba59ac04bcc49fd10aee20496c","src/format_description/mod.rs":"2109b77a8198769c6a6732a54233d7e0058bf4a6da724824103d107859795956","src/format_description/public/component.rs":"5d86912e247724957f7183d70745ced20a7408ed90c24bb47da73a0e26550899","src/format_description/public/mod.rs":"8030e767cb94d559dda2dddc72d42654a756362bd165e5c2cccf112f15d49610","src/format_description/public/modifier.rs":"e1d8fdababcaee2e181a7acb3a938baf309f5a0e2d3877585cf678fcc12f212a","src/helpers/mod.rs":"af47d6c053ffd1113788c5d7591d46fa7d879dc0c5cb2c6c02f9c05462499c4f","src/helpers/string.rs":"3af2d0c701ca978c705922a272e76506dbdf0f376d44ed9ae7283086c67852ba","src/lib.rs":"6ed2d4a41d15a1b5d9fef7d437a1520d967acbfbab98a88630062340f701ca54","src/offset.rs":"aed29d0da9fc65a7dc77314e0346dfdc6fdaf663f17adf9edf00933e8f8e605f","src/quote.rs":"d3003dafa3073825f188851a974846099681cc81145070affb033469cbc7bb31","src/serde_format_description.rs":"db5fb2dc94e01c5114cab3484e68334516d53c4642f31dae0d66f1183253a17c","src/time.rs":"299ddb54e44fb88e514592db5335f06352ebdd0dbf064752790657db85f4c13c","src/to_tokens.rs":"afb067f4d95d19c1b7a650cbcf60ae155b5a9619c89825867997f39ce163ac94"},"package":"3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"} -\ No newline at end of file -diff --git a/third_party/rust/time-macros/Cargo.toml b/third_party/rust/time-macros/Cargo.toml -index 1c86d657d5..b817622154 100644 ---- a/third_party/rust/time-macros/Cargo.toml -+++ b/third_party/rust/time-macros/Cargo.toml -@@ -4,42 +4,132 @@ - # "normalize" Cargo.toml files for maximal compatibility - # with all versions of Cargo and also rewrite `path` dependencies - # to registry (e.g., crates.io) dependencies. - # - # If you are reading this file be aware that the original Cargo.toml - # will likely look very different (and much more reasonable). - # See Cargo.toml.orig for the original contents. - - [package] - edition = "2021" --rust-version = "1.65.0" -+rust-version = "1.67.0" - name = "time-macros" --version = "0.2.10" -+version = "0.2.18" - authors = [ - "Jacob Pratt ", - "Time contributors", - ] - description = """ - Procedural macros for the time crate. - This crate is an implementation detail and should not be relied upon directly. - """ - keywords = [ - "date", - "time", - "calendar", - "duration", - ] - categories = ["date-and-time"] - license = "MIT OR Apache-2.0" - repository = "https://github.com/time-rs/time" - -+[package.metadata.docs.rs] -+rustdoc-args = ["--generate-link-to-definition"] -+ - [lib] - proc-macro = true - -+[dependencies.num-conv] -+version = "0.1.0" -+ - [dependencies.time-core] --version = "=0.1.1" -+version = "=0.1.2" - - [features] - formatting = [] - large-dates = [] - parsing = [] - serde = [] -+ -+[lints.clippy] -+all = "warn" -+alloc-instead-of-core = "deny" -+dbg-macro = "warn" -+decimal-literal-representation = "warn" -+explicit-auto-deref = "warn" -+get-unwrap = "warn" -+manual-let-else = "warn" -+missing-docs-in-private-items = "warn" -+missing-enforced-import-renames = "warn" -+nursery = "warn" -+obfuscated-if-else = "warn" -+print-stdout = "warn" -+semicolon-outside-block = "warn" -+std-instead-of-core = "deny" -+todo = "warn" -+undocumented-unsafe-blocks = "deny" -+unimplemented = "warn" -+uninlined-format-args = "warn" -+unnested-or-patterns = "warn" -+unwrap-in-result = "warn" -+unwrap-used = "warn" -+use-debug = "warn" -+ -+[lints.clippy.option-if-let-else] -+level = "allow" -+priority = 1 -+ -+[lints.clippy.redundant-pub-crate] -+level = "allow" -+priority = 1 -+ -+[lints.rust] -+ambiguous-glob-reexports = "deny" -+clashing-extern-declarations = "deny" -+const-item-mutation = "deny" -+deref-nullptr = "deny" -+drop-bounds = "deny" -+future-incompatible = "deny" -+hidden-glob-reexports = "deny" -+improper-ctypes = "deny" -+improper-ctypes-definitions = "deny" -+invalid-from-utf8 = "deny" -+invalid-macro-export-arguments = "deny" -+invalid-nan-comparisons = "deny" -+invalid-reference-casting = "deny" -+invalid-value = "deny" -+keyword-idents = "warn" -+let-underscore = "warn" -+macro-use-extern-crate = "warn" -+meta-variable-misuse = "warn" -+missing-abi = "warn" -+missing-copy-implementations = "warn" -+missing-debug-implementations = "warn" -+missing-docs = "warn" -+named-arguments-used-positionally = "deny" -+non-ascii-idents = "deny" -+noop-method-call = "warn" -+opaque-hidden-inferred-bound = "deny" -+overlapping-range-endpoints = "deny" -+single-use-lifetimes = "warn" -+suspicious-double-ref-op = "deny" -+temporary-cstring-as-ptr = "deny" -+trivial-casts = "warn" -+trivial-numeric-casts = "warn" -+unconditional-recursion = "deny" -+unnameable-test-items = "deny" -+unreachable-pub = "warn" -+unsafe-op-in-unsafe-fn = "deny" -+unstable-syntax-pre-expansion = "deny" -+unused = "warn" -+unused-import-braces = "warn" -+unused-lifetimes = "warn" -+unused-qualifications = "warn" -+variant-size-differences = "warn" -+ -+[lints.rust.unstable-name-collisions] -+level = "warn" -+priority = 1 -+ -+[lints.rustdoc] -+private-doc-tests = "warn" -+unescaped-backticks = "warn" -diff --git a/third_party/rust/time-macros/LICENSE-Apache b/third_party/rust/time-macros/LICENSE-Apache -index 7646f21e37..c763a0c9de 100644 ---- a/third_party/rust/time-macros/LICENSE-Apache -+++ b/third_party/rust/time-macros/LICENSE-Apache -@@ -180,21 +180,21 @@ - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -- Copyright 2022 Jacob Pratt et al. -+ Copyright 2024 Jacob Pratt et al. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -diff --git a/third_party/rust/time-macros/LICENSE-MIT b/third_party/rust/time-macros/LICENSE-MIT -index a11a755732..5cc097f1c0 100644 ---- a/third_party/rust/time-macros/LICENSE-MIT -+++ b/third_party/rust/time-macros/LICENSE-MIT -@@ -1,11 +1,11 @@ --Copyright (c) 2022 Jacob Pratt et al. -+Copyright (c) 2024 Jacob Pratt et al. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. -diff --git a/third_party/rust/time-macros/src/date.rs b/third_party/rust/time-macros/src/date.rs -index 574ef8ce6f..03cbf11ae2 100644 ---- a/third_party/rust/time-macros/src/date.rs -+++ b/third_party/rust/time-macros/src/date.rs -@@ -1,12 +1,13 @@ - use std::iter::Peekable; - -+use num_conv::Truncate; - use proc_macro::{token_stream, TokenTree}; - use time_core::util::{days_in_year, weeks_in_year}; - - use crate::helpers::{ - consume_any_ident, consume_number, consume_punct, days_in_year_month, ymd_to_yo, ywd_to_yo, - }; - use crate::to_tokens::ToTokenTree; - use crate::Error; - - #[cfg(feature = "large-dates")] -@@ -86,21 +87,21 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result("day", chars)?; - - if month == 0 || month > 12 { - return Err(Error::InvalidComponent { - name: "month", - value: month.to_string(), - span_start: Some(month_span), - span_end: Some(month_span), - }); - } -- let month = month as _; -+ let month = month.truncate(); - if day == 0 || day > days_in_year_month(year, month) { - return Err(Error::InvalidComponent { - name: "day", - value: day.to_string(), - span_start: Some(day_span), - span_end: Some(day_span), - }); - } - - let (year, ordinal) = ymd_to_yo(year, month, day); -@@ -120,18 +121,20 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result TokenTree { - quote_group! {{ -- const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked( -- #(self.year), -- #(self.ordinal), -- ); -+ const DATE: ::time::Date = unsafe { -+ ::time::Date::__from_ordinal_date_unchecked( -+ #(self.year), -+ #(self.ordinal), -+ ) -+ }; - DATE - }} - } - } -diff --git a/third_party/rust/time-macros/src/format_description/ast.rs b/third_party/rust/time-macros/src/format_description/ast.rs -index b75056bc2f..4c3a19e5a6 100644 ---- a/third_party/rust/time-macros/src/format_description/ast.rs -+++ b/third_party/rust/time-macros/src/format_description/ast.rs -@@ -1,11 +1,10 @@ --use std::boxed::Box; - use std::iter; - - use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; - - pub(super) enum Item<'a> { - Literal(Spanned<&'a [u8]>), - EscapedBracket { - _first: Unused, - _second: Unused, - }, -diff --git a/third_party/rust/time-macros/src/format_description/format_item.rs b/third_party/rust/time-macros/src/format_description/format_item.rs -index 6a8cf555ee..ea36caee05 100644 ---- a/third_party/rust/time-macros/src/format_description/format_item.rs -+++ b/third_party/rust/time-macros/src/format_description/format_item.rs -@@ -1,11 +1,10 @@ --use std::boxed::Box; - use std::num::NonZeroU16; - use std::str::{self, FromStr}; - - use super::{ast, unused, Error, Span, Spanned, Unused}; - - pub(super) fn parse<'a>( - ast_items: impl Iterator, Error>>, - ) -> impl Iterator, Error>> { - ast_items.map(|ast_item| ast_item.and_then(Item::from_ast)) - } -@@ -96,28 +95,23 @@ impl From> for crate::format_description::public::OwnedFormatItem { - Item::First { value, _span: _ } => { - Self::First(value.into_vec().into_iter().map(Into::into).collect()) - } - } - } - } - - impl<'a> From]>> for crate::format_description::public::OwnedFormatItem { - fn from(items: Box<[Item<'a>]>) -> Self { - let items = items.into_vec(); -- if items.len() == 1 { -- if let Ok([item]) = <[_; 1]>::try_from(items) { -- item.into() -- } else { -- bug!("the length was just checked to be 1") -- } -- } else { -- Self::Compound(items.into_iter().map(Self::from).collect()) -+ match <[_; 1]>::try_from(items) { -+ Ok([item]) => item.into(), -+ Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()), - } - } - } - - macro_rules! component_definition { - (@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; - (@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; - (@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* }; - (@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? }; - -@@ -136,20 +130,21 @@ macro_rules! component_definition { - $($vis struct $variant { - $($field: Option<$field_type>),* - })* - - $(impl $variant { - fn with_modifiers( - modifiers: &[ast::Modifier<'_>], - _component_span: Span, - ) -> Result - { -+ #[allow(unused_mut)] - let mut this = Self { - $($field: None),* - }; - - for modifier in modifiers { - $(#[allow(clippy::string_lit_as_bytes)] - if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) { - this.$field = component_definition!(@if_from_str $($from_str)? - then { - parse_from_modifier_value::<$field_type>(&modifier.value)? -@@ -205,20 +200,21 @@ macro_rules! component_definition { - Err(name.span.error("invalid component")) - } - } - } - - component_definition! { - pub(super) enum Component { - Day = "day" { - padding = "padding": Option => padding, - }, -+ End = "end" {}, - Hour = "hour" { - padding = "padding": Option => padding, - base = "repr": Option => is_12_hour_clock, - }, - Ignore = "ignore" { - #[required] - count = "count": Option<#[from_str] NonZeroU16> => count, - }, - Minute = "minute" { - padding = "padding": Option => padding, -diff --git a/third_party/rust/time-macros/src/format_description/lexer.rs b/third_party/rust/time-macros/src/format_description/lexer.rs -index 2c927cb94d..2ea53af57a 100644 ---- a/third_party/rust/time-macros/src/format_description/lexer.rs -+++ b/third_party/rust/time-macros/src/format_description/lexer.rs -@@ -1,16 +1,16 @@ - use core::iter; - - use super::{Error, Location, Spanned, SpannedValue}; - - pub(super) struct Lexed { -- iter: core::iter::Peekable, -+ iter: iter::Peekable, - } - - impl Iterator for Lexed { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } - } - -diff --git a/third_party/rust/time-macros/src/format_description/mod.rs b/third_party/rust/time-macros/src/format_description/mod.rs -index fde1272f6a..676028dec4 100644 ---- a/third_party/rust/time-macros/src/format_description/mod.rs -+++ b/third_party/rust/time-macros/src/format_description/mod.rs -@@ -1,40 +1,38 @@ - //! Parser for format descriptions. - --use std::vec::Vec; -- - macro_rules! version { - ($range:expr) => { - $range.contains(&VERSION) - }; - } - - mod ast; - mod format_item; - mod lexer; - mod public; - - pub(crate) fn parse_with_version( - version: Option, - s: &[u8], - proc_span: proc_macro::Span, --) -> Result, crate::Error> { -+) -> Result, crate::Error> { - match version { - Some(crate::FormatDescriptionVersion::V1) | None => parse::<1>(s, proc_span), - Some(crate::FormatDescriptionVersion::V2) => parse::<2>(s, proc_span), - } - } - - fn parse( - s: &[u8], - proc_span: proc_macro::Span, --) -> Result, crate::Error> { -+) -> Result, crate::Error> { - let mut lexed = lexer::lex::(s, proc_span); - let ast = ast::parse::<_, VERSION>(&mut lexed); - let format_items = format_item::parse(ast); - Ok(format_items - .map(|res| res.map(Into::into)) - .collect::>()?) - } - - #[derive(Clone, Copy)] - struct Location { -diff --git a/third_party/rust/time-macros/src/format_description/public/component.rs b/third_party/rust/time-macros/src/format_description/public/component.rs -index 4737c6ce5c..94c73f0fb4 100644 ---- a/third_party/rust/time-macros/src/format_description/public/component.rs -+++ b/third_party/rust/time-macros/src/format_description/public/component.rs -@@ -39,11 +39,12 @@ declare_component! { - Hour - Minute - Period - Second - Subsecond - OffsetHour - OffsetMinute - OffsetSecond - Ignore - UnixTimestamp -+ End - } -diff --git a/third_party/rust/time-macros/src/format_description/public/mod.rs b/third_party/rust/time-macros/src/format_description/public/mod.rs -index ccb0b6e2a3..9fd8b5ece1 100644 ---- a/third_party/rust/time-macros/src/format_description/public/mod.rs -+++ b/third_party/rust/time-macros/src/format_description/public/mod.rs -@@ -12,43 +12,43 @@ pub(crate) enum OwnedFormatItem { - Component(Component), - Compound(Box<[Self]>), - Optional(Box), - First(Box<[Self]>), - } - - impl ToTokenStream for OwnedFormatItem { - fn append_to(self, ts: &mut TokenStream) { - match self { - Self::Literal(bytes) => quote_append! { ts -- ::time::format_description::FormatItem::Literal { -+ ::time::format_description::BorrowedFormatItem::Literal { - 0: #(Literal::byte_string(bytes.as_ref())) - } - }, - Self::Component(component) => quote_append! { ts -- ::time::format_description::FormatItem::Component { 0: #S(component) } -+ ::time::format_description::BorrowedFormatItem::Component { 0: #S(component) } - }, - Self::Compound(items) => { - let items = items - .into_vec() - .into_iter() - .map(|item| quote! { #S(item), }) - .collect::(); - quote_append! { ts -- ::time::format_description::FormatItem::Compound { 0: &[#S(items)] } -+ ::time::format_description::BorrowedFormatItem::Compound { 0: &[#S(items)] } - } - } - Self::Optional(item) => quote_append! {ts -- ::time::format_description::FormatItem::Optional { 0: &#S(*item) } -+ ::time::format_description::BorrowedFormatItem::Optional { 0: &#S(*item) } - }, - Self::First(items) => { - let items = items - .into_vec() - .into_iter() - .map(|item| quote! { #S(item), }) - .collect::(); - quote_append! { ts -- ::time::format_description::FormatItem::First { 0: &[#S(items)] } -+ ::time::format_description::BorrowedFormatItem::First { 0: &[#S(items)] } - } - } - } - } - } -diff --git a/third_party/rust/time-macros/src/format_description/public/modifier.rs b/third_party/rust/time-macros/src/format_description/public/modifier.rs -index e39c6bf552..63bfaa7065 100644 ---- a/third_party/rust/time-macros/src/format_description/public/modifier.rs -+++ b/third_party/rust/time-macros/src/format_description/public/modifier.rs -@@ -3,41 +3,41 @@ use std::num::NonZeroU16; - use proc_macro::{Ident, Span, TokenStream, TokenTree}; - - use crate::to_tokens::{ToTokenStream, ToTokenTree}; - - macro_rules! to_tokens { - ( - $(#[$struct_attr:meta])* - $struct_vis:vis struct $struct_name:ident {$( - $(#[$field_attr:meta])* - $field_vis:vis $field_name:ident : $field_ty:ty -- ),+ $(,)?} -+ ),* $(,)?} - ) => { - $(#[$struct_attr])* - $struct_vis struct $struct_name {$( - $(#[$field_attr])* - $field_vis $field_name: $field_ty -- ),+} -+ ),*} - - impl ToTokenTree for $struct_name { - fn into_token_tree(self) -> TokenTree { - let mut tokens = TokenStream::new(); -- let Self {$($field_name),+} = self; -+ let Self {$($field_name),*} = self; - - quote_append! { tokens - let mut value = ::time::format_description::modifier::$struct_name::default(); - }; - $( - quote_append!(tokens value.$field_name =); - $field_name.append_to(&mut tokens); - quote_append!(tokens ;); -- )+ -+ )* - quote_append!(tokens value); - - proc_macro::TokenTree::Group(proc_macro::Group::new( - proc_macro::Delimiter::Brace, - tokens, - )) - } - } - }; - -@@ -238,10 +238,14 @@ to_tokens! { - Nanosecond, - } - } - - to_tokens! { - pub(crate) struct UnixTimestamp { - pub(crate) precision: UnixTimestampPrecision, - pub(crate) sign_is_mandatory: bool, - } - } -+ -+to_tokens! { -+ pub(crate) struct End {} -+} -diff --git a/third_party/rust/time-macros/src/helpers/mod.rs b/third_party/rust/time-macros/src/helpers/mod.rs -index 56300b3e65..0cca2002de 100644 ---- a/third_party/rust/time-macros/src/helpers/mod.rs -+++ b/third_party/rust/time-macros/src/helpers/mod.rs -@@ -1,16 +1,17 @@ - #[cfg(any(feature = "formatting", feature = "parsing"))] - mod string; - - use std::iter::Peekable; - use std::str::FromStr; - -+use num_conv::prelude::*; - use proc_macro::{token_stream, Span, TokenTree}; - use time_core::util::{days_in_year, is_leap_year}; - - use crate::Error; - - #[cfg(any(feature = "formatting", feature = "parsing"))] - pub(crate) fn get_string_literal( - mut tokens: impl Iterator, - ) -> Result<(Span, Vec), Error> { - match (tokens.next(), tokens.next()) { -@@ -85,43 +86,46 @@ fn jan_weekday(year: i32, ordinal: i32) -> u8 { - let (_quotient, _remainder) = ($a / $b, $a % $b); - if (_remainder > 0 && $b < 0) || (_remainder < 0 && $b > 0) { - _quotient - 1 - } else { - _quotient - } - }}; - } - - let adj_year = year - 1; -- ((ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) -+ (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) - + div_floor!(adj_year, 400) - + 6) -- .rem_euclid(7)) as _ -+ .rem_euclid(7) -+ .cast_unsigned() -+ .truncate() - } - - pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 { -- [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1] -- + (month == 2 && is_leap_year(year)) as u8 -+ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.extend::() - 1] -+ + u8::from(month == 2 && is_leap_year(year)) - } - - pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) { - let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number)) - .overflowing_sub(u16::from(jan_weekday(year, 4)) + 4); - - if overflow || ordinal == 0 { - return (year - 1, (ordinal.wrapping_add(days_in_year(year - 1)))); - } - - let days_in_cur_year = days_in_year(year); - if ordinal > days_in_cur_year { - (year + 1, ordinal - days_in_cur_year) - } else { - (year, ordinal) - } - } - - pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) { -- let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize - 1] -- + (month > 2 && is_leap_year(year)) as u16; -+ let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] -+ [month.extend::() - 1] -+ + u16::from(month > 2 && is_leap_year(year)); - - (year, ordinal + u16::from(day)) - } -diff --git a/third_party/rust/time-macros/src/lib.rs b/third_party/rust/time-macros/src/lib.rs -index 0e8568cdad..65e24d7d3b 100644 ---- a/third_party/rust/time-macros/src/lib.rs -+++ b/third_party/rust/time-macros/src/lib.rs -@@ -1,44 +1,19 @@ --#![deny( -- anonymous_parameters, -- clippy::all, -- illegal_floating_point_literal_pattern, -- late_bound_lifetime_arguments, -- path_statements, -- patterns_in_fns_without_body, -- rust_2018_idioms, -- trivial_casts, -- trivial_numeric_casts, -- unreachable_pub, -- unsafe_code, -- unused_extern_crates --)] --#![warn( -- clippy::dbg_macro, -- clippy::decimal_literal_representation, -- clippy::get_unwrap, -- clippy::nursery, -- clippy::print_stdout, -- clippy::todo, -- clippy::unimplemented, -- clippy::unnested_or_patterns, -- clippy::unwrap_used, -- clippy::use_debug, -- single_use_lifetimes, -- unused_qualifications, -- variant_size_differences --)] - #![allow( -- clippy::missing_const_for_fn, // useless in proc macro -- clippy::redundant_pub_crate, // suggests bad style -- clippy::option_if_let_else, // suggests terrible code -+ clippy::missing_const_for_fn, // irrelevant for proc macros -+ clippy::missing_docs_in_private_items, // TODO remove -+ clippy::std_instead_of_core, // irrelevant for proc macros -+ clippy::std_instead_of_alloc, // irrelevant for proc macros -+ clippy::alloc_instead_of_core, // irrelevant for proc macros -+ missing_docs, // TODO remove - )] -+ - #[allow(unused_macros)] - macro_rules! bug { - () => { compile_error!("provide an error message to help fix a possible bug") }; - ($descr:literal $($rest:tt)?) => { - unreachable!(concat!("internal error: ", $descr) $($rest)?) - } - } - - #[macro_use] - mod quote; -@@ -86,20 +61,21 @@ impl_macros![date datetime offset time]; - - #[cfg(any(feature = "formatting", feature = "parsing"))] - enum FormatDescriptionVersion { - V1, - V2, - } - - #[cfg(any(feature = "formatting", feature = "parsing"))] - enum VersionOrModuleName { - Version(FormatDescriptionVersion), -+ #[cfg_attr(not(feature = "serde"), allow(dead_code))] - ModuleName(Ident), - } - - #[cfg(any(feature = "formatting", feature = "parsing"))] - fn parse_format_description_version( - iter: &mut Peekable, - ) -> Result, Error> { - let version_ident = match iter.peek() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "version" => match iter.next() { - Some(TokenTree::Ident(ident)) => ident, -@@ -168,21 +144,21 @@ pub fn format_description(input: TokenStream) -> TokenStream { - let version = match parse_format_description_version::(&mut input)? { - Some(VersionOrModuleName::Version(version)) => Some(version), - None => None, - // This branch should never occur here, as `false` is the provided as a const parameter. - Some(VersionOrModuleName::ModuleName(_)) => bug!("branch should never occur"), - }; - let (span, string) = helpers::get_string_literal(input)?; - let items = format_description::parse_with_version(version, &string, span)?; - - Ok(quote! {{ -- const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S( -+ const DESCRIPTION: &[::time::format_description::BorrowedFormatItem<'_>] = &[#S( - items - .into_iter() - .map(|item| quote! { #S(item), }) - .collect::() - )]; - DESCRIPTION - }}) - })() - .unwrap_or_else(|err: Error| err.to_compile_error()) - } -@@ -229,21 +205,22 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream { - // they can provide a path to a format description. If the latter, all remaining tokens are - // assumed to be part of the path. - let (format, format_description_display) = match tokens.peek() { - // string literal - Some(TokenTree::Literal(_)) => { - let (span, format_string) = helpers::get_string_literal(tokens)?; - let items = format_description::parse_with_version(version, &format_string, span)?; - let items: TokenStream = - items.into_iter().map(|item| quote! { #S(item), }).collect(); - let items = quote! { -- const ITEMS: &[::time::format_description::FormatItem<'_>] = &[#S(items)]; -+ const ITEMS: &[::time::format_description::BorrowedFormatItem<'_>] -+ = &[#S(items)]; - ITEMS - }; - - (items, String::from_utf8_lossy(&format_string).into_owned()) - } - // path - Some(_) => { - let tokens = tokens.collect::(); - let tokens_string = tokens.to_string(); - ( -diff --git a/third_party/rust/time-macros/src/offset.rs b/third_party/rust/time-macros/src/offset.rs -index 62d7a223da..04dd37f131 100644 ---- a/third_party/rust/time-macros/src/offset.rs -+++ b/third_party/rust/time-macros/src/offset.rs -@@ -1,12 +1,13 @@ - use std::iter::Peekable; - -+use num_conv::prelude::*; - use proc_macro::{token_stream, Span, TokenTree}; - use time_core::convert::*; - - use crate::helpers::{consume_any_ident, consume_number, consume_punct}; - use crate::to_tokens::ToTokenTree; - use crate::Error; - - pub(crate) struct Offset { - pub(crate) hours: i8, - pub(crate) minutes: i8, -@@ -45,52 +46,54 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result("second", chars)?; - seconds_span = sec.0; - seconds = sec.1; - } - } - -- if hours >= 24 { -+ if hours > 25 { - Err(Error::InvalidComponent { - name: "hour", - value: hours.to_string(), - span_start: Some(hours_span), - span_end: Some(hours_span), - }) -- } else if minutes >= Minute.per(Hour) as _ { -+ } else if minutes >= Minute::per(Hour).cast_signed() { - Err(Error::InvalidComponent { - name: "minute", - value: minutes.to_string(), - span_start: Some(minutes_span), - span_end: Some(minutes_span), - }) -- } else if seconds >= Second.per(Minute) as _ { -+ } else if seconds >= Second::per(Minute).cast_signed() { - Err(Error::InvalidComponent { - name: "second", - value: seconds.to_string(), - span_start: Some(seconds_span), - span_end: Some(seconds_span), - }) - } else { - Ok(Offset { - hours: sign * hours, - minutes: sign * minutes, - seconds: sign * seconds, - }) - } - } - - impl ToTokenTree for Offset { - fn into_token_tree(self) -> TokenTree { - quote_group! {{ -- const OFFSET: ::time::UtcOffset = ::time::UtcOffset::__from_hms_unchecked( -- #(self.hours), -- #(self.minutes), -- #(self.seconds), -- ); -+ const OFFSET: ::time::UtcOffset = unsafe { -+ ::time::UtcOffset::__from_hms_unchecked( -+ #(self.hours), -+ #(self.minutes), -+ #(self.seconds), -+ ) -+ }; - OFFSET - }} - } - } -diff --git a/third_party/rust/time-macros/src/quote.rs b/third_party/rust/time-macros/src/quote.rs -index 4d3dcbca03..8603f4fa46 100644 ---- a/third_party/rust/time-macros/src/quote.rs -+++ b/third_party/rust/time-macros/src/quote.rs -@@ -38,34 +38,33 @@ macro_rules! sym { - ]); - }; - ($ts:ident $x:tt) => { - $ts.extend([::proc_macro::TokenTree::from(::proc_macro::Punct::new( - $x, - ::proc_macro::Spacing::Alone, - ))]); - }; - } - -+#[allow(unused_macro_rules)] // Varies by feature flag combination. - macro_rules! quote_inner { - // Base case - ($ts:ident) => {}; - - // Single or double symbols - ($ts:ident :: $($tail:tt)*) => { sym!($ts ':' ':'); quote_inner!($ts $($tail)*); }; -- ($ts:ident .. $($tail:tt)*) => { sym!($ts '.' '.'); quote_inner!($ts $($tail)*); }; - ($ts:ident : $($tail:tt)*) => { sym!($ts ':'); quote_inner!($ts $($tail)*); }; - ($ts:ident = $($tail:tt)*) => { sym!($ts '='); quote_inner!($ts $($tail)*); }; - ($ts:ident ; $($tail:tt)*) => { sym!($ts ';'); quote_inner!($ts $($tail)*); }; - ($ts:ident , $($tail:tt)*) => { sym!($ts ','); quote_inner!($ts $($tail)*); }; - ($ts:ident . $($tail:tt)*) => { sym!($ts '.'); quote_inner!($ts $($tail)*); }; - ($ts:ident & $($tail:tt)*) => { sym!($ts '&'); quote_inner!($ts $($tail)*); }; -- ($ts:ident << $($tail:tt)*) => { sym!($ts '<' '<'); quote_inner!($ts $($tail)*); }; - ($ts:ident < $($tail:tt)*) => { sym!($ts '<'); quote_inner!($ts $($tail)*); }; - ($ts:ident >> $($tail:tt)*) => { sym!($ts '>' '>'); quote_inner!($ts $($tail)*); }; - ($ts:ident > $($tail:tt)*) => { sym!($ts '>'); quote_inner!($ts $($tail)*); }; - ($ts:ident -> $($tail:tt)*) => { sym!($ts '-' '>'); quote_inner!($ts $($tail)*); }; - ($ts:ident ? $($tail:tt)*) => { sym!($ts '?'); quote_inner!($ts $($tail)*); }; - ($ts:ident ! $($tail:tt)*) => { sym!($ts '!'); quote_inner!($ts $($tail)*); }; - ($ts:ident | $($tail:tt)*) => { sym!($ts '|'); quote_inner!($ts $($tail)*); }; - ($ts:ident * $($tail:tt)*) => { sym!($ts '*'); quote_inner!($ts $($tail)*); }; - ($ts:ident + $($tail:tt)*) => { sym!($ts '+'); quote_inner!($ts $($tail)*); }; - -diff --git a/third_party/rust/time-macros/src/time.rs b/third_party/rust/time-macros/src/time.rs -index 96314de1f4..4e565c1ded 100644 ---- a/third_party/rust/time-macros/src/time.rs -+++ b/third_party/rust/time-macros/src/time.rs -@@ -66,54 +66,56 @@ pub(crate) fn parse(chars: &mut Peekable) -> Result