chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/writeable/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"c0272cf34581ac21d4bd90952a1511b7687731c676bf48a723a9457121c51f11","Cargo.lock":"b22bf060432fe0150018dc15e976317b8829bd617c9a7b276c0d4bd2384498ba","Cargo.toml":"2b29ccf1eaa1b62785b75cea013546853c39cbede4a2b6f8b5cd4119e3a86c68","Cargo.toml.orig":"81518a5df3b469489d0f3aa9c11cf581b732a7275c488b7ffe9ac95e2068abfd","LICENSE":"f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2","README.md":"e7e1bb1d88602f2f4f3bc7b74be4915a999854dcf9bbc970cc504049edf6b7c1","benches/writeable.rs":"20efcfad84d83a25e1882c9d6dd5267c66945e556006317dea31348aaded1bb3","examples/writeable_message.rs":"c0749bec1dcd032a36d079d39ca308062ff412d3cf401b8c7b2fc5409c538af9","src/cmp.rs":"a1ffc69c26d110b63eef815e50dbfdbca4a437b62ba3b6057c66ef0bef3deeb7","src/either.rs":"2a0c96eb83725020745fb3f9ff58080529b74f064d8863e546363d61f3a3507d","src/impls.rs":"08e85b0795e28c0774d632699a6ea6e979df181e54c564db388fad028863b573","src/lib.rs":"25d6de7f03b9541fdfc0b064d55fff807d1bb568a8eddffcdc66a0b3a0ac0ed0","src/ops.rs":"3d47d9c81652fafc1baf98c747d8be6b2145b26171a37d41307f84763c47f271","src/parts_write_adapter.rs":"817dba4e631e7a6b3151f47f2636db2d3baca8c7a8413a0c552293f6f775c3db","src/testing.rs":"426acdffce5781d5be0edfa85edfce4b0df958c4ac2cff017fed0eeba97c1ef9","src/to_string_or_borrow.rs":"b6269fec4a6966d520be35ea38eb4c7624ae491d9d257679faf895290b2fe852","src/try_writeable.rs":"0aadafb39bd200214b5c85cc7ce6af0e61558eb4238d94b20777b063c3b241af","tests/data/data.rs":"fe8b57068016004b53bbcae1e0547c41981a5bb074bf01d976f95787227288aa","tests/writeable.rs":"6dc3db45174180bcbf8980e640525b441c31b0b9db238721888d8cc0bd998ded"},"package":"9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"}

6
vendor/writeable/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "29dfe2790b6cfdab94ca6a6b69f58ce54802dbf7"
},
"path_in_vcs": "utils/writeable"
}

653
vendor/writeable/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,653 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
dependencies = [
"anstyle",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "half"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
dependencies = [
"cfg-if",
"crunchy",
]
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "is-terminal"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "oorandom"
version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "plotters"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-svg"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "syn"
version = "2.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "writeable"
version = "0.6.2"
dependencies = [
"criterion",
"either",
"rand",
]
[[package]]
name = "zerocopy"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

79
vendor/writeable/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,79 @@
# 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.82"
name = "writeable"
version = "0.6.2"
authors = ["The ICU4X Project Developers"]
build = false
include = [
"data/**/*",
"src/**/*",
"examples/**/*",
"benches/**/*",
"tests/**/*",
"Cargo.toml",
"LICENSE",
"README.md",
"build.rs",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A more efficient alternative to fmt::Display"
readme = "README.md"
license = "Unicode-3.0"
repository = "https://github.com/unicode-org/icu4x"
[package.metadata.workspaces]
independent = true
[package.metadata.docs.rs]
all-features = true
[features]
alloc = []
default = ["alloc"]
either = ["dep:either"]
[lib]
name = "writeable"
path = "src/lib.rs"
bench = false
[[example]]
name = "writeable_message"
path = "examples/writeable_message.rs"
[[test]]
name = "writeable"
path = "tests/writeable.rs"
[[bench]]
name = "writeable"
path = "benches/writeable.rs"
harness = false
[dependencies.either]
version = "1.9.0"
optional = true
default-features = false
[dev-dependencies.rand]
version = "0.9"
features = ["small_rng"]
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies.criterion]
version = "0.5.0"

46
vendor/writeable/LICENSE vendored Normal file
View File

@@ -0,0 +1,46 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 2020-2024 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE 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 OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
SPDX-License-Identifier: Unicode-3.0
Portions of ICU4X may have been adapted from ICU4C and/or ICU4J.
ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.

67
vendor/writeable/README.md vendored Normal file
View File

@@ -0,0 +1,67 @@
# writeable [![crates.io](https://img.shields.io/crates/v/writeable)](https://crates.io/crates/writeable)
<!-- cargo-rdme start -->
This crate defines [`Writeable`], a trait representing an object that can be written to a
sink implementing `std::fmt::Write`. It is an alternative to `std::fmt::Display` with the
addition of a function indicating the number of bytes to be written.
`Writeable` improves upon `std::fmt::Display` in two ways:
1. More efficient, since the sink can pre-allocate bytes.
2. Smaller code, since the format machinery can be short-circuited.
This crate also exports [`TryWriteable`], a writeable that supports a custom error.
## Benchmarks
The benchmarks to generate the following data can be found in the `benches` directory.
| Case | `Writeable` | `Display` |
|---|---|---|
| Create string from single-string message (139 chars) | 15.642 ns | 19.251 ns |
| Create string from complex message | 35.830 ns | 89.478 ns |
| Write complex message to buffer | 57.336 ns | 64.408 ns |
## Examples
```rust
use std::fmt;
use writeable::assert_writeable_eq;
use writeable::LengthHint;
use writeable::Writeable;
struct WelcomeMessage<'s> {
pub name: &'s str,
}
impl<'s> Writeable for WelcomeMessage<'s> {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_str("Hello, ")?;
sink.write_str(self.name)?;
sink.write_char('!')?;
Ok(())
}
fn writeable_length_hint(&self) -> LengthHint {
// "Hello, " + '!' + length of name
LengthHint::exact(8 + self.name.len())
}
}
let message = WelcomeMessage { name: "Alice" };
assert_writeable_eq!(&message, "Hello, Alice!");
// Types implementing `Writeable` are recommended to also implement `fmt::Display`.
// This can be simply done by redirecting to the `Writeable` implementation:
writeable::impl_display_with_writeable!(WelcomeMessage<'_>);
assert_eq!(message.to_string(), "Hello, Alice!");
```
[`ICU4X`]: ../icu/index.html
<!-- cargo-rdme end -->
## More Information
For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).

315
vendor/writeable/benches/writeable.rs vendored Normal file
View File

@@ -0,0 +1,315 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::fmt;
use writeable::LengthHint;
use writeable::Writeable;
/// A sample type implementing Writeable
struct WriteableMessage<'s> {
message: &'s str,
}
impl Writeable for WriteableMessage<'_> {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_str(self.message)
}
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(self.message.len())
}
}
writeable::impl_display_with_writeable!(WriteableMessage<'_>);
/// A sample type implementing Display
struct DisplayMessage<'s> {
message: &'s str,
}
impl fmt::Display for DisplayMessage<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.message)
}
}
/// A sample type that contains multiple fields
struct ComplexWriteable<'a> {
prefix: &'a str,
n0: usize,
infix: &'a str,
n1: usize,
suffix: &'a str,
}
impl Writeable for ComplexWriteable<'_> {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
self.prefix.write_to(sink)?;
self.n0.write_to(sink)?;
self.infix.write_to(sink)?;
self.n1.write_to(sink)?;
self.suffix.write_to(sink)?;
Ok(())
}
fn writeable_length_hint(&self) -> LengthHint {
self.prefix.writeable_length_hint()
+ self.n0.writeable_length_hint()
+ self.infix.writeable_length_hint()
+ self.n1.writeable_length_hint()
+ self.suffix.writeable_length_hint()
}
}
writeable::impl_display_with_writeable!(ComplexWriteable<'_>);
const SHORT_STR: &str = "short";
const MEDIUM_STR: &str = "this is a medium-length string";
const LONG_STR: &str = "this string is very very very very very very very very very very very very very very very very very very very very very very very very long";
const LONG_OVERLAP_STR: &str =
"this string is very very very very very very very long but different";
fn overview_bench(c: &mut Criterion) {
c.bench_function("writeable/overview", |b| {
b.iter(|| {
// This benchmark runs to_string on short, medium, and long strings in one batch.
WriteableMessage {
message: black_box(SHORT_STR),
}
.write_to_string();
WriteableMessage {
message: black_box(MEDIUM_STR),
}
.write_to_string();
WriteableMessage {
message: black_box(LONG_STR),
}
.write_to_string();
});
});
{
writeable_benches(c);
writeable_dyn_benches(c);
display_benches(c);
complex_benches(c);
}
}
fn writeable_benches(c: &mut Criterion) {
c.bench_function("writeable/to_string/short", |b| {
b.iter(|| {
WriteableMessage {
message: black_box(SHORT_STR),
}
.write_to_string()
.into_owned()
});
});
c.bench_function("writeable/to_string/medium", |b| {
b.iter(|| {
WriteableMessage {
message: black_box(MEDIUM_STR),
}
.write_to_string()
.into_owned()
});
});
c.bench_function("writeable/to_string/long", |b| {
b.iter(|| {
WriteableMessage {
message: black_box(LONG_STR),
}
.write_to_string()
.into_owned()
});
});
c.bench_function("writeable/cmp_str", |b| {
b.iter(|| {
let short = black_box(SHORT_STR);
let medium = black_box(MEDIUM_STR);
let long = black_box(LONG_STR);
let long_overlap = black_box(LONG_OVERLAP_STR);
[short, medium, long, long_overlap].map(|s1| {
[short, medium, long, long_overlap].map(|s2| {
let message = WriteableMessage { message: s1 };
writeable::cmp_str(&message, s2)
})
})
});
});
c.bench_function("writeable/write_to/short", |b| {
b.iter(|| {
let mut buf = String::with_capacity(500);
WriteableMessage {
message: black_box(SHORT_STR),
}
.write_to(&mut buf)
});
});
c.bench_function("writeable/write_to/medium", |b| {
b.iter(|| {
let mut buf = String::with_capacity(500);
WriteableMessage {
message: black_box(MEDIUM_STR),
}
.write_to(&mut buf)
});
});
c.bench_function("writeable/write_to/long", |b| {
b.iter(|| {
let mut buf = String::with_capacity(500);
WriteableMessage {
message: black_box(LONG_STR),
}
.write_to(&mut buf)
});
});
}
fn writeable_dyn_benches(c: &mut Criterion) {
// Same as `write_to_string`, but casts to a `dyn fmt::Write`
fn writeable_dyn_to_string(w: &impl Writeable) -> String {
let mut output = String::with_capacity(w.writeable_length_hint().capacity());
w.write_to(&mut output as &mut dyn fmt::Write)
.expect("impl Write for String is infallible");
output
}
c.bench_function("writeable_dyn/to_string/short", |b| {
b.iter(|| {
writeable_dyn_to_string(&WriteableMessage {
message: black_box(SHORT_STR),
})
});
});
c.bench_function("writeable_dyn/to_string/medium", |b| {
b.iter(|| {
writeable_dyn_to_string(&WriteableMessage {
message: black_box(MEDIUM_STR),
})
});
});
c.bench_function("writeable_dyn/to_string/long", |b| {
b.iter(|| {
writeable_dyn_to_string(&WriteableMessage {
message: black_box(LONG_STR),
})
});
});
}
fn display_benches(c: &mut Criterion) {
c.bench_function("display/to_string/short", |b| {
b.iter(|| {
std::string::ToString::to_string(&DisplayMessage {
message: black_box(SHORT_STR),
})
});
});
c.bench_function("display/to_string/medium", |b| {
b.iter(|| {
std::string::ToString::to_string(&DisplayMessage {
message: black_box(MEDIUM_STR),
})
});
});
c.bench_function("display/to_string/long", |b| {
b.iter(|| {
std::string::ToString::to_string(&DisplayMessage {
message: black_box(LONG_STR),
})
});
});
c.bench_function("display/fmt/short", |b| {
b.iter(|| {
use std::io::Write;
let mut buf = Vec::<u8>::with_capacity(500);
write!(
&mut buf,
"{}",
DisplayMessage {
message: black_box(SHORT_STR),
}
)
});
});
c.bench_function("display/fmt/medium", |b| {
b.iter(|| {
use std::io::Write;
let mut buf = Vec::<u8>::with_capacity(500);
write!(
&mut buf,
"{}",
DisplayMessage {
message: black_box(MEDIUM_STR),
}
)
});
});
c.bench_function("display/fmt/long", |b| {
b.iter(|| {
use std::io::Write;
let mut buf = Vec::<u8>::with_capacity(500);
write!(
&mut buf,
"{}",
DisplayMessage {
message: black_box(LONG_STR),
}
)
});
});
}
fn complex_benches(c: &mut Criterion) {
const COMPLEX_WRITEABLE_MEDIUM: ComplexWriteable = ComplexWriteable {
prefix: "There are ",
n0: 55,
infix: " apples and ",
n1: 8124,
suffix: " oranges",
};
c.bench_function("complex/write_to_string/medium", |b| {
b.iter(|| {
black_box(COMPLEX_WRITEABLE_MEDIUM)
.write_to_string()
.into_owned()
});
});
c.bench_function("complex/display_to_string/medium", |b| {
b.iter(|| std::string::ToString::to_string(&black_box(COMPLEX_WRITEABLE_MEDIUM)));
});
const REFERENCE_STRS: [&str; 6] = [
"There are 55 apples and 8124 oranges",
"There are 55 apples and 0 oranges",
"There are no apples",
SHORT_STR,
MEDIUM_STR,
LONG_STR,
];
c.bench_function("complex/cmp_str", |b| {
b.iter(|| {
black_box(REFERENCE_STRS)
.map(|s| writeable::cmp_str(black_box(&COMPLEX_WRITEABLE_MEDIUM), s))
});
});
c.bench_function("complex/write_to/medium", |b| {
b.iter(|| {
let mut buf = String::with_capacity(500);
black_box(COMPLEX_WRITEABLE_MEDIUM).write_to(&mut buf)
});
});
c.bench_function("complex/fmt/medium", |b| {
b.iter(|| {
use std::io::Write;
let mut buf = Vec::<u8>::with_capacity(500);
write!(&mut buf, "{}", black_box(COMPLEX_WRITEABLE_MEDIUM))
})
});
}
criterion_group!(benches, overview_bench,);
criterion_main!(benches);

View File

@@ -0,0 +1,61 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
// This example illustrates a very simple type implementing Writeable.
#![no_main] // https://github.com/unicode-org/icu4x/issues/395
icu_benchmark_macros::instrument!();
use std::fmt;
use writeable::*;
struct WriteableMessage<W: Writeable>(W);
const GREETING: Part = Part {
category: "meaning",
value: "greeting",
};
const EMOJI: Part = Part {
category: "meaning",
value: "emoji",
};
impl<V: Writeable> Writeable for WriteableMessage<V> {
fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
use fmt::Write;
sink.with_part(GREETING, |g| {
g.write_str("Hello")?;
g.write_str(" ")?;
self.0.write_to(g)
})?;
sink.write_char(' ')?;
sink.with_part(EMOJI, |e| e.write_char('😅'))
}
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(11) + self.0.writeable_length_hint()
}
}
impl<V: Writeable> fmt::Display for WriteableMessage<V> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.write_to(f)
}
}
fn main() {
let (string, parts) =
writeable::_internal::writeable_to_parts_for_test(&WriteableMessage("world"));
assert_eq!(string, "Hello world 😅");
// Print the greeting only
let (start, end, _) = parts
.into_iter()
.find(|(_, _, part)| part == &GREETING)
.unwrap();
println!("{}", &string[start..end]);
}

156
vendor/writeable/src/cmp.rs vendored Normal file
View File

@@ -0,0 +1,156 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::Writeable;
use core::cmp::Ordering;
use core::fmt;
struct WriteComparator<'a> {
code_units: &'a [u8],
result: Ordering,
}
/// This is an infallible impl. Functions always return Ok, not Err.
impl fmt::Write for WriteComparator<'_> {
#[inline]
fn write_str(&mut self, other: &str) -> fmt::Result {
if self.result != Ordering::Equal {
return Ok(());
}
let (this, remainder) = self
.code_units
.split_at_checked(other.len())
.unwrap_or((self.code_units, &[]));
self.code_units = remainder;
self.result = this.cmp(other.as_bytes());
Ok(())
}
}
impl<'a> WriteComparator<'a> {
#[inline]
fn new(code_units: &'a [u8]) -> Self {
Self {
code_units,
result: Ordering::Equal,
}
}
#[inline]
fn finish(self) -> Ordering {
if matches!(self.result, Ordering::Equal) && !self.code_units.is_empty() {
// Self is longer than Other
Ordering::Greater
} else {
self.result
}
}
}
/// Compares the contents of a [`Writeable`] to the given UTF-8 bytes without allocating memory.
///
/// For more details, see: [`cmp_str`]
pub fn cmp_utf8(writeable: &impl Writeable, other: &[u8]) -> Ordering {
let mut wc = WriteComparator::new(other);
let _ = writeable.write_to(&mut wc);
wc.finish().reverse()
}
/// Compares the contents of a `Writeable` to the given bytes
/// without allocating a String to hold the `Writeable` contents.
///
/// This returns a lexicographical comparison, the same as if the Writeable
/// were first converted to a String and then compared with `Ord`. For a
/// string ordering suitable for display to end users, use a localized
/// collation crate, such as `icu_collator`.
///
/// # Examples
///
/// ```
/// use core::cmp::Ordering;
/// use core::fmt;
/// use writeable::Writeable;
///
/// struct WelcomeMessage<'s> {
/// pub name: &'s str,
/// }
///
/// impl<'s> Writeable for WelcomeMessage<'s> {
/// // see impl in Writeable docs
/// # fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
/// # sink.write_str("Hello, ")?;
/// # sink.write_str(self.name)?;
/// # sink.write_char('!')?;
/// # Ok(())
/// # }
/// }
///
/// let message = WelcomeMessage { name: "Alice" };
/// let message_str = message.write_to_string();
///
/// assert_eq!(Ordering::Equal, writeable::cmp_str(&message, "Hello, Alice!"));
///
/// assert_eq!(Ordering::Greater, writeable::cmp_str(&message, "Alice!"));
/// assert_eq!(Ordering::Greater, (*message_str).cmp("Alice!"));
///
/// assert_eq!(Ordering::Less, writeable::cmp_str(&message, "Hello, Bob!"));
/// assert_eq!(Ordering::Less, (*message_str).cmp("Hello, Bob!"));
/// ```
#[inline]
pub fn cmp_str(writeable: &impl Writeable, other: &str) -> Ordering {
cmp_utf8(writeable, other.as_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
use core::fmt::Write;
mod data {
include!("../tests/data/data.rs");
}
#[test]
fn test_write_char() {
for a in data::KEBAB_CASE_STRINGS {
for b in data::KEBAB_CASE_STRINGS {
let mut wc = WriteComparator::new(a.as_bytes());
for ch in b.chars() {
wc.write_char(ch).unwrap();
}
assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}");
}
}
}
#[test]
fn test_write_str() {
for a in data::KEBAB_CASE_STRINGS {
for b in data::KEBAB_CASE_STRINGS {
let mut wc = WriteComparator::new(a.as_bytes());
wc.write_str(b).unwrap();
assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}");
}
}
}
#[test]
fn test_mixed() {
for a in data::KEBAB_CASE_STRINGS {
for b in data::KEBAB_CASE_STRINGS {
let mut wc = WriteComparator::new(a.as_bytes());
let mut first = true;
for substr in b.split('-') {
if first {
first = false;
} else {
wc.write_char('-').unwrap();
}
wc.write_str(substr).unwrap();
}
assert_eq!(a.cmp(b), wc.finish(), "{a} <=> {b}");
}
}
}
}

49
vendor/writeable/src/either.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::*;
use ::either::Either;
/// A [`Writeable`] impl that delegates to one type or another type.
impl<W0, W1> Writeable for Either<W0, W1>
where
W0: Writeable,
W1: Writeable,
{
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
match self {
Either::Left(w) => w.write_to(sink),
Either::Right(w) => w.write_to(sink),
}
}
fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
match self {
Either::Left(w) => w.write_to_parts(sink),
Either::Right(w) => w.write_to_parts(sink),
}
}
fn writeable_length_hint(&self) -> LengthHint {
match self {
Either::Left(w) => w.writeable_length_hint(),
Either::Right(w) => w.writeable_length_hint(),
}
}
fn writeable_borrow(&self) -> Option<&str> {
match self {
Either::Left(w) => w.writeable_borrow(),
Either::Right(w) => w.writeable_borrow(),
}
}
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
match self {
Either::Left(w) => w.write_to_string(),
Either::Right(w) => w.write_to_string(),
}
}
}

272
vendor/writeable/src/impls.rs vendored Normal file
View File

@@ -0,0 +1,272 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::*;
use core::fmt;
macro_rules! impl_write_num {
// random_call exists since usize doesn't have a rand impl. Should always be `random` or empty
($u:ty, $i:ty, $test:ident $(,$random_call:ident)?) => {
impl $crate::Writeable for $u {
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
const MAX_LEN: usize = <$u>::MAX.ilog10() as usize + 1;
let mut buf = [b'0'; MAX_LEN];
let mut n = *self;
let mut i = MAX_LEN;
#[expect(clippy::indexing_slicing)] // n < 10^i
while n != 0 {
i -= 1;
buf[i] = b'0' + (n % 10) as u8;
n /= 10;
}
if i == MAX_LEN {
debug_assert_eq!(*self, 0);
i -= 1;
}
#[expect(clippy::indexing_slicing)] // buf is ASCII
let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
sink.write_str(s)
}
fn writeable_length_hint(&self) -> $crate::LengthHint {
LengthHint::exact(self.checked_ilog10().unwrap_or(0) as usize + 1)
}
}
impl $crate::Writeable for $i {
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
if self.is_negative() {
sink.write_str("-")?;
}
self.unsigned_abs().write_to(sink)
}
fn writeable_length_hint(&self) -> $crate::LengthHint {
$crate::LengthHint::exact(if self.is_negative() { 1 } else { 0 })
+ self.unsigned_abs().writeable_length_hint()
}
}
#[test]
fn $test() {
use $crate::assert_writeable_eq;
assert_writeable_eq!(&(0 as $u), "0");
assert_writeable_eq!(&(0 as $i), "0");
assert_writeable_eq!(&(-0 as $i), "0");
assert_writeable_eq!(&(1 as $u), "1");
assert_writeable_eq!(&(1 as $i), "1");
assert_writeable_eq!(&(-1 as $i), "-1");
assert_writeable_eq!(&(9 as $u), "9");
assert_writeable_eq!(&(9 as $i), "9");
assert_writeable_eq!(&(-9 as $i), "-9");
assert_writeable_eq!(&(10 as $u), "10");
assert_writeable_eq!(&(10 as $i), "10");
assert_writeable_eq!(&(-10 as $i), "-10");
assert_writeable_eq!(&(99 as $u), "99");
assert_writeable_eq!(&(99 as $i), "99");
assert_writeable_eq!(&(-99 as $i), "-99");
assert_writeable_eq!(&(100 as $u), "100");
assert_writeable_eq!(&(-100 as $i), "-100");
assert_writeable_eq!(&<$u>::MAX, <$u>::MAX.to_string());
assert_writeable_eq!(&<$i>::MAX, <$i>::MAX.to_string());
assert_writeable_eq!(&<$i>::MIN, <$i>::MIN.to_string());
$(
use rand::{rngs::SmallRng, Rng, SeedableRng};
let mut rng = SmallRng::seed_from_u64(4); // chosen by fair dice roll.
// guaranteed to be random.
for _ in 0..1000 {
let rand = rng.$random_call::<$u>();
assert_writeable_eq!(rand, rand.to_string());
}
)?
}
};
}
impl_write_num!(u8, i8, test_u8, random);
impl_write_num!(u16, i16, test_u16, random);
impl_write_num!(u32, i32, test_u32, random);
impl_write_num!(u64, i64, test_u64, random);
impl_write_num!(u128, i128, test_u128, random);
impl_write_num!(usize, isize, test_usize);
impl Writeable for str {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_str(self)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(self.len())
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
Some(self)
}
}
#[cfg(feature = "alloc")]
impl Writeable for String {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_str(self)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(self.len())
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
Some(self)
}
}
impl Writeable for char {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_char(*self)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(self.len_utf8())
}
#[inline]
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
let mut s = String::with_capacity(self.len_utf8());
s.push(*self);
Cow::Owned(s)
}
}
impl<T: Writeable + ?Sized> Writeable for &T {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
(*self).write_to(sink)
}
#[inline]
fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
(*self).write_to_parts(sink)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
(*self).writeable_length_hint()
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
(*self).writeable_borrow()
}
#[inline]
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
(*self).write_to_string()
}
}
#[cfg(feature = "alloc")]
macro_rules! impl_write_smart_pointer {
($ty:path, T: $extra_bound:path) => {
impl<'a, T: ?Sized + Writeable + $extra_bound> Writeable for $ty {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
core::borrow::Borrow::<T>::borrow(self).write_to(sink)
}
#[inline]
fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
core::borrow::Borrow::<T>::borrow(self).write_to_parts(sink)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
core::borrow::Borrow::<T>::borrow(self).writeable_length_hint()
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
core::borrow::Borrow::<T>::borrow(self).writeable_borrow()
}
#[inline]
fn write_to_string(&self) -> Cow<'_, str> {
core::borrow::Borrow::<T>::borrow(self).write_to_string()
}
}
};
($ty:path) => {
// Add a harmless duplicate Writeable bound
impl_write_smart_pointer!($ty, T: Writeable);
};
}
#[cfg(feature = "alloc")]
impl_write_smart_pointer!(Cow<'a, T>, T: alloc::borrow::ToOwned);
#[cfg(feature = "alloc")]
impl_write_smart_pointer!(alloc::boxed::Box<T>);
#[cfg(feature = "alloc")]
impl_write_smart_pointer!(alloc::rc::Rc<T>);
#[cfg(feature = "alloc")]
impl_write_smart_pointer!(alloc::sync::Arc<T>);
#[test]
fn test_string_impls() {
fn check_writeable_slice<W: Writeable + core::fmt::Display>(writeables: &[W]) {
assert_writeable_eq!(&writeables[0], "");
assert_writeable_eq!(&writeables[1], "abc");
assert!(matches!(writeables[0].write_to_string(), Cow::Borrowed(_)));
assert!(matches!(writeables[1].write_to_string(), Cow::Borrowed(_)));
}
// test str impl
let arr: &[&str] = &["", "abc"];
check_writeable_slice(arr);
// test String impl
let arr: &[String] = &[String::new(), "abc".to_owned()];
check_writeable_slice(arr);
// test char impl
let chars = ['a', 'β', '你', '😀'];
for i in 0..chars.len() {
let s = String::from(chars[i]);
assert_writeable_eq!(&chars[i], s);
for j in 0..chars.len() {
assert_eq!(
crate::cmp_str(&chars[j], &s),
chars[j].cmp(&chars[i]),
"{:?} vs {:?}",
chars[j],
chars[i]
);
}
}
// test Cow impl
let arr: &[Cow<str>] = &[Cow::Borrowed(""), Cow::Owned("abc".to_string())];
check_writeable_slice(arr);
// test Box impl
let arr: &[Box<str>] = &["".into(), "abc".into()];
check_writeable_slice(arr);
// test Rc impl
let arr: &[alloc::rc::Rc<str>] = &["".into(), "abc".into()];
check_writeable_slice(arr);
// test Arc impl
let arr: &[alloc::sync::Arc<str>] = &["".into(), "abc".into()];
check_writeable_slice(arr);
// test &T impl
let arr: &[&String] = &[&String::new(), &"abc".to_owned()];
check_writeable_slice(arr);
}

509
vendor/writeable/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,509 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
// https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations
#![cfg_attr(not(any(test, doc)), no_std)]
#![cfg_attr(
not(test),
deny(
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::exhaustive_structs,
clippy::exhaustive_enums,
clippy::trivially_copy_pass_by_ref,
missing_debug_implementations,
)
)]
//! This crate defines [`Writeable`], a trait representing an object that can be written to a
//! sink implementing `std::fmt::Write`. It is an alternative to `std::fmt::Display` with the
//! addition of a function indicating the number of bytes to be written.
//!
//! `Writeable` improves upon `std::fmt::Display` in two ways:
//!
//! 1. More efficient, since the sink can pre-allocate bytes.
//! 2. Smaller code, since the format machinery can be short-circuited.
//!
//! This crate also exports [`TryWriteable`], a writeable that supports a custom error.
//!
//! # Benchmarks
//!
//! The benchmarks to generate the following data can be found in the `benches` directory.
//!
//! | Case | `Writeable` | `Display` |
//! |---|---|---|
//! | Create string from single-string message (139 chars) | 15.642 ns | 19.251 ns |
//! | Create string from complex message | 35.830 ns | 89.478 ns |
//! | Write complex message to buffer | 57.336 ns | 64.408 ns |
//!
//! # Examples
//!
//! ```
//! use std::fmt;
//! use writeable::assert_writeable_eq;
//! use writeable::LengthHint;
//! use writeable::Writeable;
//!
//! struct WelcomeMessage<'s> {
//! pub name: &'s str,
//! }
//!
//! impl<'s> Writeable for WelcomeMessage<'s> {
//! fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
//! sink.write_str("Hello, ")?;
//! sink.write_str(self.name)?;
//! sink.write_char('!')?;
//! Ok(())
//! }
//!
//! fn writeable_length_hint(&self) -> LengthHint {
//! // "Hello, " + '!' + length of name
//! LengthHint::exact(8 + self.name.len())
//! }
//! }
//!
//! let message = WelcomeMessage { name: "Alice" };
//! assert_writeable_eq!(&message, "Hello, Alice!");
//!
//! // Types implementing `Writeable` are recommended to also implement `fmt::Display`.
//! // This can be simply done by redirecting to the `Writeable` implementation:
//! writeable::impl_display_with_writeable!(WelcomeMessage<'_>);
//! assert_eq!(message.to_string(), "Hello, Alice!");
//! ```
//!
//! [`ICU4X`]: ../icu/index.html
#[cfg(feature = "alloc")]
extern crate alloc;
mod cmp;
#[cfg(feature = "either")]
mod either;
mod impls;
mod ops;
mod parts_write_adapter;
#[cfg(feature = "alloc")]
mod testing;
#[cfg(feature = "alloc")]
mod to_string_or_borrow;
mod try_writeable;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::string::String;
use core::fmt;
pub use cmp::{cmp_str, cmp_utf8};
#[cfg(feature = "alloc")]
pub use to_string_or_borrow::to_string_or_borrow;
pub use try_writeable::TryWriteable;
/// Helper types for trait impls.
pub mod adapters {
use super::*;
pub use parts_write_adapter::CoreWriteAsPartsWrite;
pub use parts_write_adapter::WithPart;
pub use try_writeable::TryWriteableInfallibleAsWriteable;
pub use try_writeable::WriteableAsTryWriteableInfallible;
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // newtype
pub struct LossyWrap<T>(pub T);
impl<T: TryWriteable> Writeable for LossyWrap<T> {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
let _ = self.0.try_write_to(sink)?;
Ok(())
}
fn writeable_length_hint(&self) -> LengthHint {
self.0.writeable_length_hint()
}
}
impl<T: TryWriteable> fmt::Display for LossyWrap<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let _ = self.0.try_write_to(f)?;
Ok(())
}
}
}
#[doc(hidden)] // for testing and macros
pub mod _internal {
#[cfg(feature = "alloc")]
pub use super::testing::try_writeable_to_parts_for_test;
#[cfg(feature = "alloc")]
pub use super::testing::writeable_to_parts_for_test;
#[cfg(feature = "alloc")]
pub use alloc::string::String;
}
/// A hint to help consumers of `Writeable` pre-allocate bytes before they call
/// [`write_to`](Writeable::write_to).
///
/// This behaves like `Iterator::size_hint`: it is a tuple where the first element is the
/// lower bound, and the second element is the upper bound. If the upper bound is `None`
/// either there is no known upper bound, or the upper bound is larger than `usize`.
///
/// `LengthHint` implements std`::ops::{Add, Mul}` and similar traits for easy composition.
/// During computation, the lower bound will saturate at `usize::MAX`, while the upper
/// bound will become `None` if `usize::MAX` is exceeded.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[non_exhaustive]
pub struct LengthHint(pub usize, pub Option<usize>);
impl LengthHint {
pub fn undefined() -> Self {
Self(0, None)
}
/// `write_to` will use exactly n bytes.
pub fn exact(n: usize) -> Self {
Self(n, Some(n))
}
/// `write_to` will use at least n bytes.
pub fn at_least(n: usize) -> Self {
Self(n, None)
}
/// `write_to` will use at most n bytes.
pub fn at_most(n: usize) -> Self {
Self(0, Some(n))
}
/// `write_to` will use between `n` and `m` bytes.
pub fn between(n: usize, m: usize) -> Self {
Self(Ord::min(n, m), Some(Ord::max(n, m)))
}
/// Returns a recommendation for the number of bytes to pre-allocate.
/// If an upper bound exists, this is used, otherwise the lower bound
/// (which might be 0).
///
/// # Examples
///
/// ```
/// use writeable::Writeable;
///
/// fn pre_allocate_string(w: &impl Writeable) -> String {
/// String::with_capacity(w.writeable_length_hint().capacity())
/// }
/// ```
pub fn capacity(&self) -> usize {
self.1.unwrap_or(self.0)
}
/// Returns whether the `LengthHint` indicates that the string is exactly 0 bytes long.
pub fn is_zero(&self) -> bool {
self.1 == Some(0)
}
}
/// [`Part`]s are used as annotations for formatted strings.
///
/// For example, a string like `Alice, Bob` could assign a `NAME` part to the
/// substrings `Alice` and `Bob`, and a `PUNCTUATION` part to `, `. This allows
/// for example to apply styling only to names.
///
/// `Part` contains two fields, whose usage is left up to the producer of the [`Writeable`].
/// Conventionally, the `category` field will identify the formatting logic that produces
/// the string/parts, whereas the `value` field will have semantic meaning. `NAME` and
/// `PUNCTUATION` could thus be defined as
/// ```
/// # use writeable::Part;
/// const NAME: Part = Part {
/// category: "userlist",
/// value: "name",
/// };
/// const PUNCTUATION: Part = Part {
/// category: "userlist",
/// value: "punctuation",
/// };
/// ```
///
/// That said, consumers should not usually have to inspect `Part` internals. Instead,
/// formatters should expose the `Part`s they produces as constants.
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // stable
pub struct Part {
pub category: &'static str,
pub value: &'static str,
}
impl Part {
/// A part that should annotate error segments in [`TryWriteable`] output.
///
/// For an example, see [`TryWriteable`].
pub const ERROR: Part = Part {
category: "writeable",
value: "error",
};
}
/// A sink that supports annotating parts of the string with [`Part`]s.
pub trait PartsWrite: fmt::Write {
type SubPartsWrite: PartsWrite + ?Sized;
/// Annotates all strings written by the closure with the given [`Part`].
fn with_part(
&mut self,
part: Part,
f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
) -> fmt::Result;
}
/// `Writeable` is an alternative to `std::fmt::Display` with the addition of a length function.
pub trait Writeable {
/// Writes a string to the given sink. Errors from the sink are bubbled up.
/// The default implementation delegates to `write_to_parts`, and discards any
/// `Part` annotations.
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
self.write_to_parts(&mut parts_write_adapter::CoreWriteAsPartsWrite(sink))
}
/// Write bytes and `Part` annotations to the given sink. Errors from the
/// sink are bubbled up. The default implementation delegates to `write_to`,
/// and doesn't produce any `Part` annotations.
fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
self.write_to(sink)
}
/// Returns a hint for the number of UTF-8 bytes that will be written to the sink.
///
/// Override this method if it can be computed quickly.
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::undefined()
}
/// Returns a `&str` that matches the output of `write_to`, if possible.
///
/// This method is used to avoid materializing a [`String`] in `write_to_string`.
fn writeable_borrow(&self) -> Option<&str> {
None
}
/// Creates a new string with the data from this `Writeable`.
///
/// Unlike [`to_string`](ToString::to_string), this does not pull in `core::fmt`
/// code, and borrows the string if possible.
///
/// To remove the `Cow` wrapper, call `.into_owned()` or `.as_str()` as appropriate.
///
/// # Examples
///
/// Inspect a [`Writeable`] before writing it to the sink:
///
/// ```
/// use core::fmt::{Result, Write};
/// use writeable::Writeable;
///
/// fn write_if_ascii<W, S>(w: &W, sink: &mut S) -> Result
/// where
/// W: Writeable + ?Sized,
/// S: Write + ?Sized,
/// {
/// let s = w.write_to_string();
/// if s.is_ascii() {
/// sink.write_str(&s)
/// } else {
/// Ok(())
/// }
/// }
/// ```
///
/// Convert the `Writeable` into a fully owned `String`:
///
/// ```
/// use writeable::Writeable;
///
/// fn make_string(w: &impl Writeable) -> String {
/// w.write_to_string().into_owned()
/// }
/// ```
///
/// # Note to implementors
///
/// This method has a default implementation in terms of `writeable_borrow`,
/// `writeable_length_hint`, and `write_to`. The only case
/// where this should be implemented is if the computation of `writeable_borrow`
/// requires a full invocation of `write_to`. In this case, implement this
/// using [`to_string_or_borrow`].
///
/// # `alloc` Cargo feature
///
/// Calling or implementing this method requires the `alloc` Cargo feature.
/// However, as all the methods required by the default implementation do
/// not require the `alloc` Cargo feature, a caller that uses the feature
/// can still call this on types from crates that don't use the `alloc`
/// Cargo feature.
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
if let Some(borrow) = self.writeable_borrow() {
return Cow::Borrowed(borrow);
}
let hint = self.writeable_length_hint();
if hint.is_zero() {
return Cow::Borrowed("");
}
let mut output = String::with_capacity(hint.capacity());
let _ = self.write_to(&mut output);
Cow::Owned(output)
}
}
/// Implements [`Display`](core::fmt::Display) for types that implement [`Writeable`].
///
/// It's recommended to do this for every [`Writeable`] type, as it will add
/// support for `core::fmt` features like [`fmt!`](std::fmt),
/// [`print!`](std::print), [`write!`](std::write), etc.
///
/// This macro also adds a concrete `to_string` function. This function will shadow the
/// standard library `ToString`, using the more efficient writeable-based code path.
/// To add only `Display`, use the `@display` macro variant.
#[macro_export]
macro_rules! impl_display_with_writeable {
(@display, $type:ty) => {
/// This trait is implemented for compatibility with [`fmt!`](alloc::fmt).
/// To create a string, [`Writeable::write_to_string`] is usually more efficient.
impl core::fmt::Display for $type {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
$crate::Writeable::write_to(&self, f)
}
}
};
($type:ty $(, #[$alloc_feature:meta])? ) => {
$crate::impl_display_with_writeable!(@display, $type);
$(#[$alloc_feature])?
impl $type {
/// Converts the given value to a `String`.
///
/// Under the hood, this uses an efficient [`Writeable`] implementation.
/// However, in order to avoid allocating a string, it is more efficient
/// to use [`Writeable`] directly.
pub fn to_string(&self) -> $crate::_internal::String {
$crate::Writeable::write_to_string(self).into_owned()
}
}
};
}
/// Testing macros for types implementing [`Writeable`].
///
/// Arguments, in order:
///
/// 1. The [`Writeable`] under test
/// 2. The expected string value
/// 3. [`*_parts_eq`] only: a list of parts (`[(start, end, Part)]`)
///
/// Any remaining arguments get passed to `format!`
///
/// The macros tests the following:
///
/// - Equality of string content
/// - Equality of parts ([`*_parts_eq`] only)
/// - Validity of size hint
///
/// # Examples
///
/// ```
/// # use writeable::Writeable;
/// # use writeable::LengthHint;
/// # use writeable::Part;
/// # use writeable::assert_writeable_eq;
/// # use writeable::assert_writeable_parts_eq;
/// # use std::fmt::{self, Write};
///
/// const WORD: Part = Part {
/// category: "foo",
/// value: "word",
/// };
///
/// struct Demo;
/// impl Writeable for Demo {
/// fn write_to_parts<S: writeable::PartsWrite + ?Sized>(
/// &self,
/// sink: &mut S,
/// ) -> fmt::Result {
/// sink.with_part(WORD, |w| w.write_str("foo"))
/// }
/// fn writeable_length_hint(&self) -> LengthHint {
/// LengthHint::exact(3)
/// }
/// }
///
/// writeable::impl_display_with_writeable!(Demo);
///
/// assert_writeable_eq!(&Demo, "foo");
/// assert_writeable_eq!(&Demo, "foo", "Message: {}", "Hello World");
///
/// assert_writeable_parts_eq!(&Demo, "foo", [(0, 3, WORD)]);
/// assert_writeable_parts_eq!(
/// &Demo,
/// "foo",
/// [(0, 3, WORD)],
/// "Message: {}",
/// "Hello World"
/// );
/// ```
///
/// [`*_parts_eq`]: assert_writeable_parts_eq
#[macro_export]
#[cfg(feature = "alloc")]
macro_rules! assert_writeable_eq {
($actual_writeable:expr, $expected_str:expr $(,)?) => {
$crate::assert_writeable_eq!($actual_writeable, $expected_str, "")
};
($actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{
$crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*);
}};
(@internal, $actual_writeable:expr, $expected_str:expr, $($arg:tt)+) => {{
let actual_writeable = &$actual_writeable;
let (actual_str, actual_parts) = $crate::_internal::writeable_to_parts_for_test(actual_writeable);
let actual_len = actual_str.len();
assert_eq!(actual_str, $expected_str, $($arg)*);
let cow = $crate::Writeable::write_to_string(actual_writeable);
assert_eq!(actual_str, cow, $($arg)+);
if let Some(borrowed) = ($crate::Writeable::writeable_borrow(&actual_writeable)) {
assert_eq!(borrowed, $expected_str, $($arg)*);
assert!(matches!(cow, std::borrow::Cow::Borrowed(_)), $($arg)*);
}
let length_hint = $crate::Writeable::writeable_length_hint(actual_writeable);
let lower = length_hint.0;
assert!(
lower <= actual_len,
"hint lower bound {lower} larger than actual length {actual_len}: {}",
format!($($arg)*),
);
if let Some(upper) = length_hint.1 {
assert!(
actual_len <= upper,
"hint upper bound {upper} smaller than actual length {actual_len}: {}",
format!($($arg)*),
);
}
assert_eq!(actual_writeable.to_string(), $expected_str, $($arg)*);
actual_parts // return for assert_writeable_parts_eq
}};
}
/// See [`assert_writeable_eq`].
#[macro_export]
#[cfg(feature = "alloc")]
macro_rules! assert_writeable_parts_eq {
($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => {
$crate::assert_writeable_parts_eq!($actual_writeable, $expected_str, $expected_parts, "")
};
($actual_writeable:expr, $expected_str:expr, $expected_parts:expr, $($arg:tt)+) => {{
let actual_parts = $crate::assert_writeable_eq!(@internal, $actual_writeable, $expected_str, $($arg)*);
assert_eq!(actual_parts, $expected_parts, $($arg)+);
}};
}

294
vendor/writeable/src/ops.rs vendored Normal file
View File

@@ -0,0 +1,294 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::LengthHint;
impl core::ops::Add<LengthHint> for LengthHint {
type Output = Self;
fn add(self, other: LengthHint) -> Self {
LengthHint(
self.0.saturating_add(other.0),
match (self.1, other.1) {
(Some(c), Some(d)) => c.checked_add(d),
_ => None,
},
)
}
}
impl core::ops::AddAssign<LengthHint> for LengthHint {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl core::iter::Sum<LengthHint> for LengthHint {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = LengthHint>,
{
iter.fold(LengthHint::exact(0), core::ops::Add::add)
}
}
impl core::ops::Add<usize> for LengthHint {
type Output = Self;
fn add(self, other: usize) -> Self {
Self(
self.0.saturating_add(other),
self.1.and_then(|upper| upper.checked_add(other)),
)
}
}
impl core::ops::AddAssign<usize> for LengthHint {
fn add_assign(&mut self, other: usize) {
*self = *self + other;
}
}
impl core::ops::Mul<usize> for LengthHint {
type Output = Self;
fn mul(self, other: usize) -> Self {
Self(
self.0.saturating_mul(other),
self.1.and_then(|upper| upper.checked_mul(other)),
)
}
}
impl core::ops::MulAssign<usize> for LengthHint {
fn mul_assign(&mut self, other: usize) {
*self = *self * other;
}
}
impl core::ops::BitOr<LengthHint> for LengthHint {
type Output = Self;
/// Returns a new hint that is correct wherever `self` is correct, and wherever
/// `other` is correct.
///
/// Example:
/// ```
/// # use writeable::{LengthHint, Writeable};
/// # use core::fmt;
/// # fn coin_flip() -> bool { true }
///
/// struct NonDeterministicWriteable(String, String);
///
/// impl Writeable for NonDeterministicWriteable {
/// fn write_to<W: fmt::Write + ?Sized>(
/// &self,
/// sink: &mut W,
/// ) -> fmt::Result {
/// sink.write_str(if coin_flip() { &self.0 } else { &self.1 })
/// }
///
/// fn writeable_length_hint(&self) -> LengthHint {
/// LengthHint::exact(self.0.len()) | LengthHint::exact(self.1.len())
/// }
/// }
///
/// writeable::impl_display_with_writeable!(NonDeterministicWriteable);
/// ```
fn bitor(self, other: LengthHint) -> Self {
LengthHint(
Ord::min(self.0, other.0),
match (self.1, other.1) {
(Some(c), Some(d)) => Some(Ord::max(c, d)),
_ => None,
},
)
}
}
impl core::ops::BitOrAssign<LengthHint> for LengthHint {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}
impl core::iter::Sum<usize> for LengthHint {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = usize>,
{
LengthHint::exact(iter.sum::<usize>())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(LengthHint::exact(3) + 2, LengthHint::exact(5));
assert_eq!(
LengthHint::exact(3) + LengthHint::exact(2),
LengthHint::exact(5)
);
assert_eq!(
LengthHint::exact(3) + LengthHint::undefined(),
LengthHint::at_least(3)
);
assert_eq!(LengthHint::undefined() + 2, LengthHint::at_least(2));
assert_eq!(
LengthHint::undefined() + LengthHint::exact(2),
LengthHint::at_least(2)
);
assert_eq!(
LengthHint::undefined() + LengthHint::undefined(),
LengthHint::undefined()
);
assert_eq!(
LengthHint::at_least(15) + LengthHint::exact(3),
LengthHint::at_least(18)
);
assert_eq!(
LengthHint::at_least(15) + LengthHint::at_most(3),
LengthHint::at_least(15)
);
assert_eq!(LengthHint::between(48, 92) + 5, LengthHint::between(53, 97));
let mut len = LengthHint::exact(5);
len += LengthHint::exact(3);
assert_eq!(len, LengthHint::exact(8));
len += 2;
assert_eq!(len, LengthHint::exact(10));
len += LengthHint::undefined();
assert_eq!(len, LengthHint::at_least(10));
len += LengthHint::exact(3);
assert_eq!(len, LengthHint::at_least(13));
len += 2;
assert_eq!(len, LengthHint::at_least(15));
len += LengthHint::undefined();
assert_eq!(len, LengthHint::at_least(15));
assert_eq!(
LengthHint::between(usize::MAX - 10, usize::MAX - 5) + LengthHint::exact(20),
LengthHint::at_least(usize::MAX)
);
}
#[test]
fn test_sum() {
let lens = [
LengthHint::exact(4),
LengthHint::exact(1),
LengthHint::exact(1),
];
assert_eq!(
lens.iter().copied().sum::<LengthHint>(),
LengthHint::exact(6)
);
let lens = [
LengthHint::exact(4),
LengthHint::undefined(),
LengthHint::at_least(1),
];
assert_eq!(
lens.iter().copied().sum::<LengthHint>(),
LengthHint::at_least(5)
);
let lens = [
LengthHint::exact(4),
LengthHint::undefined(),
LengthHint::at_most(1),
];
assert_eq!(
lens.iter().copied().sum::<LengthHint>(),
LengthHint::at_least(4)
);
let lens = [4, 1, 1];
assert_eq!(
lens.iter().copied().sum::<LengthHint>(),
LengthHint::exact(6)
);
}
#[test]
fn test_mul() {
assert_eq!(LengthHint::exact(3) * 2, LengthHint::exact(6));
assert_eq!(LengthHint::undefined() * 2, LengthHint::undefined());
assert_eq!(
LengthHint::between(48, 92) * 2,
LengthHint::between(96, 184)
);
let mut len = LengthHint::exact(5);
len *= 2;
assert_eq!(len, LengthHint::exact(10));
assert_eq!(
LengthHint::between(usize::MAX - 10, usize::MAX - 5) * 2,
LengthHint::at_least(usize::MAX)
);
}
#[test]
fn test_bitor() {
assert_eq!(
LengthHint::exact(3) | LengthHint::exact(2),
LengthHint::between(2, 3)
);
assert_eq!(
LengthHint::exact(3) | LengthHint::undefined(),
LengthHint::undefined()
);
assert_eq!(
LengthHint::undefined() | LengthHint::undefined(),
LengthHint::undefined()
);
assert_eq!(
LengthHint::exact(10) | LengthHint::exact(10),
LengthHint::exact(10)
);
assert_eq!(
LengthHint::at_least(15) | LengthHint::exact(3),
LengthHint::at_least(3)
);
assert_eq!(
LengthHint::at_least(15) | LengthHint::at_most(18),
LengthHint::undefined()
);
assert_eq!(
LengthHint::at_least(15) | LengthHint::at_least(18),
LengthHint::at_least(15)
);
assert_eq!(
LengthHint::at_most(15) | LengthHint::at_most(18),
LengthHint::at_most(18)
);
assert_eq!(
LengthHint::between(5, 10) | LengthHint::at_most(3),
LengthHint::at_most(10)
);
let mut len = LengthHint::exact(5);
len |= LengthHint::exact(3);
assert_eq!(len, LengthHint::between(5, 3));
}
}

View File

@@ -0,0 +1,120 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::*;
/// A wrapper around a type implementing [`fmt::Write`] that implements [`PartsWrite`].
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // newtype
pub struct CoreWriteAsPartsWrite<W: fmt::Write + ?Sized>(pub W);
impl<W: fmt::Write + ?Sized> fmt::Write for CoreWriteAsPartsWrite<W> {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0.write_str(s)
}
#[inline]
fn write_char(&mut self, c: char) -> fmt::Result {
self.0.write_char(c)
}
}
impl<W: fmt::Write + ?Sized> PartsWrite for CoreWriteAsPartsWrite<W> {
type SubPartsWrite = CoreWriteAsPartsWrite<W>;
#[inline]
fn with_part(
&mut self,
_part: Part,
mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
) -> fmt::Result {
f(self)
}
}
/// A [`Writeable`] that writes out the given part.
///
/// # Examples
///
/// ```
/// use writeable::adapters::WithPart;
/// use writeable::assert_writeable_parts_eq;
/// use writeable::Part;
///
/// // Simple usage:
///
/// const PART: Part = Part {
/// category: "foo",
/// value: "bar",
/// };
///
/// assert_writeable_parts_eq!(
/// WithPart {
/// writeable: "Hello World",
/// part: PART
/// },
/// "Hello World",
/// [(0, 11, PART)],
/// );
///
/// // Can be nested:
///
/// const PART2: Part = Part {
/// category: "foo2",
/// value: "bar2",
/// };
///
/// assert_writeable_parts_eq!(
/// WithPart {
/// writeable: WithPart {
/// writeable: "Hello World",
/// part: PART
/// },
/// part: PART2
/// },
/// "Hello World",
/// [(0, 11, PART), (0, 11, PART2)],
/// );
/// ```
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // public adapter
pub struct WithPart<T: ?Sized> {
pub part: Part,
pub writeable: T,
}
impl<T: Writeable + ?Sized> Writeable for WithPart<T> {
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
self.writeable.write_to(sink)
}
#[inline]
fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.with_part(self.part, |w| self.writeable.write_to_parts(w))
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
self.writeable.writeable_length_hint()
}
fn writeable_borrow(&self) -> Option<&str> {
self.writeable.writeable_borrow()
}
#[inline]
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
self.writeable.write_to_string()
}
}
impl<T: Writeable + ?Sized> fmt::Display for WithPart<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Writeable::write_to(&self, f)
}
}

77
vendor/writeable/src/testing.rs vendored Normal file
View File

@@ -0,0 +1,77 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::*;
use alloc::string::String;
use alloc::vec::Vec;
pub(crate) struct TestWriter {
pub(crate) string: String,
pub(crate) parts: Vec<(usize, usize, Part)>,
}
impl TestWriter {
pub(crate) fn finish(mut self) -> (String, Vec<(usize, usize, Part)>) {
// Sort by first open and last closed
self.parts
.sort_unstable_by_key(|(begin, end, _)| (*begin, end.wrapping_neg()));
(self.string, self.parts)
}
}
impl fmt::Write for TestWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.string.write_str(s)
}
fn write_char(&mut self, c: char) -> fmt::Result {
self.string.write_char(c)
}
}
impl PartsWrite for TestWriter {
type SubPartsWrite = Self;
fn with_part(
&mut self,
part: Part,
mut f: impl FnMut(&mut Self::SubPartsWrite) -> fmt::Result,
) -> fmt::Result {
let start = self.string.len();
f(self)?;
let end = self.string.len();
if start < end {
self.parts.push((start, end, part));
}
Ok(())
}
}
pub fn writeable_to_parts_for_test<W: Writeable>(
writeable: &W,
) -> (String, Vec<(usize, usize, Part)>) {
let mut writer = TestWriter {
string: alloc::string::String::new(),
parts: Vec::new(),
};
#[expect(clippy::expect_used)] // for test code
writeable
.write_to_parts(&mut writer)
.expect("String writer infallible");
writer.finish()
}
#[expect(clippy::type_complexity)]
pub fn try_writeable_to_parts_for_test<W: TryWriteable>(
writeable: &W,
) -> (String, Vec<(usize, usize, Part)>, Option<W::Error>) {
let mut writer = TestWriter {
string: alloc::string::String::new(),
parts: Vec::new(),
};
#[expect(clippy::expect_used)] // for test code
let result = writeable
.try_write_to_parts(&mut writer)
.expect("String writer infallible");
let (actual_str, actual_parts) = writer.finish();
(actual_str, actual_parts, result.err())
}

View File

@@ -0,0 +1,210 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::Writeable;
use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt;
/// Bytes that have been partially validated as UTF-8 up to an offset.
struct PartiallyValidatedUtf8<'a> {
// Safety Invariants:
// 1. The offset is less than or equal to the length of the slice.
// 2. The slice is valid UTF-8 up to the offset.
slice: &'a [u8],
offset: usize,
}
impl<'a> PartiallyValidatedUtf8<'a> {
fn new(slice: &'a [u8]) -> Self {
// Safety: Field invariants maintained here trivially:
// 1. The offset 0 is ≤ all possible lengths of slice
// 2. The slice contains nothing up to the offset zero
Self { slice, offset: 0 }
}
/// Check whether the given string is the next chunk of unvalidated bytes.
/// If so, increment offset and return true. Otherwise, return false.
fn try_push(&mut self, valid_str: &str) -> bool {
let new_offset = self.offset + valid_str.len();
if self.slice.get(self.offset..new_offset) == Some(valid_str.as_bytes()) {
// Safety: Field invariants maintained here:
// 1. In the line above, `self.slice.get()` returned `Some()` for `new_offset` at
// the end of a `Range`, so `new_offset` is ≤ the length of `self.slice`.
// 2. By invariant, we have already validated the string up to `self.offset`, and
// the portion of the slice between `self.offset` and `new_offset` is equal to
// `valid_str`, which is a `&str`, so the string is valid up to `new_offset`.
self.offset = new_offset;
true
} else {
false
}
}
/// Return the validated portion as `&str`.
fn validated_as_str(&self) -> &'a str {
debug_assert!(self.offset <= self.slice.len());
// Safety: self.offset is a valid end index in a range (from field invariant)
let valid_slice = unsafe { self.slice.get_unchecked(..self.offset) };
debug_assert!(core::str::from_utf8(valid_slice).is_ok());
// Safety: the UTF-8 of slice has been validated up to offset (from field invariant)
unsafe { core::str::from_utf8_unchecked(valid_slice) }
}
}
enum SliceOrString<'a> {
Slice(PartiallyValidatedUtf8<'a>),
String(String),
}
/// This is an infallible impl. Functions always return Ok, not Err.
impl fmt::Write for SliceOrString<'_> {
#[inline]
fn write_str(&mut self, other: &str) -> fmt::Result {
match self {
SliceOrString::Slice(slice) => {
if !slice.try_push(other) {
// We failed to match. Convert to owned.
let valid_str = slice.validated_as_str();
let mut owned = String::with_capacity(valid_str.len() + other.len());
owned.push_str(valid_str);
owned.push_str(other);
*self = SliceOrString::String(owned);
}
Ok(())
}
SliceOrString::String(owned) => owned.write_str(other),
}
}
}
impl<'a> SliceOrString<'a> {
#[inline]
fn new(slice: &'a [u8]) -> Self {
Self::Slice(PartiallyValidatedUtf8::new(slice))
}
#[inline]
fn finish(self) -> Cow<'a, str> {
match self {
SliceOrString::Slice(slice) => Cow::Borrowed(slice.validated_as_str()),
SliceOrString::String(owned) => Cow::Owned(owned),
}
}
}
/// Writes the contents of a `Writeable` to a string, returning a reference
/// to a slice if it matches the provided reference bytes, and allocating a
/// String otherwise.
///
/// This function is useful if you have borrowed bytes which you expect
/// to be equal to a writeable a high percentage of the time.
///
/// You can also use this function to make a more efficient implementation of
/// [`Writeable::write_to_string`].
///
/// # Examples
///
/// Basic usage and behavior:
///
/// ```
/// use std::fmt;
/// use std::borrow::Cow;
/// use writeable::Writeable;
///
/// struct WelcomeMessage<'s> {
/// pub name: &'s str,
/// }
///
/// impl<'s> Writeable for WelcomeMessage<'s> {
/// // see impl in Writeable docs
/// # fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
/// # sink.write_str("Hello, ")?;
/// # sink.write_str(self.name)?;
/// # sink.write_char('!')?;
/// # Ok(())
/// # }
/// }
///
/// let message = WelcomeMessage { name: "Alice" };
///
/// assert!(matches!(
/// writeable::to_string_or_borrow(&message, b""),
/// Cow::Owned(s) if s == "Hello, Alice!"
/// ));
/// assert!(matches!(
/// writeable::to_string_or_borrow(&message, b"Hello"),
/// Cow::Owned(s) if s == "Hello, Alice!"
/// ));
/// assert!(matches!(
/// writeable::to_string_or_borrow(&message, b"Hello, Bob!"),
/// Cow::Owned(s) if s == "Hello, Alice!"
/// ));
/// assert!(matches!(
/// writeable::to_string_or_borrow(&message, b"Hello, Alice!"),
/// Cow::Borrowed("Hello, Alice!")
/// ));
///
/// // Borrowing can use a prefix:
/// assert!(matches!(
/// writeable::to_string_or_borrow(&message, b"Hello, Alice!..\xFF\x00\xFF"),
/// Cow::Borrowed("Hello, Alice!")
/// ));
/// ```
///
/// Example use case: a function that transforms a string to lowercase.
/// We are also able to write a more efficient implementation of
/// [`Writeable::write_to_string`] in this situation.
///
/// ```
/// use std::fmt;
/// use std::borrow::Cow;
/// use writeable::Writeable;
///
/// struct MakeAsciiLower<'a>(&'a str);
///
/// impl<'a> Writeable for MakeAsciiLower<'a> {
/// fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
/// for c in self.0.chars() {
/// sink.write_char(c.to_ascii_lowercase())?;
/// }
/// Ok(())
/// }
/// #[inline]
/// fn writeable_borrow(&self) -> Option<&str> {
/// self.0.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_whitespace()).then_some(self.0)
/// }
/// }
///
/// fn make_lowercase(input: &str) -> Cow<'_, str> {
/// let writeable = MakeAsciiLower(input);
/// writeable::to_string_or_borrow(&writeable, input.as_bytes())
/// }
///
/// assert!(matches!(
/// make_lowercase("this is lowercase"),
/// Cow::Borrowed("this is lowercase")
/// ));
/// assert!(matches!(
/// make_lowercase("this is UPPERCASE"),
/// Cow::Owned(s) if s == "this is uppercase"
/// ));
///
/// assert!(matches!(
/// MakeAsciiLower("this is lowercase").write_to_string(),
/// Cow::Borrowed("this is lowercase")
/// ));
/// assert!(matches!(
/// MakeAsciiLower("this is UPPERCASE").write_to_string(),
/// Cow::Owned(s) if s == "this is uppercase"
/// ));
/// ```
pub fn to_string_or_borrow<'a>(
writeable: &impl Writeable,
reference_bytes: &'a [u8],
) -> Cow<'a, str> {
let mut sink = SliceOrString::new(reference_bytes);
let _ = writeable.write_to(&mut sink);
sink.finish()
}

443
vendor/writeable/src/try_writeable.rs vendored Normal file
View File

@@ -0,0 +1,443 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use super::*;
use crate::parts_write_adapter::CoreWriteAsPartsWrite;
use core::convert::Infallible;
/// A writeable object that can fail while writing.
///
/// The default [`Writeable`] trait returns a [`fmt::Error`], which originates from the sink.
/// In contrast, this trait allows the _writeable itself_ to trigger an error as well.
///
/// Implementations are expected to always make a _best attempt_ at writing to the sink
/// and should write replacement values in the error state. Therefore, the returned `Result`
/// can be safely ignored to emulate a "lossy" mode.
///
/// Any error substrings should be annotated with [`Part::ERROR`].
///
/// # Implementer Notes
///
/// This trait requires that implementers make a _best attempt_ at writing to the sink,
/// _even in the error state_, such as with a placeholder or fallback string.
///
/// In [`TryWriteable::try_write_to_parts()`], error substrings should be annotated with
/// [`Part::ERROR`]. Because of this, writing to parts is not default-implemented like
/// it is on [`Writeable`].
///
/// The trait is implemented on [`Result<T, E>`] where `T` and `E` both implement [`Writeable`];
/// In the `Ok` case, `T` is written, and in the `Err` case, `E` is written as a fallback value.
/// This impl, which writes [`Part::ERROR`], can be used as a basis for more advanced impls.
///
/// # Examples
///
/// Implementing on a custom type:
///
/// ```
/// use core::fmt;
/// use writeable::LengthHint;
/// use writeable::PartsWrite;
/// use writeable::TryWriteable;
///
/// #[derive(Debug, PartialEq, Eq)]
/// enum HelloWorldWriteableError {
/// MissingName,
/// }
///
/// #[derive(Debug, PartialEq, Eq)]
/// struct HelloWorldWriteable {
/// pub name: Option<&'static str>,
/// }
///
/// impl TryWriteable for HelloWorldWriteable {
/// type Error = HelloWorldWriteableError;
///
/// fn try_write_to_parts<S: PartsWrite + ?Sized>(
/// &self,
/// sink: &mut S,
/// ) -> Result<Result<(), Self::Error>, fmt::Error> {
/// sink.write_str("Hello, ")?;
/// // Use `impl TryWriteable for Result` to generate the error part:
/// let err = self.name.ok_or("nobody").try_write_to_parts(sink)?.err();
/// sink.write_char('!')?;
/// // Return a doubly-wrapped Result.
/// // The outer Result is for fmt::Error, handled by the `?`s above.
/// // The inner Result is for our own Self::Error.
/// if err.is_none() {
/// Ok(Ok(()))
/// } else {
/// Ok(Err(HelloWorldWriteableError::MissingName))
/// }
/// }
///
/// fn writeable_length_hint(&self) -> LengthHint {
/// self.name.ok_or("nobody").writeable_length_hint() + 8
/// }
/// }
///
/// // Success case:
/// writeable::assert_try_writeable_eq!(
/// HelloWorldWriteable {
/// name: Some("Alice")
/// },
/// "Hello, Alice!"
/// );
///
/// // Failure case, including the ERROR part:
/// writeable::assert_try_writeable_parts_eq!(
/// HelloWorldWriteable { name: None },
/// "Hello, nobody!",
/// Err(HelloWorldWriteableError::MissingName),
/// [(7, 13, writeable::Part::ERROR)]
/// );
/// ```
pub trait TryWriteable {
type Error;
/// Writes the content of this writeable to a sink.
///
/// If the sink hits an error, writing immediately ends,
/// `Err(`[`fmt::Error`]`)` is returned, and the sink does not contain valid output.
///
/// If the writeable hits an error, writing is continued with a replacement value,
/// `Ok(Err(`[`TryWriteable::Error`]`))` is returned, and the caller may continue using the sink.
///
/// # Lossy Mode
///
/// The [`fmt::Error`] should always be handled, but the [`TryWriteable::Error`] can be
/// ignored if a fallback string is desired instead of an error.
///
/// To handle the sink error, but not the writeable error, write:
///
/// ```
/// # use writeable::TryWriteable;
/// # let my_writeable: Result<&str, &str> = Ok("");
/// # let mut sink = String::new();
/// let _ = my_writeable.try_write_to(&mut sink)?;
/// # Ok::<(), core::fmt::Error>(())
/// ```
///
/// # Examples
///
/// The following examples use `Result<&str, usize>`, which implements [`TryWriteable`] because both `&str` and `usize` do.
///
/// Success case:
///
/// ```
/// use writeable::TryWriteable;
///
/// let w: Result<&str, usize> = Ok("success");
/// let mut sink = String::new();
/// let result = w.try_write_to(&mut sink);
///
/// assert_eq!(result, Ok(Ok(())));
/// assert_eq!(sink, "success");
/// ```
///
/// Failure case:
///
/// ```
/// use writeable::TryWriteable;
///
/// let w: Result<&str, usize> = Err(44);
/// let mut sink = String::new();
/// let result = w.try_write_to(&mut sink);
///
/// assert_eq!(result, Ok(Err(44)));
/// assert_eq!(sink, "44");
/// ```
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Self::Error>, fmt::Error> {
self.try_write_to_parts(&mut CoreWriteAsPartsWrite(sink))
}
/// Writes the content of this writeable to a sink with parts (annotations).
///
/// For more information, see:
///
/// - [`TryWriteable::try_write_to()`] for the general behavior.
/// - [`TryWriteable`] for an example with parts.
/// - [`Part`] for more about parts.
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Self::Error>, fmt::Error>;
/// Returns a hint for the number of UTF-8 bytes that will be written to the sink.
///
/// This function returns the length of the "lossy mode" string; for more information,
/// see [`TryWriteable::try_write_to()`].
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::undefined()
}
/// Writes the content of this writeable to a string.
///
/// In the failure case, this function returns the error and the best-effort string ("lossy mode").
///
/// Examples
///
/// ```
/// # use std::borrow::Cow;
/// # use writeable::TryWriteable;
/// // use the best-effort string
/// let r1: Cow<str> = Ok::<&str, u8>("ok")
/// .try_write_to_string()
/// .unwrap_or_else(|(_, s)| s);
/// // propagate the error
/// let r2: Result<Cow<str>, u8> = Ok::<&str, u8>("ok")
/// .try_write_to_string()
/// .map_err(|(e, _)| e);
/// ```
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Self::Error, Cow<'_, str>)> {
let hint = self.writeable_length_hint();
if hint.is_zero() {
return Ok(Cow::Borrowed(""));
}
let mut output = String::with_capacity(hint.capacity());
match self
.try_write_to(&mut output)
.unwrap_or_else(|fmt::Error| Ok(()))
{
Ok(()) => Ok(Cow::Owned(output)),
Err(e) => Err((e, Cow::Owned(output))),
}
}
}
impl<T, E> TryWriteable for Result<T, E>
where
T: Writeable,
E: Writeable + Clone,
{
type Error = E;
#[inline]
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Self::Error>, fmt::Error> {
match self {
Ok(t) => t.write_to(sink).map(Ok),
Err(e) => e.write_to(sink).map(|()| Err(e.clone())),
}
}
#[inline]
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Self::Error>, fmt::Error> {
match self {
Ok(t) => t.write_to_parts(sink).map(Ok),
Err(e) => sink
.with_part(Part::ERROR, |sink| e.write_to_parts(sink))
.map(|()| Err(e.clone())),
}
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
match self {
Ok(t) => t.writeable_length_hint(),
Err(e) => e.writeable_length_hint(),
}
}
#[inline]
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Self::Error, Cow<'_, str>)> {
match self {
Ok(t) => Ok(t.write_to_string()),
Err(e) => Err((e.clone(), e.write_to_string())),
}
}
}
/// A wrapper around [`TryWriteable`] that implements [`Writeable`]
/// if [`TryWriteable::Error`] is [`Infallible`].
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::exhaustive_structs)] // transparent newtype
pub struct TryWriteableInfallibleAsWriteable<T>(pub T);
impl<T> Writeable for TryWriteableInfallibleAsWriteable<T>
where
T: TryWriteable<Error = Infallible>,
{
#[inline]
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
match self.0.try_write_to(sink) {
Ok(Ok(())) => Ok(()),
Ok(Err(infallible)) => match infallible {},
Err(e) => Err(e),
}
}
#[inline]
fn write_to_parts<S: PartsWrite + ?Sized>(&self, sink: &mut S) -> fmt::Result {
match self.0.try_write_to_parts(sink) {
Ok(Ok(())) => Ok(()),
Ok(Err(infallible)) => match infallible {},
Err(e) => Err(e),
}
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
self.0.writeable_length_hint()
}
#[inline]
#[cfg(feature = "alloc")]
fn write_to_string(&self) -> Cow<'_, str> {
match self.0.try_write_to_string() {
Ok(s) => s,
Err((infallible, _)) => match infallible {},
}
}
}
impl<T> fmt::Display for TryWriteableInfallibleAsWriteable<T>
where
T: TryWriteable<Error = Infallible>,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_to(f)
}
}
/// A wrapper around [`Writeable`] that implements [`TryWriteable`]
/// with [`TryWriteable::Error`] set to [`Infallible`].
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::exhaustive_structs)] // transparent newtype
pub struct WriteableAsTryWriteableInfallible<T>(pub T);
impl<T> TryWriteable for WriteableAsTryWriteableInfallible<T>
where
T: Writeable,
{
type Error = Infallible;
#[inline]
fn try_write_to<W: fmt::Write + ?Sized>(
&self,
sink: &mut W,
) -> Result<Result<(), Infallible>, fmt::Error> {
self.0.write_to(sink).map(Ok)
}
#[inline]
fn try_write_to_parts<S: PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Infallible>, fmt::Error> {
self.0.write_to_parts(sink).map(Ok)
}
#[inline]
fn writeable_length_hint(&self) -> LengthHint {
self.0.writeable_length_hint()
}
#[inline]
#[cfg(feature = "alloc")]
fn try_write_to_string(&self) -> Result<Cow<'_, str>, (Infallible, Cow<'_, str>)> {
Ok(self.0.write_to_string())
}
}
/// Testing macros for types implementing [`TryWriteable`].
///
/// Arguments, in order:
///
/// 1. The [`TryWriteable`] under test
/// 2. The expected string value
/// 3. The expected result value, or `Ok(())` if omitted
/// 3. [`*_parts_eq`] only: a list of parts (`[(start, end, Part)]`)
///
/// Any remaining arguments get passed to `format!`
///
/// The macros tests the following:
///
/// - Equality of string content
/// - Equality of parts ([`*_parts_eq`] only)
/// - Validity of size hint
///
/// For a usage example, see [`TryWriteable`].
///
/// [`*_parts_eq`]: assert_try_writeable_parts_eq
#[macro_export]
macro_rules! assert_try_writeable_eq {
($actual_writeable:expr, $expected_str:expr $(,)?) => {
$crate::assert_try_writeable_eq!($actual_writeable, $expected_str, Ok(()))
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr $(,)?) => {
$crate::assert_try_writeable_eq!($actual_writeable, $expected_str, $expected_result, "")
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
$crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
}};
(@internal, $actual_writeable:expr, $expected_str:expr, $expected_result:expr, $($arg:tt)+) => {{
use $crate::TryWriteable;
let actual_writeable = &$actual_writeable;
let (actual_str, actual_parts, actual_error) = $crate::_internal::try_writeable_to_parts_for_test(actual_writeable);
assert_eq!(actual_str, $expected_str, $($arg)*);
assert_eq!(actual_error, Result::<(), _>::from($expected_result).err(), $($arg)*);
let actual_result = match actual_writeable.try_write_to_string() {
Ok(actual_cow_str) => {
assert_eq!(actual_cow_str, $expected_str, $($arg)+);
Ok(())
}
Err((e, actual_cow_str)) => {
assert_eq!(actual_cow_str, $expected_str, $($arg)+);
Err(e)
}
};
assert_eq!(actual_result, Result::<(), _>::from($expected_result), $($arg)*);
let length_hint = actual_writeable.writeable_length_hint();
assert!(
length_hint.0 <= actual_str.len(),
"hint lower bound {} larger than actual length {}: {}",
length_hint.0, actual_str.len(), format!($($arg)*),
);
if let Some(upper) = length_hint.1 {
assert!(
actual_str.len() <= upper,
"hint upper bound {} smaller than actual length {}: {}",
length_hint.0, actual_str.len(), format!($($arg)*),
);
}
actual_parts // return for assert_try_writeable_parts_eq
}};
}
/// See [`assert_try_writeable_eq`].
#[macro_export]
macro_rules! assert_try_writeable_parts_eq {
($actual_writeable:expr, $expected_str:expr, $expected_parts:expr $(,)?) => {
$crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, Ok(()), $expected_parts)
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr $(,)?) => {
$crate::assert_try_writeable_parts_eq!($actual_writeable, $expected_str, $expected_result, $expected_parts, "")
};
($actual_writeable:expr, $expected_str:expr, $expected_result:expr, $expected_parts:expr, $($arg:tt)+) => {{
let actual_parts = $crate::assert_try_writeable_eq!(@internal, $actual_writeable, $expected_str, $expected_result, $($arg)*);
assert_eq!(actual_parts, $expected_parts, $($arg)+);
}};
}
#[test]
fn test_result_try_writeable() {
let mut result: Result<&str, usize> = Ok("success");
assert_try_writeable_eq!(result, "success");
result = Err(44);
assert_try_writeable_eq!(result, "44", Err(44));
assert_try_writeable_parts_eq!(result, "44", Err(44), [(0, 2, Part::ERROR)])
}

26
vendor/writeable/tests/data/data.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
#[allow(dead_code)]
pub const KEBAB_CASE_STRINGS: &[&str] = &[
"ca",
"ca-ÉS",
"ca-ÉS-u-ca-buddhist",
"ca-ÉS-valencia",
"ca-ÉS-x-gbp",
"ca-ÉS-x-gbp-short",
"ca-ÉS-x-usd",
"ca-ÉS-xyzabc",
"ca-x-eur",
"cat",
"cat-bus",
"cat-🚐",
"pl-Latn-PL",
"und",
"und-fonipa",
"und-u-ca-hebrew",
"und-u-ca-japanese",
"und-x-mxn",
"zh",
];

34
vendor/writeable/tests/writeable.rs vendored Normal file
View File

@@ -0,0 +1,34 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use std::fmt;
use writeable::assert_writeable_eq;
use writeable::LengthHint;
use writeable::Writeable;
/// A sample type implementing Writeable
struct WriteableMessage<'s> {
message: &'s str,
}
impl Writeable for WriteableMessage<'_> {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
sink.write_str(self.message)
}
fn writeable_length_hint(&self) -> LengthHint {
LengthHint::exact(self.message.len())
}
}
writeable::impl_display_with_writeable!(WriteableMessage<'_>);
#[test]
fn test_basic() {
let input_string = "hello world";
let message = WriteableMessage {
message: input_string,
};
assert_writeable_eq!(&message, input_string);
}