зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1800407
- Update intl_pluralrules, unic-langid-impl and unic-langid-macros to use newer ICU4X's tinystr. r=supply-chain-reviewers,platform-i18n-reviewers,nordzilla
We have a plan to integrate ICU4X into Gecko. When importing ICU4X, tinystr crate has 2 versions. So I should update these crates to newer version to fix it. Differential Revision: https://phabricator.services.mozilla.com/D162081
This commit is contained in:
Родитель
f3ee106f12
Коммит
4df748b3ba
|
@ -1407,6 +1407,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dns-parser"
|
||||
version = "0.8.0"
|
||||
|
@ -2718,11 +2729,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "intl_pluralrules"
|
||||
version = "7.0.1"
|
||||
version = "7.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
|
||||
checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
|
@ -5384,9 +5394,12 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
|||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.3.4"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
|
||||
checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
|
@ -5695,18 +5708,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
|
||||
checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff"
|
||||
dependencies = [
|
||||
"tinystr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid-macros"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
|
||||
checksum = "055e618bf694161ffff0466d95cef3e1a5edc59f6ba1888e97801f2b4ebdc4fe"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"tinystr",
|
||||
|
|
|
@ -355,6 +355,16 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "0.3.4 -> 0.3.5"
|
||||
|
||||
[[audits.displaydoc]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.2.3"
|
||||
notes = """
|
||||
This crate is convenient macros to implement core::fmt::Display trait.
|
||||
Although `unsafe` is used for test code to call `libc::abort()`, it has no `unsafe` code in this crate. And there is no file access.
|
||||
It meets the criteria for safe-to-deploy.
|
||||
"""
|
||||
|
||||
[[audits.dogear]]
|
||||
who = "Sammy Khamis <skhamis@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -767,6 +777,11 @@ who = "Zibi Braniecki <zibi@unicode.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
version = "7.0.1"
|
||||
|
||||
[[audits.intl_pluralrules]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "7.0.1 -> 7.0.2"
|
||||
|
||||
[[audits.itoa]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -1391,6 +1406,12 @@ who = "Zibi Braniecki <zibi@unicode.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
version = "0.6.0"
|
||||
|
||||
[[audits.tinystr]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.7.0"
|
||||
notes = "One of original auther was Zibi Braniecki who worked at Mozilla and maintained by ICU4X developers (Google and Mozilla). I've vetted the one instance of unsafe code."
|
||||
|
||||
[[audits.topological-sort]]
|
||||
who = "Bobby Holley <bobbyholley@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -1446,11 +1467,21 @@ who = "Zibi Braniecki <zibi@unicode.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
version = "0.9.0"
|
||||
|
||||
[[audits.unic-langid-impl]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.9.0 -> 0.9.1"
|
||||
|
||||
[[audits.unic-langid-macros]]
|
||||
who = "Zibi Braniecki <zibi@unicode.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.9.0"
|
||||
|
||||
[[audits.unic-langid-macros]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.9.0 -> 0.9.1"
|
||||
|
||||
[[audits.unic-langid-macros-impl]]
|
||||
who = "Zibi Braniecki <zibi@unicode.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"1c4dbc1c48e7b5701be1d62f4628ecc1f933f70e43b5e8040203e8d98f53271d","Cargo.lock":"5a48a896619a85847e026ad6dde382c16385ca046f44eead947dbe8a787824be","Cargo.toml":"fcdfd7e2b923eb8cbf3bdf47da18cc26c17c5d530fe3704c5f7753d5541a9c2e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a83258f76a3af3aa3ca42731473d9a2789858b25e4e153fe3dbbd6614328bdcd","README.tpl":"974701ae7f6d238bd325dd32104a5265841e79a9b79a595f7fdd964711c0cd19","examples/simple.rs":"bca524b36621ac56044cbe3eebf8892f7a3b398fd8f257fc67b17b26ae34bfbf","src/attr.rs":"a41e416d4d2a854677052f18517549a20d05cccffa04a24398107237aff3c415","src/expand.rs":"52bbf8fca0b3cb70e3a5e4ec763d294230c0afdf47612b32e46ca5fbee64154d","src/fmt.rs":"9c13cbcd337066614bc26ecfd36ea9730c9b8099083a9a7ffd79f8dc17e414f1","src/lib.rs":"168a0b9ee9dda0b26b7c68ed13c92b9e8aaf0587110b9751ede12bbfbb58be76","tests/compile_tests.rs":"935609d68eaa52e0ce9f29b3dae649787cd1f540f9b4bbadcbefcb8a0febcfc8","tests/happy.rs":"1b4c119184ca3914684c1cafaa25a8fbb9e60ef45eb257ad7ac86b8b2d7a3856","tests/no_std/enum_prefix.rs":"12401753bb44e408e1940223e8c295e63f9ddaaa114b634aac1e057a771d7eee","tests/no_std/enum_prefix_missing.rs":"8531d335c3bb7036c2adecf9af6507c46017ef1d78e222a7528396bd78316002","tests/no_std/enum_prefix_missing.stderr":"8b9f12979bdd0f88d57c5f1217accb7268eb26ee434493fe454cf89be93394b1","tests/no_std/multi_line.rs":"a87214677020029f526cdc7024d38c738f4cbba335d215dbab5cbf394118700a","tests/no_std/multi_line.stderr":"a93b9dba31f879df465fffb0cbc8b962a07181e4e94a4ef3cf34fc5422eee4a8","tests/no_std/multi_line_allow.rs":"737ea03df1c75a1adcd375a84abcc654feb39781faf9964cdfef4b3253ce1376","tests/no_std/with.rs":"c13a2d4701606a5bacfa31593c16d6417f829259dd28dfa129b729a6f8fafac2","tests/no_std/without.rs":"992344f252e856ce4979e50cb19fdb6f4dc92fb8594e2cf26f47c4ff8124f2cd","tests/no_std/without.stderr":"811331272d445ebbc00019ff62d13cd0614bd8a83153a96136911f1a30221b20","tests/num_in_field.rs":"d0bc3ba7f9aea17d3abee4ac05afe6d4a0112d3527698791e1eeaa3464e2b1d4","tests/std/enum_prefix.rs":"12401753bb44e408e1940223e8c295e63f9ddaaa114b634aac1e057a771d7eee","tests/std/enum_prefix_missing.rs":"8531d335c3bb7036c2adecf9af6507c46017ef1d78e222a7528396bd78316002","tests/std/enum_prefix_missing.stderr":"6c5eff42d8ae6c06aceff7214b570ee179329d427f6a2117097a381eed2f2f17","tests/std/multi_line.rs":"a87214677020029f526cdc7024d38c738f4cbba335d215dbab5cbf394118700a","tests/std/multi_line.stderr":"af08a172042d4c7bce308f49044c5648896ea0707fbdfa9b971bcaf951584c77","tests/std/multi_line_allow.rs":"737ea03df1c75a1adcd375a84abcc654feb39781faf9964cdfef4b3253ce1376","tests/std/multiple.rs":"f59ea8ad70b9f6cc72e66d40832c8f8ee0402b170af0fb1443615ef6af9380dd","tests/std/without.rs":"992344f252e856ce4979e50cb19fdb6f4dc92fb8594e2cf26f47c4ff8124f2cd","tests/std/without.stderr":"43bd84eca8a361b1fcfc258014115dd12080357e15b640dd7ac6f42f59eee6ca"},"package":"3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"}
|
|
@ -0,0 +1,39 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
<!-- next-header -->
|
||||
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
# [0.2.3] - 2021-07-16
|
||||
## Added
|
||||
- Added `#[displaydoc("..")]` attribute for overriding a doc comment
|
||||
|
||||
# [0.2.2] - 2021-07-01
|
||||
## Added
|
||||
- Added prefix feature to use the doc comment from an enum and prepend it
|
||||
before the error message from each variant.
|
||||
|
||||
# [0.2.1] - 2021-03-26
|
||||
## Added
|
||||
- Added opt in support for ignoring extra doc attributes
|
||||
|
||||
# [0.2.0] - 2021-03-16
|
||||
## Changed
|
||||
|
||||
- (BREAKING) disallow multiple `doc` attributes in display impl
|
||||
[https://github.com/yaahc/displaydoc/pull/22]. Allowing and ignoring extra
|
||||
doc attributes made it too easy to accidentally create a broken display
|
||||
implementation with missing context without realizing it, this change turns
|
||||
that into a hard error and directs users towards block comments if multiple
|
||||
lines are needed.
|
||||
|
||||
<!-- next-url -->
|
||||
[Unreleased]: https://github.com/yaahc/displaydoc/compare/v0.2.3...HEAD
|
||||
[0.2.3]: https://github.com/yaahc/displaydoc/compare/v0.2.2...v0.2.3
|
||||
[0.2.2]: https://github.com/yaahc/displaydoc/compare/v0.2.1...v0.2.2
|
||||
[0.2.1]: https://github.com/yaahc/displaydoc/compare/v0.2.0...v0.2.1
|
||||
[0.2.0]: https://github.com/yaahc/displaydoc/releases/tag/v0.2.0
|
|
@ -0,0 +1,255 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"static_assertions",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"difference",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99471a206425fba51842a9186315f32d91c56eadc21ea4c21f847b59cf778f8b"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -0,0 +1,98 @@
|
|||
# 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 believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "displaydoc"
|
||||
version = "0.2.3"
|
||||
authors = ["Jane Lusby <jlusby@yaah.dev>"]
|
||||
description = "A derive macro for implementing the display Trait via a doc comment and string interpolation\n"
|
||||
homepage = "https://github.com/yaahc/displaydoc"
|
||||
documentation = "https://docs.rs/displaydoc"
|
||||
readme = "README.md"
|
||||
keywords = ["display", "derive"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/yaahc/displaydoc"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.release]
|
||||
no-dev-version = true
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "CHANGELOG.md"
|
||||
replace = "{{version}}"
|
||||
search = "Unreleased"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
exactly = 1
|
||||
file = "src/lib.rs"
|
||||
replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]"
|
||||
search = "#!\\[doc\\(html_root_url.*"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "CHANGELOG.md"
|
||||
replace = "{{date}}"
|
||||
search = "ReleaseDate"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
exactly = 1
|
||||
file = "CHANGELOG.md"
|
||||
replace = "<!-- next-header -->\n\n## [Unreleased] - ReleaseDate"
|
||||
search = "<!-- next-header -->"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
exactly = 1
|
||||
file = "CHANGELOG.md"
|
||||
replace = "...{{tag_name}}"
|
||||
search = "\\.\\.\\.HEAD"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
exactly = 1
|
||||
file = "CHANGELOG.md"
|
||||
replace = "<!-- next-url -->\n[Unreleased]: https://github.com/yaahc/{{crate_name}}/compare/{{tag_name}}...HEAD"
|
||||
search = "<!-- next-url -->"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
proc-macro = true
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
[dev-dependencies.libc]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.pretty_assertions]
|
||||
version = "0.6.1"
|
||||
|
||||
[dev-dependencies.rustversion]
|
||||
version = "1.0.0"
|
||||
|
||||
[dev-dependencies.static_assertions]
|
||||
version = "0.3.4"
|
||||
|
||||
[dev-dependencies.thiserror]
|
||||
version = "1.0.24"
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
|
@ -0,0 +1,99 @@
|
|||
derive(Display) /// `From<docs>`
|
||||
===============
|
||||
|
||||
[![Latest Version](https://img.shields.io/crates/v/displaydoc.svg)](https://crates.io/crates/displaydoc)
|
||||
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/displaydoc)
|
||||
|
||||
This library provides a convenient derive macro for the standard library's
|
||||
[`core::fmt::Display`] trait.
|
||||
|
||||
[`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
displaydoc = "0.2"
|
||||
```
|
||||
|
||||
*Compiler support: requires rustc 1.31+*
|
||||
|
||||
<br>
|
||||
|
||||
### Example
|
||||
|
||||
```rust
|
||||
use std::io;
|
||||
use displaydoc::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Display, Error, Debug)]
|
||||
pub enum DataStoreError {
|
||||
/// data store disconnected
|
||||
Disconnect(#[source] io::Error),
|
||||
/// the data for key `{0}` is not available
|
||||
Redaction(String),
|
||||
/// invalid header (expected {expected:?}, found {found:?})
|
||||
InvalidHeader {
|
||||
expected: String,
|
||||
found: String,
|
||||
},
|
||||
/// unknown data store error
|
||||
Unknown,
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### Details
|
||||
|
||||
- A `Display` impl is generated for your type if you provide doc comment
|
||||
messages on the struct or each variant of your enum, as shown above in the
|
||||
example.
|
||||
|
||||
The messages support a shorthand for interpolating fields from the error.
|
||||
|
||||
- `/// {var}` ⟶ `write!("{}", self.var)`
|
||||
- `/// {0}` ⟶ `write!("{}", self.0)`
|
||||
- `/// {var:?}` ⟶ `write!("{:?}", self.var)`
|
||||
- `/// {0:?}` ⟶ `write!("{:?}", self.0)`
|
||||
|
||||
- Two optional attributes can be added to your types next to the derive:
|
||||
|
||||
- `#[ignore_extra_doc_attributes]` makes the macro ignore any doc
|
||||
comment attributes (or `///` lines) after the first. Multi-line
|
||||
comments using `///` are otherwise treated as an error, so use this
|
||||
attribute or consider switching to block doc comments (`/** */`).
|
||||
|
||||
- `#[prefix_enum_doc_attributes]` combines the doc comment message on
|
||||
your enum itself with the messages for each variant, in the format
|
||||
“enum: variant”. When added to an enum, the doc comment on the enum
|
||||
becomes mandatory. When added to any other type, it has no effect.
|
||||
|
||||
- In case you want to have an independent doc comment, the
|
||||
`#[displaydoc("...")` atrribute may be used on the variant or struct to
|
||||
override it.
|
||||
|
||||
<br>
|
||||
|
||||
### FAQ
|
||||
|
||||
1. **Is this crate `no_std` compatible?**
|
||||
* Yes! This crate implements the `core::fmt::Display` trait not the `std::fmt::Display` trait so it should work in `std` and `no_std` environments. Just add `default-features = false`.
|
||||
|
||||
2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?**
|
||||
* Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl, it then specializes for `Path` and `PathBuf` and when either of these types are found it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the Display format specifier!
|
||||
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||
</sup>
|
||||
|
||||
<br>
|
||||
|
||||
<sub>
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
</sub>
|
|
@ -0,0 +1,23 @@
|
|||
derive(Display) /// `From<docs>`
|
||||
===============
|
||||
|
||||
[![Latest Version](https://img.shields.io/crates/v/displaydoc.svg)](https://crates.io/crates/displaydoc)
|
||||
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/displaydoc)
|
||||
|
||||
{{readme}}
|
||||
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||
</sup>
|
||||
|
||||
<br>
|
||||
|
||||
<sub>
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
</sub>
|
|
@ -0,0 +1,36 @@
|
|||
use displaydoc::Display;
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum DataStoreError {
|
||||
/// data store disconnected
|
||||
Disconnect,
|
||||
/// the data for key `{0}` is not available
|
||||
Redaction(String),
|
||||
/// invalid header (expected {expected:?}, found {found:?})
|
||||
InvalidHeader { expected: String, found: String },
|
||||
/// unknown data store error
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let disconnect = DataStoreError::Disconnect;
|
||||
println!(
|
||||
"Enum value `Disconnect` should be printed as:\n\t{}",
|
||||
disconnect
|
||||
);
|
||||
|
||||
let redaction = DataStoreError::Redaction(String::from("Dummy"));
|
||||
println!(
|
||||
"Enum value `Redaction` should be printed as:\n\t{}",
|
||||
redaction
|
||||
);
|
||||
|
||||
let invalid_header = DataStoreError::InvalidHeader {
|
||||
expected: String::from("https"),
|
||||
found: String::from("http"),
|
||||
};
|
||||
println!(
|
||||
"Enum value `InvalidHeader` should be printed as:\n\t{}",
|
||||
invalid_header
|
||||
);
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{Attribute, LitStr, Meta, Result};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Display {
|
||||
pub(crate) fmt: LitStr,
|
||||
pub(crate) args: TokenStream,
|
||||
}
|
||||
|
||||
pub(crate) struct VariantDisplay {
|
||||
pub(crate) r#enum: Option<Display>,
|
||||
pub(crate) variant: Display,
|
||||
}
|
||||
|
||||
impl ToTokens for Display {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let fmt = &self.fmt;
|
||||
let args = &self.args;
|
||||
tokens.extend(quote! {
|
||||
write!(formatter, #fmt #args)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for VariantDisplay {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
if let Some(ref r#enum) = self.r#enum {
|
||||
r#enum.to_tokens(tokens);
|
||||
tokens.extend(quote! { ?; write!(formatter, ": ")?; });
|
||||
}
|
||||
self.variant.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct AttrsHelper {
|
||||
ignore_extra_doc_attributes: bool,
|
||||
prefix_enum_doc_attributes: bool,
|
||||
}
|
||||
|
||||
impl AttrsHelper {
|
||||
pub(crate) fn new(attrs: &[Attribute]) -> Self {
|
||||
let ignore_extra_doc_attributes = attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.is_ident("ignore_extra_doc_attributes"));
|
||||
let prefix_enum_doc_attributes = attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.is_ident("prefix_enum_doc_attributes"));
|
||||
|
||||
Self {
|
||||
ignore_extra_doc_attributes,
|
||||
prefix_enum_doc_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display(&self, attrs: &[Attribute]) -> Result<Option<Display>> {
|
||||
let displaydoc_attr = attrs.iter().find(|attr| attr.path.is_ident("displaydoc"));
|
||||
|
||||
if let Some(displaydoc_attr) = displaydoc_attr {
|
||||
let lit = displaydoc_attr
|
||||
.parse_args()
|
||||
.expect("#[displaydoc(\"foo\")] must contain string arguments");
|
||||
let mut display = Display {
|
||||
fmt: lit,
|
||||
args: TokenStream::new(),
|
||||
};
|
||||
|
||||
display.expand_shorthand();
|
||||
return Ok(Some(display));
|
||||
}
|
||||
|
||||
let num_doc_attrs = attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path.is_ident("doc"))
|
||||
.count();
|
||||
|
||||
if !self.ignore_extra_doc_attributes && num_doc_attrs > 1 {
|
||||
panic!("Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive.");
|
||||
}
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path.is_ident("doc") {
|
||||
let meta = attr.parse_meta()?;
|
||||
let lit = match meta {
|
||||
Meta::NameValue(syn::MetaNameValue {
|
||||
lit: syn::Lit::Str(lit),
|
||||
..
|
||||
}) => lit,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
// Make an attempt and cleaning up multiline doc comments
|
||||
let doc_str = lit
|
||||
.value()
|
||||
.lines()
|
||||
.map(|line| line.trim().trim_start_matches('*').trim())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n");
|
||||
|
||||
let lit = LitStr::new(doc_str.trim(), lit.span());
|
||||
|
||||
let mut display = Display {
|
||||
fmt: lit,
|
||||
args: TokenStream::new(),
|
||||
};
|
||||
|
||||
display.expand_shorthand();
|
||||
return Ok(Some(display));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) fn display_with_input(
|
||||
&self,
|
||||
r#enum: &[Attribute],
|
||||
variant: &[Attribute],
|
||||
) -> Result<Option<VariantDisplay>> {
|
||||
let r#enum = if self.prefix_enum_doc_attributes {
|
||||
let result = self
|
||||
.display(r#enum)?
|
||||
.expect("Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself.");
|
||||
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(self
|
||||
.display(variant)?
|
||||
.map(|variant| VariantDisplay { r#enum, variant }))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
use super::attr::AttrsHelper;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Result};
|
||||
|
||||
pub(crate) fn derive(input: &DeriveInput) -> Result<TokenStream> {
|
||||
let impls = match &input.data {
|
||||
Data::Struct(data) => impl_struct(input, data),
|
||||
Data::Enum(data) => impl_enum(input, data),
|
||||
Data::Union(_) => Err(Error::new_spanned(input, "Unions are not supported")),
|
||||
}?;
|
||||
|
||||
let helpers = specialization();
|
||||
let dummy_const = format_ident!("_DERIVE_Display_FOR_{}", input.ident);
|
||||
Ok(quote! {
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
#helpers
|
||||
#impls
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn specialization() -> TokenStream {
|
||||
quote! {
|
||||
trait DisplayToDisplayDoc {
|
||||
fn __displaydoc_display(&self) -> Self;
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Display> DisplayToDisplayDoc for &T {
|
||||
fn __displaydoc_display(&self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// If the `std` feature gets enabled we want to ensure that any crate
|
||||
// using displaydoc can still reference the std crate, which is already
|
||||
// being compiled in by whoever enabled the `std` feature in
|
||||
// `displaydoc`, even if the crates using displaydoc are no_std.
|
||||
extern crate std;
|
||||
|
||||
trait PathToDisplayDoc {
|
||||
fn __displaydoc_display(&self) -> std::path::Display<'_>;
|
||||
}
|
||||
|
||||
impl PathToDisplayDoc for std::path::Path {
|
||||
fn __displaydoc_display(&self) -> std::path::Display<'_> {
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
impl PathToDisplayDoc for std::path::PathBuf {
|
||||
fn __displaydoc_display(&self) -> std::path::Display<'_> {
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn specialization() -> TokenStream {
|
||||
quote! {}
|
||||
}
|
||||
|
||||
fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
|
||||
let ty = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let helper = AttrsHelper::new(&input.attrs);
|
||||
|
||||
let display = helper.display(&input.attrs)?.map(|display| {
|
||||
let pat = match &data.fields {
|
||||
Fields::Named(fields) => {
|
||||
let var = fields.named.iter().map(|field| &field.ident);
|
||||
quote!(Self { #(#var),* })
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
|
||||
quote!(Self(#(#var),*))
|
||||
}
|
||||
Fields::Unit => quote!(_),
|
||||
};
|
||||
quote! {
|
||||
impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
|
||||
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[allow(unused_variables)]
|
||||
let #pat = self;
|
||||
#display
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! { #display })
|
||||
}
|
||||
|
||||
fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
|
||||
let ty = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let helper = AttrsHelper::new(&input.attrs);
|
||||
|
||||
let displays = data
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| helper.display_with_input(&input.attrs, &variant.attrs))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
if displays.iter().any(Option::is_some) {
|
||||
let arms = data
|
||||
.variants
|
||||
.iter()
|
||||
.zip(displays)
|
||||
.map(|(variant, display)| {
|
||||
let display =
|
||||
display.ok_or_else(|| Error::new_spanned(variant, "missing doc comment"))?;
|
||||
let ident = &variant.ident;
|
||||
Ok(match &variant.fields {
|
||||
Fields::Named(fields) => {
|
||||
let var = fields.named.iter().map(|field| &field.ident);
|
||||
quote!(#ty::#ident { #(#var),* } => { #display })
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
|
||||
quote!(#ty::#ident(#(#var),*) => { #display })
|
||||
}
|
||||
Fields::Unit => quote!(#ty::#ident => { #display }),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
Ok(quote! {
|
||||
impl #impl_generics core::fmt::Display for #ty #ty_generics #where_clause {
|
||||
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[allow(unused_variables)]
|
||||
match self {
|
||||
#(#arms,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Err(Error::new_spanned(input, "Missing doc comments"))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
use crate::attr::Display;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote_spanned;
|
||||
use syn::{Ident, LitStr};
|
||||
|
||||
macro_rules! peek_next {
|
||||
($read:ident) => {
|
||||
match $read.chars().next() {
|
||||
Some(next) => next,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Display {
|
||||
// Transform `"error {var}"` to `"error {}", var`.
|
||||
pub(crate) fn expand_shorthand(&mut self) {
|
||||
let span = self.fmt.span();
|
||||
let fmt = self.fmt.value();
|
||||
let mut read = fmt.as_str();
|
||||
let mut out = String::new();
|
||||
let mut args = TokenStream::new();
|
||||
|
||||
while let Some(brace) = read.find('{') {
|
||||
out += &read[..=brace];
|
||||
read = &read[brace + 1..];
|
||||
|
||||
// skip cases where we find a {{
|
||||
if read.starts_with('{') {
|
||||
out.push('{');
|
||||
read = &read[1..];
|
||||
continue;
|
||||
}
|
||||
|
||||
let next = peek_next!(read);
|
||||
|
||||
let var = match next {
|
||||
'0'..='9' => take_int(&mut read),
|
||||
'a'..='z' | 'A'..='Z' | '_' => take_ident(&mut read),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let ident = Ident::new(&var, span);
|
||||
|
||||
let next = peek_next!(read);
|
||||
|
||||
let arg = if cfg!(feature = "std") && next == '}' {
|
||||
quote_spanned!(span=> , #ident.__displaydoc_display())
|
||||
} else {
|
||||
quote_spanned!(span=> , #ident)
|
||||
};
|
||||
|
||||
args.extend(arg);
|
||||
}
|
||||
|
||||
out += read;
|
||||
self.fmt = LitStr::new(&out, self.fmt.span());
|
||||
self.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
fn take_int(read: &mut &str) -> String {
|
||||
let mut int = String::new();
|
||||
int.push('_');
|
||||
for (i, ch) in read.char_indices() {
|
||||
match ch {
|
||||
'0'..='9' => int.push(ch),
|
||||
_ => {
|
||||
*read = &read[i..];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int
|
||||
}
|
||||
|
||||
fn take_ident(read: &mut &str) -> String {
|
||||
let mut ident = String::new();
|
||||
for (i, ch) in read.char_indices() {
|
||||
match ch {
|
||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch),
|
||||
_ => {
|
||||
*read = &read[i..];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ident
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use proc_macro2::Span;
|
||||
|
||||
fn assert(input: &str, fmt: &str, args: &str) {
|
||||
let mut display = Display {
|
||||
fmt: LitStr::new(input, Span::call_site()),
|
||||
args: TokenStream::new(),
|
||||
};
|
||||
display.expand_shorthand();
|
||||
assert_eq!(fmt, display.fmt.value());
|
||||
assert_eq!(args, display.args.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand() {
|
||||
assert("fn main() {{ }}", "fn main() {{ }}", "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(feature = "std"), ignore)]
|
||||
fn test_std_expand() {
|
||||
assert(
|
||||
"{v} {v:?} {0} {0:?}",
|
||||
"{} {:?} {} {:?}",
|
||||
", v . __displaydoc_display () , v , _0 . __displaydoc_display () , _0",
|
||||
);
|
||||
assert("error {var}", "error {}", ", var . __displaydoc_display ()");
|
||||
|
||||
assert(
|
||||
"error {var1}",
|
||||
"error {}",
|
||||
", var1 . __displaydoc_display ()",
|
||||
);
|
||||
|
||||
assert(
|
||||
"error {var1var}",
|
||||
"error {}",
|
||||
", var1var . __displaydoc_display ()",
|
||||
);
|
||||
|
||||
assert(
|
||||
"The path {0}",
|
||||
"The path {}",
|
||||
", _0 . __displaydoc_display ()",
|
||||
);
|
||||
assert("The path {0:?}", "The path {:?}", ", _0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "std", ignore)]
|
||||
fn test_nostd_expand() {
|
||||
assert(
|
||||
"{v} {v:?} {0} {0:?}",
|
||||
"{} {:?} {} {:?}",
|
||||
", v , v , _0 , _0",
|
||||
);
|
||||
assert("error {var}", "error {}", ", var");
|
||||
|
||||
assert("The path {0}", "The path {}", ", _0");
|
||||
assert("The path {0:?}", "The path {:?}", ", _0");
|
||||
|
||||
assert("error {var1}", "error {}", ", var1");
|
||||
|
||||
assert("error {var1var}", "error {}", ", var1var");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
//! This library provides a convenient derive macro for the standard library's
|
||||
//! [`core::fmt::Display`] trait.
|
||||
//!
|
||||
//! [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! displaydoc = "0.2"
|
||||
//! ```
|
||||
//!
|
||||
//! *Compiler support: requires rustc 1.31+*
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::io;
|
||||
//! use displaydoc::Display;
|
||||
//! use thiserror::Error;
|
||||
//!
|
||||
//! #[derive(Display, Error, Debug)]
|
||||
//! pub enum DataStoreError {
|
||||
//! /// data store disconnected
|
||||
//! Disconnect(#[source] io::Error),
|
||||
//! /// the data for key `{0}` is not available
|
||||
//! Redaction(String),
|
||||
//! /// invalid header (expected {expected:?}, found {found:?})
|
||||
//! InvalidHeader {
|
||||
//! expected: String,
|
||||
//! found: String,
|
||||
//! },
|
||||
//! /// unknown data store error
|
||||
//! Unknown,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! ## Details
|
||||
//!
|
||||
//! - A `Display` impl is generated for your type if you provide doc comment
|
||||
//! messages on the struct or each variant of your enum, as shown above in the
|
||||
//! example.
|
||||
//!
|
||||
//! The messages support a shorthand for interpolating fields from the error.
|
||||
//!
|
||||
//! - `/// {var}` ⟶ `write!("{}", self.var)`
|
||||
//! - `/// {0}` ⟶ `write!("{}", self.0)`
|
||||
//! - `/// {var:?}` ⟶ `write!("{:?}", self.var)`
|
||||
//! - `/// {0:?}` ⟶ `write!("{:?}", self.0)`
|
||||
//!
|
||||
//! - Two optional attributes can be added to your types next to the derive:
|
||||
//!
|
||||
//! - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc
|
||||
//! comment attributes (or `///` lines) after the first. Multi-line
|
||||
//! comments using `///` are otherwise treated as an error, so use this
|
||||
//! attribute or consider switching to block doc comments (`/** */`).
|
||||
//!
|
||||
//! - `#[prefix_enum_doc_attributes]` combines the doc comment message on
|
||||
//! your enum itself with the messages for each variant, in the format
|
||||
//! “enum: variant”. When added to an enum, the doc comment on the enum
|
||||
//! becomes mandatory. When added to any other type, it has no effect.
|
||||
//!
|
||||
//! - In case you want to have an independent doc comment, the
|
||||
//! `#[displaydoc("...")` atrribute may be used on the variant or struct to
|
||||
//! override it.
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! ## FAQ
|
||||
//!
|
||||
//! 1. **Is this crate `no_std` compatible?**
|
||||
//! * Yes! This crate implements the `core::fmt::Display` trait not the `std::fmt::Display` trait so it should work in `std` and `no_std` environments. Just add `default-features = false`.
|
||||
//!
|
||||
//! 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?**
|
||||
//! * Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl, it then specializes for `Path` and `PathBuf` and when either of these types are found it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the Display format specifier!
|
||||
#![doc(html_root_url = "https://docs.rs/displaydoc/0.2.3")]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![warn(
|
||||
rust_2018_idioms,
|
||||
unreachable_pub,
|
||||
bad_style,
|
||||
const_err,
|
||||
dead_code,
|
||||
improper_ctypes,
|
||||
non_shorthand_field_patterns,
|
||||
no_mangle_generic_items,
|
||||
overflowing_literals,
|
||||
path_statements,
|
||||
patterns_in_fns_without_body,
|
||||
private_in_public,
|
||||
unconditional_recursion,
|
||||
unused,
|
||||
unused_allocation,
|
||||
unused_comparisons,
|
||||
unused_parens,
|
||||
while_true
|
||||
)]
|
||||
#![allow(clippy::try_err)]
|
||||
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate proc_macro;
|
||||
|
||||
mod attr;
|
||||
mod expand;
|
||||
mod fmt;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
/// Derive macro for implementing `Display` via doc comment attributes
|
||||
#[proc_macro_derive(
|
||||
Display,
|
||||
attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc)
|
||||
)]
|
||||
pub fn derive_error(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
expand::derive(&input)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#[allow(unused_attributes)]
|
||||
#[rustversion::attr(not(nightly), ignore)]
|
||||
#[test]
|
||||
fn no_std() {
|
||||
let t = trybuild::TestCases::new();
|
||||
#[cfg(not(feature = "std"))]
|
||||
t.compile_fail("tests/no_std/without.rs");
|
||||
#[cfg(not(feature = "std"))]
|
||||
t.compile_fail("tests/no_std/multi_line.rs");
|
||||
#[cfg(not(feature = "std"))]
|
||||
t.pass("tests/no_std/multi_line_allow.rs");
|
||||
#[cfg(not(feature = "std"))]
|
||||
t.compile_fail("tests/no_std/enum_prefix_missing.rs");
|
||||
#[cfg(not(feature = "std"))]
|
||||
t.pass("tests/no_std/enum_prefix.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.compile_fail("tests/std/without.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.compile_fail("tests/std/multi_line.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.pass("tests/std/multi_line_allow.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.compile_fail("tests/std/enum_prefix_missing.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.pass("tests/std/enum_prefix.rs");
|
||||
#[cfg(feature = "std")]
|
||||
t.pass("tests/std/multiple.rs");
|
||||
t.pass("tests/no_std/with.rs");
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
use displaydoc::Display;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Display)]
|
||||
/// Just a basic struct {thing}
|
||||
struct HappyStruct {
|
||||
thing: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
#[ignore_extra_doc_attributes]
|
||||
/// Just a basic struct {thing}
|
||||
/// and this line should get ignored
|
||||
struct HappyStruct2 {
|
||||
thing: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
enum Happy {
|
||||
/// I really like Variant1
|
||||
Variant1,
|
||||
/// Variant2 is pretty swell 2
|
||||
Variant2,
|
||||
/// Variant3 is okay {sometimes}
|
||||
Variant3 { sometimes: &'static str },
|
||||
/**
|
||||
* Variant4 wants to have a lot of lines
|
||||
*
|
||||
* Lets see how this works out for it
|
||||
*/
|
||||
Variant4,
|
||||
/// Variant5 has a parameter {0} and some regular comments
|
||||
// A regular comment that won't get picked
|
||||
Variant5(u32),
|
||||
|
||||
/// The path {0}
|
||||
#[cfg(feature = "std")]
|
||||
Variant6(PathBuf),
|
||||
|
||||
/// These docs are ignored
|
||||
#[displaydoc("Variant7 has a parameter {0} and uses #[displaydoc]")]
|
||||
/// These docs are also ignored
|
||||
Variant7(u32),
|
||||
}
|
||||
|
||||
// Used for testing indented doc comments
|
||||
mod inner_mod {
|
||||
use super::Display;
|
||||
|
||||
#[derive(Display)]
|
||||
pub enum InnerHappy {
|
||||
/// I really like Variant1
|
||||
Variant1,
|
||||
/// Variant2 is pretty swell 2
|
||||
Variant2,
|
||||
/// Variant3 is okay {sometimes}
|
||||
Variant3 { sometimes: &'static str },
|
||||
/**
|
||||
* Variant4 wants to have a lot of lines
|
||||
*
|
||||
* Lets see how this works out for it
|
||||
*/
|
||||
Variant4,
|
||||
/// Variant5 has a parameter {0} and some regular comments
|
||||
// A regular comment that won't get picked
|
||||
Variant5(u32),
|
||||
|
||||
/** what happens if we
|
||||
* put text on the first line?
|
||||
*/
|
||||
Variant6,
|
||||
|
||||
/**
|
||||
what happens if we don't use *?
|
||||
*/
|
||||
Variant7,
|
||||
|
||||
/**
|
||||
*
|
||||
* what about extra new lines?
|
||||
*/
|
||||
Variant8,
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_display<T: std::fmt::Display>(input: T, expected: &'static str) {
|
||||
let out = format!("{}", input);
|
||||
assert_eq!(expected, out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_it_print() {
|
||||
assert_display(Happy::Variant1, "I really like Variant1");
|
||||
assert_display(Happy::Variant2, "Variant2 is pretty swell 2");
|
||||
assert_display(Happy::Variant3 { sometimes: "hi" }, "Variant3 is okay hi");
|
||||
assert_display(
|
||||
Happy::Variant4,
|
||||
"Variant4 wants to have a lot of lines\n\nLets see how this works out for it",
|
||||
);
|
||||
assert_display(
|
||||
Happy::Variant5(2),
|
||||
"Variant5 has a parameter 2 and some regular comments",
|
||||
);
|
||||
assert_display(
|
||||
Happy::Variant7(2),
|
||||
"Variant7 has a parameter 2 and uses #[displaydoc]",
|
||||
);
|
||||
assert_display(HappyStruct { thing: "hi" }, "Just a basic struct hi");
|
||||
|
||||
assert_display(HappyStruct2 { thing: "hi2" }, "Just a basic struct hi2");
|
||||
|
||||
assert_display(inner_mod::InnerHappy::Variant1, "I really like Variant1");
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant2,
|
||||
"Variant2 is pretty swell 2",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant3 { sometimes: "hi" },
|
||||
"Variant3 is okay hi",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant4,
|
||||
"Variant4 wants to have a lot of lines\n\nLets see how this works out for it",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant5(2),
|
||||
"Variant5 has a parameter 2 and some regular comments",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant6,
|
||||
"what happens if we\nput text on the first line?",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant7,
|
||||
"what happens if we don\'t use *?",
|
||||
);
|
||||
assert_display(
|
||||
inner_mod::InnerHappy::Variant8,
|
||||
"what about extra new lines?",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "std")]
|
||||
fn does_it_print_path() {
|
||||
assert_display(
|
||||
Happy::Variant6(PathBuf::from("/var/log/happy")),
|
||||
"The path /var/log/happy",
|
||||
);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
#[prefix_enum_doc_attributes]
|
||||
enum TestType {
|
||||
/// this variant is too
|
||||
Variant1,
|
||||
|
||||
/// this variant is two
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,35 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
#[derive(Display)]
|
||||
#[prefix_enum_doc_attributes]
|
||||
enum TestType {
|
||||
/// this variant is too
|
||||
Variant1,
|
||||
|
||||
/// this variant is two
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
error: proc-macro derive panicked
|
||||
--> $DIR/enum_prefix_missing.rs:22:10
|
||||
|
|
||||
22 | #[derive(Display)]
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: message: Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself.
|
||||
|
||||
error[E0277]: `TestType` doesn't implement `Display`
|
||||
--> $DIR/enum_prefix_missing.rs:32:44
|
||||
|
|
||||
32 | static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `TestType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `Display` is not implemented for `TestType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -0,0 +1,37 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
enum TestType {
|
||||
/// This one is okay
|
||||
Variant1,
|
||||
|
||||
/// Multi
|
||||
/// line
|
||||
/// doc.
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
error: proc-macro derive panicked
|
||||
--> $DIR/multi_line.rs:23:10
|
||||
|
|
||||
23 | #[derive(Display)]
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: message: Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive.
|
||||
|
||||
error[E0277]: `TestType` doesn't implement `Display`
|
||||
--> $DIR/multi_line.rs:34:44
|
||||
|
|
||||
34 | static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `TestType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `Display` is not implemented for `TestType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -0,0 +1,38 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
#[ignore_extra_doc_attributes]
|
||||
enum TestType {
|
||||
/// This one is okay
|
||||
Variant1,
|
||||
|
||||
/// Multi
|
||||
/// line
|
||||
/// doc.
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,32 @@
|
|||
#![feature(lang_items, start)]
|
||||
#![no_std]
|
||||
|
||||
#[start]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
struct FakeType;
|
||||
|
||||
static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
|
@ -0,0 +1,28 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
struct FakeType;
|
||||
|
||||
static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
warning: unused import: `displaydoc::Display`
|
||||
--> $DIR/without.rs:20:5
|
||||
|
|
||||
20 | use displaydoc::Display;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
||||
error[E0277]: `FakeType` doesn't implement `Display`
|
||||
--> $DIR/without.rs:25:44
|
||||
|
|
||||
25 | static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `FakeType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `Display` is not implemented for `FakeType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -0,0 +1,22 @@
|
|||
/// {foo1} {foo2}
|
||||
#[derive(displaydoc::Display)]
|
||||
pub struct Test {
|
||||
foo1: String,
|
||||
foo2: String,
|
||||
}
|
||||
|
||||
fn assert_display<T: std::fmt::Display>(input: T, expected: &'static str) {
|
||||
let out = format!("{}", input);
|
||||
assert_eq!(expected, out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_it_print() {
|
||||
assert_display(
|
||||
Test {
|
||||
foo1: "hi".into(),
|
||||
foo2: "hello".into(),
|
||||
},
|
||||
"hi hello",
|
||||
);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
#[prefix_enum_doc_attributes]
|
||||
enum TestType {
|
||||
/// this variant is too
|
||||
Variant1,
|
||||
|
||||
/// this variant is two
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,35 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
#[derive(Display)]
|
||||
#[prefix_enum_doc_attributes]
|
||||
enum TestType {
|
||||
/// this variant is too
|
||||
Variant1,
|
||||
|
||||
/// this variant is two
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
error: proc-macro derive panicked
|
||||
--> $DIR/enum_prefix_missing.rs:22:10
|
||||
|
|
||||
22 | #[derive(Display)]
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: message: Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself.
|
||||
|
||||
error[E0277]: `TestType` doesn't implement `std::fmt::Display`
|
||||
--> $DIR/enum_prefix_missing.rs:32:44
|
||||
|
|
||||
32 | static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `TestType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `std::fmt::Display` is not implemented for `TestType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -0,0 +1,37 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
enum TestType {
|
||||
/// This one is okay
|
||||
Variant1,
|
||||
|
||||
/// Multi
|
||||
/// line
|
||||
/// doc.
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
error: proc-macro derive panicked
|
||||
--> $DIR/multi_line.rs:23:10
|
||||
|
|
||||
23 | #[derive(Display)]
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: message: Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive.
|
||||
|
||||
error[E0277]: `TestType` doesn't implement `std::fmt::Display`
|
||||
--> $DIR/multi_line.rs:34:44
|
||||
|
|
||||
34 | static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `TestType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `std::fmt::Display` is not implemented for `TestType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -0,0 +1,38 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
#[ignore_extra_doc_attributes]
|
||||
enum TestType {
|
||||
/// This one is okay
|
||||
Variant1,
|
||||
|
||||
/// Multi
|
||||
/// line
|
||||
/// doc.
|
||||
Variant2,
|
||||
}
|
||||
|
||||
static_assertions::assert_impl_all!(label; TestType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,38 @@
|
|||
#![feature(lang_items, start)]
|
||||
#![no_std]
|
||||
|
||||
#[start]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
#[derive(Display)]
|
||||
struct FakeType;
|
||||
|
||||
static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
||||
|
||||
/// this type is pretty swell2
|
||||
#[derive(Display)]
|
||||
struct FakeType2;
|
||||
|
||||
static_assertions::assert_impl_all!(label2; FakeType2, core::fmt::Display);
|
|
@ -0,0 +1,28 @@
|
|||
#![cfg_attr(not(feature = "std"), feature(lang_items, start))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg_attr(not(feature = "std"), start)]
|
||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
0
|
||||
}
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
libc::abort();
|
||||
}
|
||||
}
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// this type is pretty swell
|
||||
struct FakeType;
|
||||
|
||||
static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn main() {}
|
|
@ -0,0 +1,19 @@
|
|||
warning: unused import: `displaydoc::Display`
|
||||
--> $DIR/without.rs:20:5
|
||||
|
|
||||
20 | use displaydoc::Display;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
||||
error[E0277]: `FakeType` doesn't implement `std::fmt::Display`
|
||||
--> $DIR/without.rs:25:44
|
||||
|
|
||||
25 | static_assertions::assert_impl_all!(label; FakeType, core::fmt::Display);
|
||||
| -------------------------------------------^^^^^^^^----------------------
|
||||
| | |
|
||||
| | `FakeType` cannot be formatted with the default formatter
|
||||
| required by this bound in `assert_impl_all`
|
||||
|
|
||||
= help: the trait `std::fmt::Display` is not implemented for `FakeType`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"3b7451d96ed662827dd4163d64d96840fee1c4241c2480b8cdd91ef156ad7896","README.md":"4dad9bc033a67101bc318662c055318457f0b91e12fdc0a566495b455ceeb4b8","benches/pluralrules.rs":"9c80009fa94a8dbf31bdba489f320d92a40f482c37149030fbba53cd62b5435f","src/lib.rs":"a642fa4b7147731449acf39650fc92e764716de8303d9637c3263ae3ae03b899","src/operands.rs":"b58c9f8f0f7ce4622ccc685f08b15ede09591b628deffa3b14abde873d26c44a","src/rules.rs":"7b3f0c12e0722401efbdb54873e8545f943bf4a34cba9f894ead9dc955112aaf"},"package":"b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"}
|
||||
{"files":{"Cargo.toml":"595634ece6249e274c389db766170aa88f78df41e8887d333a7cac8dad6b0bed","README.md":"7516007e35aa847bf81b8b94f45181f532bdc0aad3e389efd14691fa3aace3f0","benches/pluralrules.rs":"9c80009fa94a8dbf31bdba489f320d92a40f482c37149030fbba53cd62b5435f","src/lib.rs":"054fd83f1e8b42ef7866ec681c59617952cad48f578ca338e945d2a99178ea7d","src/operands.rs":"68d920f66ed67f361b3b0387844dbb8db57fad660f84cfcb361d8f4f5157832a","src/rules.rs":"7b3f0c12e0722401efbdb54873e8545f943bf4a34cba9f894ead9dc955112aaf"},"package":"078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"}
|
|
@ -3,40 +3,56 @@
|
|||
# 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
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
# 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 = "2018"
|
||||
name = "intl_pluralrules"
|
||||
version = "7.0.1"
|
||||
authors = ["Kekoa Riggin <kekoariggin@gmail.com>", "Zibi Braniecki <zbraniecki@mozilla.com>"]
|
||||
include = ["src/**/*", "benches/*.rs", "Cargo.toml", "README.md"]
|
||||
version = "7.0.2"
|
||||
authors = [
|
||||
"Kekoa Riggin <kekoariggin@gmail.com>",
|
||||
"Zibi Braniecki <zbraniecki@mozilla.com>",
|
||||
]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"benches/*.rs",
|
||||
"Cargo.toml",
|
||||
"README.md",
|
||||
]
|
||||
description = "Unicode Plural Rules categorizer for numeric input."
|
||||
readme = "README.md"
|
||||
keywords = ["localization", "l10n", "i18n", "intl", "internationalization"]
|
||||
categories = ["localization", "internationalization"]
|
||||
keywords = [
|
||||
"localization",
|
||||
"l10n",
|
||||
"i18n",
|
||||
"intl",
|
||||
"internationalization",
|
||||
]
|
||||
categories = [
|
||||
"localization",
|
||||
"internationalization",
|
||||
]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/zbraniecki/pluralrules"
|
||||
|
||||
[[bench]]
|
||||
name = "pluralrules"
|
||||
harness = false
|
||||
[dependencies.tinystr]
|
||||
version = "0.3.2"
|
||||
|
||||
[dependencies.unic-langid]
|
||||
version = "0.9"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.unic-langid]
|
||||
version = "0.9"
|
||||
features = ["macros"]
|
||||
|
||||
[badges.coveralls]
|
||||
branch = "master"
|
||||
repository = "zbraniecki/pluralrules"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# INTL Plural Rules
|
||||
|
||||
`intl_pluralrules` categorizes numbers by plural operands. See [Unicode Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules)
|
||||
`intl_pluralrules` categorizes numbers by plural operands. See [Unicode Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules)
|
||||
|
||||
|
||||
[![crates.io](http://meritbadge.herokuapp.com/intl_pluralrules)](https://crates.io/crates/intl_pluralrules)
|
||||
[![crates.io](https://img.shields.io/crates/v/intl_pluralrules.svg)](https://crates.io/crates/intl_pluralrules)
|
||||
[![Build Status](https://travis-ci.org/zbraniecki/pluralrules.svg?branch=master)](https://travis-ci.org/zbraniecki/pluralrules)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/zbraniecki/pluralrules/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/pluralrules?branch=master)
|
||||
|
||||
|
@ -37,6 +37,6 @@ Contributors
|
|||
|
||||
Thank you to all contributors!
|
||||
|
||||
[CLDR]: http://cldr.unicode.org/
|
||||
[PluralRules]: http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
[LDML Language Plural Rules Syntax]: http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
|
||||
[CLDR]: https://cldr.unicode.org/
|
||||
[PluralRules]: https://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
[LDML Language Plural Rules Syntax]: https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! A crate for generating plural rule operands from numberical input.
|
||||
//!
|
||||
//! This crate generates plural operands according to the specifications outlined at [Unicode's website](http://unicode.org/reports/tr35/tr35-numbers.html#Operands).
|
||||
//! This crate generates plural operands according to the specifications outlined at [Unicode's website](https://unicode.org/reports/tr35/tr35-numbers.html#Operands).
|
||||
//!
|
||||
//! Input is supported for int, float, and &str.
|
||||
//!
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Plural operands in compliance with [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules).
|
||||
//! Plural operands in compliance with [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules).
|
||||
//!
|
||||
//! See [full operands description](http://unicode.org/reports/tr35/tr35-numbers.html#Operands).
|
||||
//! See [full operands description](https://unicode.org/reports/tr35/tr35-numbers.html#Operands).
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
|
@ -53,7 +53,7 @@ use std::convert::TryFrom;
|
|||
use std::isize;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// A full plural operands representation of a number. See [CLDR Plural Rules](http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description.
|
||||
/// A full plural operands representation of a number. See [CLDR Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules) for complete operands description.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PluralOperands {
|
||||
/// Absolute value of input
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"cb378d2a5c7efd2259cdb7513e1a6bc8bc05b2c5f89b69b69f1f16037495760b","Cargo.lock":"c772766a0c3008c0e528d1ef08c5d8b0e8752a308cb5a782855f691c4b3c223f","Cargo.toml":"af00927b1bd3451ef45685cb34c466566df816aa41f31555c9492c74af597c2c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"7eaffe990ac4432ab388dcda2703f5594e9b1c7a6f753882906b0b8df9876bb4","benches/construct.rs":"9c8652afb0770f60238952b06abb4b90c6cec41d50fd19b760e573eb8dff8e4f","benches/tinystr.rs":"dc61b52ca7be3312a8c504786ad60e5e2c4c729e111c2033716c9996c5e87bc8","examples/main.rs":"463f7c0db47a3843097b012315fc8b0d68baa4afca0a0fe06210ed3c8c471453","src/helpers.rs":"d7768b34dacbb586ade41249147bce1c90d1c08dbfd7c7de6792c88ea45200e1","src/lib.rs":"17740bacc4b05e433f58ddd06d86287097f83024849166e95e8ddb16e491873f","src/tinystr16.rs":"b40595634190ee298bbdcb60b3699b11a25ef2861bccdb567821815ff21fbac3","src/tinystr4.rs":"1787c52e9ec9711b0a115f4c32bee952fb410ce569db4a89fef4c5282bb4f04f","src/tinystr8.rs":"2baf0330c3a20e2e01ff791aa932450d2727d53eb8ed80d76a685bdd824700b6","src/tinystrauto.rs":"55b9333cd8a69e8ea79db72801c4f2b946c5b61108e1ddfb2bf2e06384c41f4a","tests/main.rs":"83b1892f2c5437f99be3ce6d5e98a8df42a5c293447437cc567dcc133a2768d7"},"package":"29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"}
|
||||
{"files":{"Cargo.toml":"279fef44ae26d0bda43cc629cbe22795d696b36635fcb7ca484e74b9d72b9eb4","LICENSE":"4ad7541d66a407234e2c84902124cef325c29f3e966353efdb800bedb8b8da21","README.md":"ce7d4b009ab4878b4b647fa5c01037b4d3e8bf72e44942dbf6cf1ef217d10b4d","benches/common/mod.rs":"7a31f89cb68cec2574287636ac22fe3fc86a66688b8b1e99700a5da692bd485e","benches/construct.rs":"0e0e7c1459dd3efea0c734a999318078b53e18c3389c74a1ff5a226cd3d05cca","benches/overview.rs":"296d19b32a2d52e449140771d89f9c099d19177eb84e1395c942469d51c4c3f8","benches/read.rs":"cbf349393a50eb90e7ba53906f98a689d585242292f867a37acf6842263af4d9","benches/serde.rs":"5c88866d08c07088b82dbd5472e6276c632d11e064417f5d8f2025a5ade867f0","src/ascii.rs":"ca84603237893d515cf4d3cc5bf61470a81c499956b8bdf51239433c0d49785e","src/asciibyte.rs":"fa29de7403c0424c52c2f30bb47002b9abf4bd08b302c411ffe679d3decfb8de","src/databake.rs":"9f29e30e6deec989822cbdf01f5165e098fa544cf7e49ccea3f5de827648fc1e","src/error.rs":"859d03faa3e98d979e0d6b5d232810d42b58f9c6ef69403d442545327053265e","src/int_ops.rs":"c2be314d19dd41cf18fb3589901d7e58ee32fe3f764fb6a66b08a1e005336406","src/lib.rs":"41db27f31650945dbf41b72a21d42fa4de0722b6f0717a45a3569c3dd4f1e148","src/macros.rs":"3fe76e258b0db2896284bcf4f50a4ac35b7efc542649b4c9f13c6e71c5957ae4","src/serde.rs":"0bd6bbe2ee8195aea68dd235d59b94faa3419aaeb8939e3220dd64bd888873f5","src/ule.rs":"139543634949a95405bc49862840b0794db089abed6efe66533858376cae180f","tests/serde.rs":"cf8cee82f731928375888d1b5e7e5e50368d3e16ce372fced230c9b1ee2a7451"},"package":"f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2"}
|
|
@ -1,35 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
- …
|
||||
|
||||
## tinystr 0.3.4 (August 21, 2020)
|
||||
|
||||
- Add `macros` feature which exposes `tinystr::macros`.
|
||||
|
||||
## tinystr 0.3.3 (July 26, 2020)
|
||||
|
||||
- Add `TinyStrAuto`.
|
||||
- Add `no_std` feature.
|
||||
|
||||
## tinystr 0.3.2 (October 28, 2019)
|
||||
|
||||
- Add `from_bytes` method.
|
||||
|
||||
## tinystr 0.3.1 (October 1, 2019)
|
||||
|
||||
- Documentation.
|
||||
|
||||
## tinystr 0.3.1 (October 1, 2019)
|
||||
|
||||
- Documentation.
|
||||
|
||||
## tinystr 0.3.0 (August 23, 2019)
|
||||
|
||||
- Separate out `is_ascii_numeric`, `is_ascii_alphanumeric` and `is_ascii_alphabetic`.
|
||||
|
||||
## tinystr 0.2.0 (August 16, 2019)
|
||||
|
||||
- Add TinyStr16
|
||||
- Add to_ascii_titlecase specialization for all TinyStr*
|
|
@ -1,625 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[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 = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707151f004e8db265b83b1c7509d6c3b4c2c2bc8696113cbe0a8e595c2fdbd3b"
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"tinystr-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c97a45afd04e6bf6d5945895d6982afd9a428e0ebf66585d5e09961d8319ac30"
|
||||
dependencies = [
|
||||
"tinystr 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -3,40 +3,112 @@
|
|||
# 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
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
# 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 = "2018"
|
||||
edition = "2021"
|
||||
name = "tinystr"
|
||||
version = "0.3.4"
|
||||
authors = ["Raph Levien <raph.levien@gmail.com>", "Zibi Braniecki <zibi@braniecki.net>"]
|
||||
description = "A small ASCII-only bounded length string representation.\n"
|
||||
readme = "README.md"
|
||||
keywords = ["string", "str", "small", "tiny", "no_std"]
|
||||
version = "0.7.0"
|
||||
authors = ["The ICU4X Project Developers"]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"examples/**/*",
|
||||
"benches/**/*",
|
||||
"tests/**/*",
|
||||
"Cargo.toml",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
]
|
||||
description = "A small ASCII-only bounded length string representation."
|
||||
keywords = [
|
||||
"string",
|
||||
"str",
|
||||
"small",
|
||||
"tiny",
|
||||
"no_std",
|
||||
]
|
||||
categories = ["data-structures"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/zbraniecki/tinystr"
|
||||
license = "Unicode-DFS-2016"
|
||||
repository = "https://github.com/unicode-org/icu4x"
|
||||
resolver = "2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[[test]]
|
||||
name = "serde"
|
||||
required-features = ["serde"]
|
||||
|
||||
[[bench]]
|
||||
name = "overview"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "construct"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
[[bench]]
|
||||
name = "tinystr"
|
||||
name = "read"
|
||||
harness = false
|
||||
[dependencies.tinystr-macros]
|
||||
required-features = ["bench"]
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = [
|
||||
"bench",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[dependencies.databake]
|
||||
version = "0.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.displaydoc]
|
||||
version = "0.2.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.123"
|
||||
features = ["alloc"]
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.zerovec]
|
||||
version = "0.9"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.bincode]
|
||||
version = "1.3"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.postcard]
|
||||
version = "1.0.0"
|
||||
features = ["use-std"]
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.8.5"
|
||||
features = ["small_rng"]
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1.0"
|
||||
features = ["alloc"]
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.tinystr_old]
|
||||
version = "0.4"
|
||||
features = ["serde"]
|
||||
package = "tinystr"
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
default = ["std"]
|
||||
macros = ["tinystr-macros"]
|
||||
std = []
|
||||
bench = []
|
||||
default = ["alloc"]
|
||||
zerovec = ["dep:zerovec"]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
|
||||
|
||||
See Terms of Use <https://www.unicode.org/copyright.html>
|
||||
for definitions of Unicode Inc.’s Data Files and Software.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement.
|
||||
BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
|
||||
DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("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.
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 1991-2022 Unicode, Inc. All rights reserved.
|
||||
Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Unicode data files and any associated documentation
|
||||
(the "Data Files") or Unicode 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.
|
||||
|
||||
—
|
||||
|
||||
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.
|
|
@ -1,95 +1,53 @@
|
|||
# tinystr [![crates.io](http://meritbadge.herokuapp.com/tinystr)](https://crates.io/crates/tinystr) [![Build Status](https://travis-ci.org/zbraniecki/tinystr.svg?branch=master)](https://travis-ci.org/zbraniecki/tinystr) [![Coverage Status](https://coveralls.io/repos/github/zbraniecki/tinystr/badge.svg?branch=master)](https://coveralls.io/github/zbraniecki/tinystr?branch=master)
|
||||
# tinystr [![crates.io](https://img.shields.io/crates/v/tinystr)](https://crates.io/crates/tinystr)
|
||||
|
||||
`tinystr` is a small ASCII-only bounded length string representation.
|
||||
`tinystr` is a utility crate of the [`ICU4X`] project.
|
||||
|
||||
Usage
|
||||
-----
|
||||
It includes [`TinyAsciiStr`], a core API for representing small ASCII-only bounded length strings.
|
||||
|
||||
It is optimized for operations on strings of size 8 or smaller. When use cases involve comparison
|
||||
and conversion of strings for lowercase/uppercase/titlecase, or checking
|
||||
numeric/alphabetic/alphanumeric, `TinyAsciiStr` is the edge performance library.
|
||||
|
||||
## Examples
|
||||
|
||||
```rust
|
||||
use tinystr::{TinyStr4, TinyStr8, TinyStr16, TinyStrAuto};
|
||||
use tinystr::TinyAsciiStr;
|
||||
|
||||
fn main() {
|
||||
let s1: TinyStr4 = "tEsT".parse()
|
||||
.expect("Failed to parse.");
|
||||
let s1: TinyAsciiStr<4> = "tEsT".parse().expect("Failed to parse.");
|
||||
|
||||
assert_eq!(s1, "tEsT");
|
||||
assert_eq!(s1.to_ascii_uppercase(), "TEST");
|
||||
assert_eq!(s1.to_ascii_lowercase(), "test");
|
||||
assert_eq!(s1.to_ascii_titlecase(), "Test");
|
||||
assert_eq!(s1.is_ascii_alphanumeric(), true);
|
||||
assert_eq!(s1, "tEsT");
|
||||
assert_eq!(s1.to_ascii_uppercase(), "TEST");
|
||||
assert_eq!(s1.to_ascii_lowercase(), "test");
|
||||
assert_eq!(s1.to_ascii_titlecase(), "Test");
|
||||
assert_eq!(s1.is_ascii_alphanumeric(), true);
|
||||
assert_eq!(s1.is_ascii_numeric(), false);
|
||||
|
||||
let s2: TinyStr8 = "New York".parse()
|
||||
.expect("Failed to parse.");
|
||||
let s2 = TinyAsciiStr::<8>::try_from_raw(*b"New York")
|
||||
.expect("Failed to parse.");
|
||||
|
||||
assert_eq!(s2, "New York");
|
||||
assert_eq!(s2.to_ascii_uppercase(), "NEW YORK");
|
||||
assert_eq!(s2.to_ascii_lowercase(), "new york");
|
||||
assert_eq!(s2.to_ascii_titlecase(), "New york");
|
||||
assert_eq!(s2.is_ascii_alphanumeric(), false);
|
||||
|
||||
let s3: TinyStr16 = "metaMoRphosis123".parse()
|
||||
.expect("Failed to parse.");
|
||||
|
||||
assert_eq!(s3, "metaMoRphosis123");
|
||||
assert_eq!(s3.to_ascii_uppercase(), "METAMORPHOSIS123");
|
||||
assert_eq!(s3.to_ascii_lowercase(), "metamorphosis123");
|
||||
assert_eq!(s3.to_ascii_titlecase(), "Metamorphosis123");
|
||||
assert_eq!(s3.is_ascii_alphanumeric(), true);
|
||||
|
||||
let s4: TinyStrAuto = "shortNoAlloc".parse().unwrap();
|
||||
assert!(matches!(s4, TinyStrAuto::Tiny { .. }));
|
||||
assert_eq!(s4, "shortNoAlloc");
|
||||
|
||||
let s5: TinyStrAuto = "longFallbackToHeap".parse().unwrap();
|
||||
assert!(matches!(s4, TinyStrAuto::Heap { .. }));
|
||||
assert_eq!(s4, "shortNoAlloc");
|
||||
}
|
||||
assert_eq!(s2, "New York");
|
||||
assert_eq!(s2.to_ascii_uppercase(), "NEW YORK");
|
||||
assert_eq!(s2.to_ascii_lowercase(), "new york");
|
||||
assert_eq!(s2.to_ascii_titlecase(), "New york");
|
||||
assert_eq!(s2.is_ascii_alphanumeric(), false);
|
||||
```
|
||||
|
||||
Details
|
||||
-------
|
||||
## Details
|
||||
|
||||
The crate provides three structs and an enum:
|
||||
* `TinyStr4` an ASCII-only string limited to 4 characters.
|
||||
* `TinyStr8` an ASCII-only string limited to 8 characters.
|
||||
* `TinyStr16` an ASCII-only string limited to 16 characters.
|
||||
* `TinyStrAuto` (enum):
|
||||
* `Tiny` when the string is 16 characters or less.
|
||||
* `Heap` when the string is 17 or more characters.
|
||||
When strings are of size 8 or smaller, the struct transforms the strings as `u32`/`u64` and uses
|
||||
bitmasking to provide basic string manipulation operations:
|
||||
* `is_ascii_numeric`
|
||||
* `is_ascii_alphabetic`
|
||||
* `is_ascii_alphanumeric`
|
||||
* `to_ascii_lowercase`
|
||||
* `to_ascii_uppercase`
|
||||
* `to_ascii_titlecase`
|
||||
* `PartialEq`
|
||||
|
||||
The structs stores the characters as `u32`/`u64`/`u128` and uses bitmasking to provide basic string manipulation operations:
|
||||
* is_ascii_numeric
|
||||
* is_ascii_alphabetic
|
||||
* is_ascii_alphanumeric
|
||||
* to_ascii_lowercase
|
||||
* to_ascii_uppercase
|
||||
* to_ascii_titlecase
|
||||
* PartialEq
|
||||
`TinyAsciiStr` will fall back to `u8` character manipulation for strings of length greater than 8.
|
||||
|
||||
`TinyStrAuto` stores the string as a TinyStr16 when it is short enough, or else falls back to a standard `String`. You should use TinyStrAuto when you expect most strings to be 16 characters or smaller, but occasionally you receive one that exceeds that length. Unlike the structs, `TinyStrAuto` does not implement `Copy`.
|
||||
[`ICU4X`]: ../icu/index.html
|
||||
|
||||
This set is sufficient for certain classes of uses such as `unic-langid` libraries.
|
||||
## More Information
|
||||
|
||||
no_std
|
||||
------
|
||||
|
||||
Disable the `std` feature of this crate to make it `#[no_std]`. Doing so disables `TinyStrAuto`. You
|
||||
can re-enable `TinyStrAuto` in `#[no_std]` mode by enabling the `alloc` feature.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
For those uses, TinyStr provides [performance characteristics](https://github.com/zbraniecki/tinystr/wiki/Performance) much better than the regular `String`.
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
The crate is fully functional and ready to be used in production.
|
||||
The capabilities can be extended.
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||
</sup
|
||||
For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// 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 file was adapted from parts of https://github.com/zbraniecki/tinystr
|
||||
|
||||
pub static STRINGS_4: &[&str] = &[
|
||||
"US", "GB", "AR", "Hans", "CN", "AT", "PL", "FR", "AT", "Cyrl", "SR", "NO", "FR", "MK", "UK",
|
||||
];
|
||||
|
||||
pub static STRINGS_8: &[&str] = &[
|
||||
"Latn", "windows", "AR", "Hans", "macos", "AT", "pl", "FR", "en", "Cyrl", "SR", "NO", "419",
|
||||
"und", "UK",
|
||||
];
|
||||
|
||||
pub static STRINGS_16: &[&str] = &[
|
||||
"Latn",
|
||||
"windows",
|
||||
"AR",
|
||||
"Hans",
|
||||
"macos",
|
||||
"AT",
|
||||
"infiniband",
|
||||
"FR",
|
||||
"en",
|
||||
"Cyrl",
|
||||
"FromIntegral",
|
||||
"NO",
|
||||
"419",
|
||||
"MacintoshOSX2019",
|
||||
"UK",
|
||||
];
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bench_block {
|
||||
($c:expr, $name:expr, $action:ident) => {
|
||||
let mut group4 = $c.benchmark_group(&format!("{}/4", $name));
|
||||
group4.bench_function("String", $action!(String, STRINGS_4));
|
||||
group4.bench_function("TinyAsciiStr<4>", $action!(TinyAsciiStr<4>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr4",
|
||||
$action!(tinystr_old::TinyStr4, STRINGS_4),
|
||||
);
|
||||
group4.bench_function("TinyAsciiStr<8>", $action!(TinyAsciiStr<8>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr8",
|
||||
$action!(tinystr_old::TinyStr8, STRINGS_4),
|
||||
);
|
||||
group4.bench_function("TinyAsciiStr<16>", $action!(TinyAsciiStr<16>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
$action!(tinystr_old::TinyStr16, STRINGS_4),
|
||||
);
|
||||
group4.finish();
|
||||
|
||||
let mut group8 = $c.benchmark_group(&format!("{}/8", $name));
|
||||
group8.bench_function("String", $action!(String, STRINGS_8));
|
||||
group8.bench_function("TinyAsciiStr<8>", $action!(TinyAsciiStr<8>, STRINGS_8));
|
||||
group8.bench_function("TinyAsciiStr<16>", $action!(TinyAsciiStr<16>, STRINGS_8));
|
||||
group8.bench_function(
|
||||
"tinystr_old::TinyStr8",
|
||||
$action!(tinystr_old::TinyStr8, STRINGS_8),
|
||||
);
|
||||
group8.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
$action!(tinystr_old::TinyStr16, STRINGS_8),
|
||||
);
|
||||
group8.finish();
|
||||
|
||||
let mut group16 = $c.benchmark_group(&format!("{}/16", $name));
|
||||
group16.bench_function("String", $action!(String, STRINGS_16));
|
||||
group16.bench_function("TinyAsciiStr<16>", $action!(TinyAsciiStr<16>, STRINGS_16));
|
||||
group16.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
$action!(tinystr_old::TinyStr16, STRINGS_16),
|
||||
);
|
||||
group16.finish();
|
||||
};
|
||||
}
|
|
@ -1,91 +1,41 @@
|
|||
// 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 file was adapted from https://github.com/zbraniecki/tinystr
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Bencher;
|
||||
use criterion::Criterion;
|
||||
use criterion::Fun;
|
||||
|
||||
use tinystr::{TinyStr16, TinyStr4, TinyStr8, TinyStrAuto};
|
||||
|
||||
static STRINGS_4: &[&str] = &[
|
||||
"US", "GB", "AR", "Hans", "CN", "AT", "PL", "FR", "AT", "Cyrl", "SR", "NO", "FR", "MK", "UK",
|
||||
];
|
||||
|
||||
static STRINGS_8: &[&str] = &[
|
||||
"Latn", "windows", "AR", "Hans", "macos", "AT", "pl", "FR", "en", "Cyrl", "SR", "NO", "419",
|
||||
"und", "UK",
|
||||
];
|
||||
|
||||
static STRINGS_16: &[&str] = &[
|
||||
"Latn",
|
||||
"windows",
|
||||
"AR",
|
||||
"Hans",
|
||||
"macos",
|
||||
"AT",
|
||||
"infiniband",
|
||||
"FR",
|
||||
"en",
|
||||
"Cyrl",
|
||||
"FromIntegral",
|
||||
"NO",
|
||||
"419",
|
||||
"MacintoshOSX2019",
|
||||
"UK",
|
||||
];
|
||||
|
||||
macro_rules! bench_block {
|
||||
($c:expr, $name:expr, $action:ident) => {
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr4", $action!(TinyStr4)),
|
||||
Fun::new("TinyStr8", $action!(TinyStr8)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
Fun::new("TinyStrAuto", $action!(TinyStrAuto)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/4", $name), funcs, STRINGS_4);
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr8", $action!(TinyStr8)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
Fun::new("TinyStrAuto", $action!(TinyStrAuto)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/8", $name), funcs, STRINGS_8);
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
Fun::new("TinyStrAuto", $action!(TinyStrAuto)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/16", $name), funcs, STRINGS_16);
|
||||
};
|
||||
}
|
||||
use tinystr::TinyAsciiStr;
|
||||
|
||||
fn construct_from_str(c: &mut Criterion) {
|
||||
macro_rules! cfs {
|
||||
($r:ty) => {
|
||||
|b: &mut Bencher, strings: &&[&str]| {
|
||||
($r:ty, $inputs:expr) => {
|
||||
|b: &mut Bencher| {
|
||||
b.iter(|| {
|
||||
for s in *strings {
|
||||
for s in $inputs {
|
||||
let _: $r = black_box(s.parse().unwrap());
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "construct_from_str", cfs);
|
||||
}
|
||||
|
||||
fn construct_from_bytes(c: &mut Criterion) {
|
||||
macro_rules! cfu {
|
||||
($r:ty) => {
|
||||
|b, inputs: &&[&str]| {
|
||||
let raw: Vec<&[u8]> = inputs.iter().map(|s| s.as_bytes()).collect();
|
||||
($r:ty, $inputs:expr) => {
|
||||
|b| {
|
||||
let raw: Vec<&[u8]> = $inputs.iter().map(|s| s.as_bytes()).collect();
|
||||
b.iter(move || {
|
||||
for u in &raw {
|
||||
let _ = black_box(<$r>::from_bytes(*u).unwrap());
|
||||
|
@ -93,62 +43,47 @@ fn construct_from_bytes(c: &mut Criterion) {
|
|||
})
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("TinyStr4", cfu!(TinyStr4)),
|
||||
Fun::new("TinyStr8", cfu!(TinyStr8)),
|
||||
Fun::new("TinyStr16", cfu!(TinyStr16)),
|
||||
];
|
||||
let mut group4 = c.benchmark_group("construct_from_bytes/4");
|
||||
group4.bench_function("TinyAsciiStr<4>", cfu!(TinyAsciiStr<4>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr4",
|
||||
cfu!(tinystr_old::TinyStr4, STRINGS_4),
|
||||
);
|
||||
group4.bench_function("TinyAsciiStr<8>", cfu!(TinyAsciiStr<8>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr8",
|
||||
cfu!(tinystr_old::TinyStr8, STRINGS_4),
|
||||
);
|
||||
group4.bench_function("TinyAsciiStr<16>", cfu!(TinyAsciiStr<16>, STRINGS_4));
|
||||
group4.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
cfu!(tinystr_old::TinyStr16, STRINGS_4),
|
||||
);
|
||||
group4.finish();
|
||||
|
||||
c.bench_functions("construct_from_bytes/4", funcs, STRINGS_4);
|
||||
let mut group8 = c.benchmark_group("construct_from_bytes/8");
|
||||
group8.bench_function("TinyAsciiStr<8>", cfu!(TinyAsciiStr<8>, STRINGS_8));
|
||||
group8.bench_function(
|
||||
"tinystr_old::TinyStr8",
|
||||
cfu!(tinystr_old::TinyStr8, STRINGS_8),
|
||||
);
|
||||
group8.bench_function("TinyAsciiStr<16>", cfu!(TinyAsciiStr<16>, STRINGS_8));
|
||||
group8.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
cfu!(tinystr_old::TinyStr16, STRINGS_8),
|
||||
);
|
||||
group8.finish();
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("TinyStr8", cfu!(TinyStr8)),
|
||||
Fun::new("TinyStr16", cfu!(TinyStr16)),
|
||||
];
|
||||
|
||||
c.bench_functions("construct_from_bytes/8", funcs, STRINGS_8);
|
||||
|
||||
let funcs = vec![Fun::new("TinyStr16", cfu!(TinyStr16))];
|
||||
|
||||
c.bench_functions("construct_from_bytes/16", funcs, STRINGS_16);
|
||||
let mut group16 = c.benchmark_group("construct_from_bytes/16");
|
||||
group16.bench_function("TinyAsciiStr<16>", cfu!(TinyAsciiStr<16>, STRINGS_16));
|
||||
group16.bench_function(
|
||||
"tinystr_old::TinyStr16",
|
||||
cfu!(tinystr_old::TinyStr16, STRINGS_16),
|
||||
);
|
||||
group16.finish();
|
||||
}
|
||||
|
||||
fn construct_unchecked(c: &mut Criterion) {
|
||||
macro_rules! cu {
|
||||
($tty:ty, $rty:ty) => {
|
||||
|b, inputs: &&[&str]| {
|
||||
let raw: Vec<$rty> = inputs
|
||||
.iter()
|
||||
.map(|s| s.parse::<$tty>().unwrap().into())
|
||||
.collect();
|
||||
b.iter(move || {
|
||||
for num in &raw {
|
||||
let _ = unsafe { <$tty>::new_unchecked(black_box(*num)) };
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let funcs = vec![Fun::new("TinyStr4", cu!(TinyStr4, u32))];
|
||||
|
||||
c.bench_functions("construct_unchecked/4", funcs, STRINGS_4);
|
||||
|
||||
let funcs = vec![Fun::new("TinyStr8", cu!(TinyStr8, u64))];
|
||||
|
||||
c.bench_functions("construct_unchecked/8", funcs, STRINGS_8);
|
||||
|
||||
let funcs = vec![Fun::new("TinyStr16", cu!(TinyStr16, u128))];
|
||||
|
||||
c.bench_functions("construct_unchecked/16", funcs, STRINGS_16);
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
construct_from_str,
|
||||
construct_from_bytes,
|
||||
construct_unchecked,
|
||||
);
|
||||
criterion_group!(benches, construct_from_str, construct_from_bytes,);
|
||||
criterion_main!(benches);
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
// 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 ).
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use tinystr::TinyAsciiStr;
|
||||
use tinystr_old::TinyStr16;
|
||||
use tinystr_old::TinyStr4;
|
||||
use tinystr_old::TinyStr8;
|
||||
|
||||
fn overview(c: &mut Criterion) {
|
||||
let mut g = c.benchmark_group("overview");
|
||||
|
||||
g.bench_function("construct/TinyAsciiStr", |b| {
|
||||
b.iter(|| {
|
||||
for s in STRINGS_4 {
|
||||
let _: TinyAsciiStr<4> = black_box(s).parse().unwrap();
|
||||
let _: TinyAsciiStr<8> = black_box(s).parse().unwrap();
|
||||
let _: TinyAsciiStr<16> = black_box(s).parse().unwrap();
|
||||
}
|
||||
for s in STRINGS_8 {
|
||||
let _: TinyAsciiStr<8> = black_box(s).parse().unwrap();
|
||||
let _: TinyAsciiStr<16> = black_box(s).parse().unwrap();
|
||||
}
|
||||
for s in STRINGS_16 {
|
||||
let _: TinyAsciiStr<16> = black_box(s).parse().unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
g.bench_function("construct/TinyStr", |b| {
|
||||
b.iter(|| {
|
||||
for s in STRINGS_4 {
|
||||
let _: TinyStr4 = black_box(s).parse().unwrap();
|
||||
let _: TinyStr8 = black_box(s).parse().unwrap();
|
||||
let _: TinyStr16 = black_box(s).parse().unwrap();
|
||||
}
|
||||
for s in STRINGS_8 {
|
||||
let _: TinyStr8 = black_box(s).parse().unwrap();
|
||||
let _: TinyStr16 = black_box(s).parse().unwrap();
|
||||
}
|
||||
for s in STRINGS_16 {
|
||||
let _: TinyStr16 = black_box(s).parse().unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let parsed_ascii_4: Vec<TinyAsciiStr<4>> = STRINGS_4
|
||||
.iter()
|
||||
.map(|s| s.parse::<TinyAsciiStr<4>>().unwrap())
|
||||
.collect();
|
||||
let parsed_ascii_8: Vec<TinyAsciiStr<8>> = STRINGS_4
|
||||
.iter()
|
||||
.chain(STRINGS_8)
|
||||
.map(|s| s.parse::<TinyAsciiStr<8>>().unwrap())
|
||||
.collect();
|
||||
let parsed_ascii_16: Vec<TinyAsciiStr<16>> = STRINGS_4
|
||||
.iter()
|
||||
.chain(STRINGS_8)
|
||||
.chain(STRINGS_16)
|
||||
.map(|s| s.parse::<TinyAsciiStr<16>>().unwrap())
|
||||
.collect();
|
||||
|
||||
let parsed_tiny_4: Vec<TinyStr4> = STRINGS_4
|
||||
.iter()
|
||||
.map(|s| s.parse::<TinyStr4>().unwrap())
|
||||
.collect();
|
||||
let parsed_tiny_8: Vec<TinyStr8> = STRINGS_4
|
||||
.iter()
|
||||
.chain(STRINGS_8)
|
||||
.map(|s| s.parse::<TinyStr8>().unwrap())
|
||||
.collect();
|
||||
let parsed_tiny_16: Vec<TinyStr16> = STRINGS_4
|
||||
.iter()
|
||||
.chain(STRINGS_8)
|
||||
.chain(STRINGS_16)
|
||||
.map(|s| s.parse::<TinyStr16>().unwrap())
|
||||
.collect();
|
||||
|
||||
g.bench_function("read/TinyAsciiStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut collector: usize = 0;
|
||||
for t in black_box(&parsed_ascii_4) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
for t in black_box(&parsed_ascii_8) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
for t in black_box(&parsed_ascii_16) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
collector
|
||||
});
|
||||
});
|
||||
|
||||
g.bench_function("read/TinyStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut collector: usize = 0;
|
||||
for t in black_box(&parsed_tiny_4) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
for t in black_box(&parsed_tiny_8) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
for t in black_box(&parsed_tiny_16) {
|
||||
let s: &str = t;
|
||||
collector += s.bytes().map(usize::from).sum::<usize>();
|
||||
}
|
||||
collector
|
||||
});
|
||||
});
|
||||
|
||||
g.bench_function("compare/TinyAsciiStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut collector: usize = 0;
|
||||
for ts in black_box(&parsed_ascii_4).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
for ts in black_box(&parsed_ascii_8).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
for ts in black_box(&parsed_ascii_16).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
collector
|
||||
});
|
||||
});
|
||||
|
||||
g.bench_function("compare/TinyStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut collector: usize = 0;
|
||||
for ts in black_box(&parsed_tiny_4).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
for ts in black_box(&parsed_tiny_8).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
for ts in black_box(&parsed_tiny_16).windows(2) {
|
||||
let o = ts[0].cmp(&ts[1]);
|
||||
collector ^= o as usize;
|
||||
}
|
||||
collector
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, overview,);
|
||||
criterion_main!(benches);
|
|
@ -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 ).
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Bencher;
|
||||
use criterion::Criterion;
|
||||
|
||||
use tinystr::TinyAsciiStr;
|
||||
|
||||
fn read(c: &mut Criterion) {
|
||||
macro_rules! cfs {
|
||||
($r:ty, $inputs:expr) => {
|
||||
|b: &mut Bencher| {
|
||||
let parsed: Vec<$r> = $inputs.iter().map(|s| s.parse().unwrap()).collect();
|
||||
b.iter(|| {
|
||||
for s in &parsed {
|
||||
let _: &str = black_box(&**s);
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "read", cfs);
|
||||
}
|
||||
|
||||
criterion_group!(benches, read,);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,37 @@
|
|||
// 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 ).
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Bencher;
|
||||
use criterion::Criterion;
|
||||
|
||||
use tinystr::TinyAsciiStr;
|
||||
|
||||
fn deserialize(c: &mut Criterion) {
|
||||
macro_rules! cfs {
|
||||
($r:ty, $inputs:expr) => {
|
||||
|b: &mut Bencher| {
|
||||
let serialized: Vec<Vec<u8>> = $inputs
|
||||
.iter()
|
||||
.map(|s| postcard::to_stdvec(&s.parse::<$r>().unwrap()).unwrap())
|
||||
.collect();
|
||||
b.iter(|| {
|
||||
for bytes in &serialized {
|
||||
let _: Result<$r, _> = black_box(postcard::from_bytes(bytes));
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "deserialize", cfs);
|
||||
}
|
||||
|
||||
criterion_group!(benches, deserialize,);
|
||||
criterion_main!(benches);
|
|
@ -1,176 +0,0 @@
|
|||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Bencher;
|
||||
use criterion::Criterion;
|
||||
use criterion::Fun;
|
||||
|
||||
use tinystr::{TinyStr16, TinyStr4, TinyStr8};
|
||||
|
||||
static STRINGS_4: &[&str] = &[
|
||||
"US", "GB", "AR", "Hans", "CN", "AT", "PL", "FR", "AT", "Cyrl", "SR", "NO", "FR", "MK", "UK",
|
||||
];
|
||||
|
||||
static STRINGS_8: &[&str] = &[
|
||||
"Latn", "windows", "AR", "Hans", "macos", "AT", "pl", "FR", "en", "Cyrl", "SR", "NO", "419",
|
||||
"und", "UK",
|
||||
];
|
||||
|
||||
static STRINGS_16: &[&str] = &[
|
||||
"Latn",
|
||||
"windows",
|
||||
"AR",
|
||||
"Hans",
|
||||
"macos",
|
||||
"AT",
|
||||
"infiniband",
|
||||
"FR",
|
||||
"en",
|
||||
"Cyrl",
|
||||
"FromIntegral",
|
||||
"NO",
|
||||
"419",
|
||||
"MacintoshOSX2019",
|
||||
"UK",
|
||||
];
|
||||
|
||||
macro_rules! bench_block {
|
||||
($c:expr, $name:expr, $action:ident) => {
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr4", $action!(TinyStr4)),
|
||||
Fun::new("TinyStr8", $action!(TinyStr8)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/4", $name), funcs, STRINGS_4);
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr8", $action!(TinyStr8)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/8", $name), funcs, STRINGS_8);
|
||||
|
||||
let funcs = vec![
|
||||
Fun::new("String", $action!(String)),
|
||||
Fun::new("TinyStr16", $action!(TinyStr16)),
|
||||
];
|
||||
|
||||
$c.bench_functions(&format!("{}/16", $name), funcs, STRINGS_16);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! convert_to_ascii {
|
||||
($ty:ty, $action:ident) => {
|
||||
|b: &mut Bencher, inputs: &&[&str]| {
|
||||
let raw: Vec<$ty> = inputs.iter().map(|s| s.parse::<$ty>().unwrap()).collect();
|
||||
b.iter(move || {
|
||||
for s in &raw {
|
||||
let _ = black_box(s.$action());
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn convert_to_ascii_lowercase(c: &mut Criterion) {
|
||||
macro_rules! ctal {
|
||||
($ty:ty) => {
|
||||
convert_to_ascii!($ty, to_ascii_lowercase)
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "convert_to_ascii_lowercase", ctal);
|
||||
}
|
||||
|
||||
fn convert_to_ascii_uppercase(c: &mut Criterion) {
|
||||
macro_rules! ctau {
|
||||
($ty:ty) => {
|
||||
convert_to_ascii!($ty, to_ascii_uppercase)
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "convert_to_ascii_uppercase", ctau);
|
||||
}
|
||||
|
||||
trait ExtToAsciiTitlecase {
|
||||
#[inline(always)]
|
||||
fn to_ascii_titlecase(&self) -> String;
|
||||
}
|
||||
|
||||
impl ExtToAsciiTitlecase for str {
|
||||
fn to_ascii_titlecase(&self) -> String {
|
||||
let mut result = self.to_ascii_lowercase();
|
||||
result[0..1].make_ascii_uppercase();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_ascii_titlecase(c: &mut Criterion) {
|
||||
macro_rules! ctat {
|
||||
($ty:ty) => {
|
||||
convert_to_ascii!($ty, to_ascii_titlecase)
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "convert_to_ascii_titlecase", ctat);
|
||||
}
|
||||
|
||||
trait ExtIsAsciiAlphanumeric {
|
||||
#[inline(always)]
|
||||
fn is_ascii_alphanumeric(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ExtIsAsciiAlphanumeric for str {
|
||||
fn is_ascii_alphanumeric(&self) -> bool {
|
||||
self.chars().all(|c| c.is_ascii_alphanumeric())
|
||||
}
|
||||
}
|
||||
|
||||
fn test_is_ascii_alphanumeric(c: &mut Criterion) {
|
||||
macro_rules! tiaa {
|
||||
($ty:ty) => {
|
||||
|b: &mut Bencher, inputs: &&[&str]| {
|
||||
let raw: Vec<$ty> = inputs.iter().map(|s| s.parse::<$ty>().unwrap()).collect();
|
||||
b.iter(move || {
|
||||
for s in &raw {
|
||||
let _ = black_box(s.is_ascii_alphanumeric());
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "test_is_ascii_alphanumeric", tiaa);
|
||||
}
|
||||
|
||||
fn test_eq(c: &mut Criterion) {
|
||||
macro_rules! te {
|
||||
($ty:ty) => {
|
||||
|b: &mut Bencher, inputs: &&[&str]| {
|
||||
let raw: Vec<$ty> = inputs.iter().map(|s| s.parse::<$ty>().unwrap()).collect();
|
||||
b.iter(move || {
|
||||
for s in &raw {
|
||||
for l in &raw {
|
||||
let _ = black_box(s == l);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bench_block!(c, "test_eq", te);
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
convert_to_ascii_lowercase,
|
||||
convert_to_ascii_uppercase,
|
||||
convert_to_ascii_titlecase,
|
||||
test_is_ascii_alphanumeric,
|
||||
test_eq,
|
||||
);
|
||||
criterion_main!(benches);
|
|
@ -1,18 +0,0 @@
|
|||
use tinystr::{TinyStr4, TinyStr8};
|
||||
|
||||
fn main() {
|
||||
let s1: TinyStr4 = "tEsT".parse().expect("Failed to parse.");
|
||||
|
||||
assert_eq!(s1, "tEsT");
|
||||
assert_eq!(s1.to_ascii_uppercase(), "TEST");
|
||||
assert_eq!(s1.to_ascii_lowercase(), "test");
|
||||
assert_eq!(s1.to_ascii_titlecase(), "Test");
|
||||
assert_eq!(s1.is_ascii_alphanumeric(), true);
|
||||
|
||||
let s2: TinyStr8 = "New York".parse().expect("Failed to parse.");
|
||||
|
||||
assert_eq!(s2, "New York");
|
||||
assert_eq!(s2.to_ascii_uppercase(), "NEW YORK");
|
||||
assert_eq!(s2.to_ascii_lowercase(), "new york");
|
||||
assert_eq!(s2.is_ascii_alphanumeric(), false);
|
||||
}
|
|
@ -0,0 +1,987 @@
|
|||
// 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::asciibyte::AsciiByte;
|
||||
use crate::int_ops::{Aligned4, Aligned8};
|
||||
use crate::TinyStrError;
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use core::str::{self, FromStr};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash)]
|
||||
pub struct TinyAsciiStr<const N: usize> {
|
||||
bytes: [AsciiByte; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> TinyAsciiStr<N> {
|
||||
/// Creates a `TinyAsciiStr<N>` from the given byte slice.
|
||||
/// `bytes` may contain at most `N` non-null ASCII bytes.
|
||||
pub const fn from_bytes(bytes: &[u8]) -> Result<Self, TinyStrError> {
|
||||
Self::from_bytes_inner(bytes, 0, bytes.len(), false)
|
||||
}
|
||||
|
||||
/// Attempts to parse a fixed-length byte array to a `TinyAsciiStr`.
|
||||
///
|
||||
/// The byte array may contain trailing NUL bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::tinystr;
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// TinyAsciiStr::<3>::try_from_raw(*b"GB\0"),
|
||||
/// Ok(tinystr!(3, "GB"))
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// TinyAsciiStr::<3>::try_from_raw(*b"USD"),
|
||||
/// Ok(tinystr!(3, "USD"))
|
||||
/// );
|
||||
/// assert!(matches!(TinyAsciiStr::<3>::try_from_raw(*b"\0A\0"), Err(_)));
|
||||
/// ```
|
||||
pub const fn try_from_raw(raw: [u8; N]) -> Result<Self, TinyStrError> {
|
||||
Self::from_bytes_inner(&raw, 0, N, true)
|
||||
}
|
||||
|
||||
/// Equivalent to [`from_bytes(bytes[start..end])`](Self::from_bytes),
|
||||
/// but callable in a `const` context (which range indexing is not).
|
||||
pub const fn from_bytes_manual_slice(
|
||||
bytes: &[u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
) -> Result<Self, TinyStrError> {
|
||||
Self::from_bytes_inner(bytes, start, end, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) const fn from_bytes_inner(
|
||||
bytes: &[u8],
|
||||
start: usize,
|
||||
end: usize,
|
||||
allow_trailing_null: bool,
|
||||
) -> Result<Self, TinyStrError> {
|
||||
let len = end - start;
|
||||
if len > N {
|
||||
return Err(TinyStrError::TooLarge { max: N, len });
|
||||
}
|
||||
|
||||
let mut out = [0; N];
|
||||
let mut i = 0;
|
||||
let mut found_null = false;
|
||||
// Indexing is protected by TinyStrError::TooLarge
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < len {
|
||||
let b = bytes[start + i];
|
||||
|
||||
if b == 0 {
|
||||
found_null = true;
|
||||
} else if b >= 0x80 {
|
||||
return Err(TinyStrError::NonAscii);
|
||||
} else if found_null {
|
||||
// Error if there are contentful bytes after null
|
||||
return Err(TinyStrError::ContainsNull);
|
||||
}
|
||||
out[i] = b;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if !allow_trailing_null && found_null {
|
||||
// We found some trailing nulls, error
|
||||
return Err(TinyStrError::ContainsNull);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
// SAFETY: `out` only contains ASCII bytes and has same size as `self.bytes`
|
||||
bytes: unsafe { AsciiByte::to_ascii_byte_array(&out) },
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: This function shadows the FromStr trait. Rename?
|
||||
#[inline]
|
||||
pub const fn from_str(s: &str) -> Result<Self, TinyStrError> {
|
||||
Self::from_bytes_inner(s.as_bytes(), 0, s.len(), false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_str(&self) -> &str {
|
||||
// as_bytes is valid utf8
|
||||
unsafe { str::from_utf8_unchecked(self.as_bytes()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
if N <= 4 {
|
||||
Aligned4::from_ascii_bytes(&self.bytes).len()
|
||||
} else if N <= 8 {
|
||||
Aligned8::from_ascii_bytes(&self.bytes).len()
|
||||
} else {
|
||||
let mut i = 0;
|
||||
#[allow(clippy::indexing_slicing)] // < N is safe
|
||||
while i < N && self.bytes[i] as u8 != AsciiByte::B0 as u8 {
|
||||
i += 1
|
||||
}
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.bytes[0] as u8 == AsciiByte::B0 as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_bytes(&self) -> &[u8] {
|
||||
/// core::slice::from_raw_parts(a, b) = core::mem::transmute((a, b)) hack
|
||||
/// ```compile_fail
|
||||
/// const unsafe fn canary() { core::slice::from_raw_parts(0 as *const u8, 0); }
|
||||
/// ```
|
||||
const _: () = ();
|
||||
// Safe because `self.bytes.as_slice()` pointer-casts to `&[u8]`,
|
||||
// and changing the length of that slice to self.len() < N is safe.
|
||||
unsafe { core::mem::transmute((self.bytes.as_slice().as_ptr(), self.len())) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn all_bytes(&self) -> &[u8; N] {
|
||||
// SAFETY: `self.bytes` has same size as [u8; N]
|
||||
unsafe { core::mem::transmute(&self.bytes) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
/// Resizes a TinyAsciiStr<N> to a TinyAsciiStr<M>.
|
||||
///
|
||||
/// If M < len() the string gets truncated, otherwise only the
|
||||
/// memory representation changes.
|
||||
pub const fn resize<const M: usize>(self) -> TinyAsciiStr<M> {
|
||||
let mut bytes = [0; M];
|
||||
let mut i = 0;
|
||||
// Indexing is protected by the loop guard
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < M && i < N {
|
||||
bytes[i] = self.bytes[i] as u8;
|
||||
i += 1;
|
||||
}
|
||||
// `self.bytes` only contains ASCII bytes, with no null bytes between
|
||||
// ASCII characters, so this also holds for `bytes`.
|
||||
unsafe { TinyAsciiStr::from_bytes_unchecked(bytes) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Must be called with a bytes array made of valid ASCII bytes, with no null bytes
|
||||
/// between ASCII characters
|
||||
#[must_use]
|
||||
pub const unsafe fn from_bytes_unchecked(bytes: [u8; N]) -> Self {
|
||||
Self {
|
||||
bytes: AsciiByte::to_ascii_byte_array(&bytes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_is {
|
||||
($self:ident, $check_int:ident, $check_u8:ident) => {
|
||||
if N <= 4 {
|
||||
Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else if N <= 8 {
|
||||
Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else {
|
||||
let mut i = 0;
|
||||
// Won't panic because self.bytes has length N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
|
||||
if !($self.bytes[i] as u8).$check_u8() {
|
||||
return false;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
($self:ident, $check_int:ident, !$check_u8_0_inv:ident, !$check_u8_1_inv:ident) => {
|
||||
if N <= 4 {
|
||||
Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else if N <= 8 {
|
||||
Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else {
|
||||
// Won't panic because N is > 8
|
||||
if ($self.bytes[0] as u8).$check_u8_0_inv() {
|
||||
return false;
|
||||
}
|
||||
let mut i = 1;
|
||||
// Won't panic because self.bytes has length N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
|
||||
if ($self.bytes[i] as u8).$check_u8_1_inv() {
|
||||
return false;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
($self:ident, $check_int:ident, $check_u8_0_inv:ident, $check_u8_1_inv:ident) => {
|
||||
if N <= 4 {
|
||||
Aligned4::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else if N <= 8 {
|
||||
Aligned8::from_ascii_bytes(&$self.bytes).$check_int()
|
||||
} else {
|
||||
// Won't panic because N is > 8
|
||||
if !($self.bytes[0] as u8).$check_u8_0_inv() {
|
||||
return false;
|
||||
}
|
||||
let mut i = 1;
|
||||
// Won't panic because self.bytes has length N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
|
||||
if !($self.bytes[i] as u8).$check_u8_1_inv() {
|
||||
return false;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<const N: usize> TinyAsciiStr<N> {
|
||||
/// Checks if the value is composed of ASCII alphabetic characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// assert!(!s2.is_ascii_alphabetic());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_alphabetic(&self) -> bool {
|
||||
check_is!(self, is_ascii_alphabetic, is_ascii_alphabetic)
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphanumeric characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z', or
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "A15b".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "[3@w".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphanumeric());
|
||||
/// assert!(!s2.is_ascii_alphanumeric());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_alphanumeric(&self) -> bool {
|
||||
check_is!(self, is_ascii_alphanumeric, is_ascii_alphanumeric)
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII decimal digits:
|
||||
///
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "312".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "3d".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_numeric());
|
||||
/// assert!(!s2.is_ascii_numeric());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_numeric(&self) -> bool {
|
||||
check_is!(self, is_ascii_numeric, is_ascii_digit)
|
||||
}
|
||||
|
||||
/// Checks if the value is in ASCII lower case.
|
||||
///
|
||||
/// All letter characters are checked for case. Non-letter characters are ignored.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(!s1.is_ascii_lowercase());
|
||||
/// assert!(s2.is_ascii_lowercase());
|
||||
/// assert!(s3.is_ascii_lowercase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_lowercase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_lowercase,
|
||||
!is_ascii_uppercase,
|
||||
!is_ascii_uppercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the value is in ASCII title case.
|
||||
///
|
||||
/// This verifies that the first character is ASCII uppercase and all others ASCII lowercase.
|
||||
/// Non-letter characters are ignored.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(!s1.is_ascii_titlecase());
|
||||
/// assert!(s2.is_ascii_titlecase());
|
||||
/// assert!(s3.is_ascii_titlecase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_titlecase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_titlecase,
|
||||
!is_ascii_lowercase,
|
||||
!is_ascii_uppercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the value is in ASCII upper case.
|
||||
///
|
||||
/// All letter characters are checked for case. Non-letter characters are ignored.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "TEST".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(!s1.is_ascii_uppercase());
|
||||
/// assert!(s2.is_ascii_uppercase());
|
||||
/// assert!(!s3.is_ascii_uppercase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_uppercase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_uppercase,
|
||||
!is_ascii_lowercase,
|
||||
!is_ascii_lowercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic lower case characters:
|
||||
///
|
||||
/// * U+0061 'a' ..= U+007A 'z',
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s4: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
|
||||
/// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(!s1.is_ascii_alphabetic_lowercase());
|
||||
/// assert!(!s2.is_ascii_alphabetic_lowercase());
|
||||
/// assert!(!s3.is_ascii_alphabetic_lowercase());
|
||||
/// assert!(s4.is_ascii_alphabetic_lowercase());
|
||||
/// assert!(!s5.is_ascii_alphabetic_lowercase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_alphabetic_lowercase,
|
||||
is_ascii_lowercase,
|
||||
is_ascii_lowercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic, with the first character being ASCII uppercase, and all others ASCII lowercase.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s4: TinyAsciiStr<4> = "test".parse().expect("Failed to parse.");
|
||||
/// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphabetic_titlecase());
|
||||
/// assert!(!s2.is_ascii_alphabetic_titlecase());
|
||||
/// assert!(!s3.is_ascii_alphabetic_titlecase());
|
||||
/// assert!(!s4.is_ascii_alphabetic_titlecase());
|
||||
/// assert!(!s5.is_ascii_alphabetic_titlecase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_alphabetic_titlecase,
|
||||
is_ascii_uppercase,
|
||||
is_ascii_lowercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic upper case characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z',
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "Test".parse().expect("Failed to parse.");
|
||||
/// let s2: TinyAsciiStr<4> = "Te3t".parse().expect("Failed to parse.");
|
||||
/// let s3: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
/// let s4: TinyAsciiStr<4> = "TEST".parse().expect("Failed to parse.");
|
||||
/// let s5: TinyAsciiStr<4> = "001z".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(!s1.is_ascii_alphabetic_uppercase());
|
||||
/// assert!(!s2.is_ascii_alphabetic_uppercase());
|
||||
/// assert!(!s3.is_ascii_alphabetic_uppercase());
|
||||
/// assert!(s4.is_ascii_alphabetic_uppercase());
|
||||
/// assert!(!s5.is_ascii_alphabetic_uppercase());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
|
||||
check_is!(
|
||||
self,
|
||||
is_ascii_alphabetic_uppercase,
|
||||
is_ascii_uppercase,
|
||||
is_ascii_uppercase
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! to {
|
||||
($self:ident, $to:ident, $later_char_to:ident $(,$first_char_to:ident)?) => {{
|
||||
let mut i = 0;
|
||||
if N <= 4 {
|
||||
let aligned = Aligned4::from_ascii_bytes(&$self.bytes).$to().to_ascii_bytes();
|
||||
// Won't panic because self.bytes has length N and aligned has length >= N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N {
|
||||
$self.bytes[i] = aligned[i];
|
||||
i += 1;
|
||||
}
|
||||
} else if N <= 8 {
|
||||
let aligned = Aligned8::from_ascii_bytes(&$self.bytes).$to().to_ascii_bytes();
|
||||
// Won't panic because self.bytes has length N and aligned has length >= N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N {
|
||||
$self.bytes[i] = aligned[i];
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
// Won't panic because self.bytes has length N
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N && $self.bytes[i] as u8 != AsciiByte::B0 as u8 {
|
||||
// SAFETY: AsciiByte is repr(u8) and has same size as u8
|
||||
unsafe {
|
||||
$self.bytes[i] = core::mem::transmute(
|
||||
($self.bytes[i] as u8).$later_char_to()
|
||||
);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
// SAFETY: AsciiByte is repr(u8) and has same size as u8
|
||||
$(
|
||||
$self.bytes[0] = unsafe {
|
||||
core::mem::transmute(($self.bytes[0] as u8).$first_char_to())
|
||||
};
|
||||
)?
|
||||
}
|
||||
$self
|
||||
}};
|
||||
}
|
||||
|
||||
impl<const N: usize> TinyAsciiStr<N> {
|
||||
/// Converts this type to its ASCII lower case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "TeS3".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(&*s1.to_ascii_lowercase(), "tes3");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_ascii_lowercase(mut self) -> Self {
|
||||
to!(self, to_ascii_lowercase, to_ascii_lowercase)
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII title case equivalent in-place.
|
||||
///
|
||||
/// The first character is converted to ASCII uppercase; the remaining characters
|
||||
/// are converted to ASCII lowercase.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "teSt".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(&*s1.to_ascii_titlecase(), "Test");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_ascii_titlecase(mut self) -> Self {
|
||||
to!(
|
||||
self,
|
||||
to_ascii_titlecase,
|
||||
to_ascii_lowercase,
|
||||
to_ascii_uppercase
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII upper case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyAsciiStr;
|
||||
///
|
||||
/// let s1: TinyAsciiStr<4> = "Tes3".parse().expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(&*s1.to_ascii_uppercase(), "TES3");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_ascii_uppercase(mut self) -> Self {
|
||||
to!(self, to_ascii_uppercase, to_ascii_uppercase)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for TinyAsciiStr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Display for TinyAsciiStr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for TinyAsciiStr<N> {
|
||||
type Target = str;
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromStr for TinyAsciiStr<N> {
|
||||
type Err = TinyStrError;
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, TinyStrError> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<str> for TinyAsciiStr<N> {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.deref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<&str> for TinyAsciiStr<N> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.deref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<const N: usize> PartialEq<alloc::string::String> for TinyAsciiStr<N> {
|
||||
fn eq(&self, other: &alloc::string::String) -> bool {
|
||||
self.deref() == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<const N: usize> PartialEq<TinyAsciiStr<N>> for alloc::string::String {
|
||||
fn eq(&self, other: &TinyAsciiStr<N>) -> bool {
|
||||
self.deref() == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rand::distributions::Distribution;
|
||||
use rand::distributions::Standard;
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::SeedableRng;
|
||||
|
||||
const STRINGS: &[&str] = &[
|
||||
"Latn",
|
||||
"laTn",
|
||||
"windows",
|
||||
"AR",
|
||||
"Hans",
|
||||
"macos",
|
||||
"AT",
|
||||
"infiniband",
|
||||
"FR",
|
||||
"en",
|
||||
"Cyrl",
|
||||
"FromIntegral",
|
||||
"NO",
|
||||
"419",
|
||||
"MacintoshOSX2019",
|
||||
"a3z",
|
||||
"A3z",
|
||||
"A3Z",
|
||||
"a3Z",
|
||||
"3A",
|
||||
"3Z",
|
||||
"3a",
|
||||
"3z",
|
||||
"@@[`{",
|
||||
"UK",
|
||||
"E12",
|
||||
];
|
||||
|
||||
fn gen_strings(num_strings: usize, allowed_lengths: &[usize]) -> Vec<String> {
|
||||
let mut rng = SmallRng::seed_from_u64(2022);
|
||||
// Need to do this in 2 steps since the RNG is needed twice
|
||||
let string_lengths = core::iter::repeat_with(|| *allowed_lengths.choose(&mut rng).unwrap())
|
||||
.take(num_strings)
|
||||
.collect::<Vec<usize>>();
|
||||
string_lengths
|
||||
.iter()
|
||||
.map(|len| {
|
||||
Standard
|
||||
.sample_iter(&mut rng)
|
||||
.filter(|b: &u8| *b > 0 && *b < 0x80)
|
||||
.take(*len)
|
||||
.collect::<Vec<u8>>()
|
||||
})
|
||||
.map(|byte_vec| String::from_utf8(byte_vec).expect("All ASCII"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn check_operation<T, F1, F2, const N: usize>(reference_f: F1, tinystr_f: F2)
|
||||
where
|
||||
F1: Fn(&str) -> T,
|
||||
F2: Fn(TinyAsciiStr<N>) -> T,
|
||||
T: core::fmt::Debug + core::cmp::PartialEq,
|
||||
{
|
||||
for s in STRINGS
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.chain(gen_strings(100, &[3, 4, 5, 8, 12]))
|
||||
{
|
||||
let t = match TinyAsciiStr::<N>::from_str(&s) {
|
||||
Ok(t) => t,
|
||||
Err(TinyStrError::TooLarge { .. }) => continue,
|
||||
Err(e) => panic!("{}", e),
|
||||
};
|
||||
let expected = reference_f(&s);
|
||||
let actual = tinystr_f(t);
|
||||
assert_eq!(expected, actual, "TinyAsciiStr<{}>: {:?}", N, s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_alphabetic() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| s.chars().all(|c| c.is_ascii_alphabetic()),
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_alphanumeric() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| s.chars().all(|c| c.is_ascii_alphanumeric()),
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphanumeric(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_numeric() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| s.chars().all(|c| c.is_ascii_digit()),
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_numeric(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_lowercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_lowercase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_lowercase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_titlecase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_titlecase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_uppercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_uppercase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_uppercase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_alphabetic_lowercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
// Check alphabetic
|
||||
s.chars().all(|c| c.is_ascii_alphabetic()) &&
|
||||
// Check lowercase
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_lowercase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_lowercase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_alphabetic_titlecase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
// Check alphabetic
|
||||
s.chars().all(|c| c.is_ascii_alphabetic()) &&
|
||||
// Check titlecase
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_titlecase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_ascii_alphabetic_uppercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
// Check alphabetic
|
||||
s.chars().all(|c| c.is_ascii_alphabetic()) &&
|
||||
// Check uppercase
|
||||
s == TinyAsciiStr::<16>::from_str(s)
|
||||
.unwrap()
|
||||
.to_ascii_uppercase()
|
||||
.as_str()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::is_ascii_alphabetic_uppercase(&t),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_ascii_lowercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
s.chars()
|
||||
.map(|c| c.to_ascii_lowercase())
|
||||
.collect::<String>()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_lowercase(t).to_string(),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_ascii_titlecase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
let mut r = s
|
||||
.chars()
|
||||
.map(|c| c.to_ascii_lowercase())
|
||||
.collect::<String>();
|
||||
// Safe because the string is nonempty and an ASCII string
|
||||
unsafe { r.as_bytes_mut()[0].make_ascii_uppercase() };
|
||||
r
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_titlecase(t).to_string(),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_ascii_uppercase() {
|
||||
fn check<const N: usize>() {
|
||||
check_operation(
|
||||
|s| {
|
||||
s.chars()
|
||||
.map(|c| c.to_ascii_uppercase())
|
||||
.collect::<String>()
|
||||
},
|
||||
|t: TinyAsciiStr<N>| TinyAsciiStr::to_ascii_uppercase(t).to_string(),
|
||||
)
|
||||
}
|
||||
check::<2>();
|
||||
check::<3>();
|
||||
check::<4>();
|
||||
check::<5>();
|
||||
check::<8>();
|
||||
check::<16>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// 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 ).
|
||||
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash)]
|
||||
pub enum AsciiByte {
|
||||
B0 = 0,
|
||||
B1 = 1,
|
||||
B2 = 2,
|
||||
B3 = 3,
|
||||
B4 = 4,
|
||||
B5 = 5,
|
||||
B6 = 6,
|
||||
B7 = 7,
|
||||
B8 = 8,
|
||||
B9 = 9,
|
||||
B10 = 10,
|
||||
B11 = 11,
|
||||
B12 = 12,
|
||||
B13 = 13,
|
||||
B14 = 14,
|
||||
B15 = 15,
|
||||
B16 = 16,
|
||||
B17 = 17,
|
||||
B18 = 18,
|
||||
B19 = 19,
|
||||
B20 = 20,
|
||||
B21 = 21,
|
||||
B22 = 22,
|
||||
B23 = 23,
|
||||
B24 = 24,
|
||||
B25 = 25,
|
||||
B26 = 26,
|
||||
B27 = 27,
|
||||
B28 = 28,
|
||||
B29 = 29,
|
||||
B30 = 30,
|
||||
B31 = 31,
|
||||
B32 = 32,
|
||||
B33 = 33,
|
||||
B34 = 34,
|
||||
B35 = 35,
|
||||
B36 = 36,
|
||||
B37 = 37,
|
||||
B38 = 38,
|
||||
B39 = 39,
|
||||
B40 = 40,
|
||||
B41 = 41,
|
||||
B42 = 42,
|
||||
B43 = 43,
|
||||
B44 = 44,
|
||||
B45 = 45,
|
||||
B46 = 46,
|
||||
B47 = 47,
|
||||
B48 = 48,
|
||||
B49 = 49,
|
||||
B50 = 50,
|
||||
B51 = 51,
|
||||
B52 = 52,
|
||||
B53 = 53,
|
||||
B54 = 54,
|
||||
B55 = 55,
|
||||
B56 = 56,
|
||||
B57 = 57,
|
||||
B58 = 58,
|
||||
B59 = 59,
|
||||
B60 = 60,
|
||||
B61 = 61,
|
||||
B62 = 62,
|
||||
B63 = 63,
|
||||
B64 = 64,
|
||||
B65 = 65,
|
||||
B66 = 66,
|
||||
B67 = 67,
|
||||
B68 = 68,
|
||||
B69 = 69,
|
||||
B70 = 70,
|
||||
B71 = 71,
|
||||
B72 = 72,
|
||||
B73 = 73,
|
||||
B74 = 74,
|
||||
B75 = 75,
|
||||
B76 = 76,
|
||||
B77 = 77,
|
||||
B78 = 78,
|
||||
B79 = 79,
|
||||
B80 = 80,
|
||||
B81 = 81,
|
||||
B82 = 82,
|
||||
B83 = 83,
|
||||
B84 = 84,
|
||||
B85 = 85,
|
||||
B86 = 86,
|
||||
B87 = 87,
|
||||
B88 = 88,
|
||||
B89 = 89,
|
||||
B90 = 90,
|
||||
B91 = 91,
|
||||
B92 = 92,
|
||||
B93 = 93,
|
||||
B94 = 94,
|
||||
B95 = 95,
|
||||
B96 = 96,
|
||||
B97 = 97,
|
||||
B98 = 98,
|
||||
B99 = 99,
|
||||
B100 = 100,
|
||||
B101 = 101,
|
||||
B102 = 102,
|
||||
B103 = 103,
|
||||
B104 = 104,
|
||||
B105 = 105,
|
||||
B106 = 106,
|
||||
B107 = 107,
|
||||
B108 = 108,
|
||||
B109 = 109,
|
||||
B110 = 110,
|
||||
B111 = 111,
|
||||
B112 = 112,
|
||||
B113 = 113,
|
||||
B114 = 114,
|
||||
B115 = 115,
|
||||
B116 = 116,
|
||||
B117 = 117,
|
||||
B118 = 118,
|
||||
B119 = 119,
|
||||
B120 = 120,
|
||||
B121 = 121,
|
||||
B122 = 122,
|
||||
B123 = 123,
|
||||
B124 = 124,
|
||||
B125 = 125,
|
||||
B126 = 126,
|
||||
B127 = 127,
|
||||
}
|
||||
|
||||
impl AsciiByte {
|
||||
// Convert [u8; N] to [AsciiByte; N]
|
||||
#[inline]
|
||||
pub const unsafe fn to_ascii_byte_array<const N: usize>(bytes: &[u8; N]) -> [AsciiByte; N] {
|
||||
*(bytes as *const [u8; N] as *const [AsciiByte; N])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// 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::TinyAsciiStr;
|
||||
use databake::*;
|
||||
|
||||
impl<const N: usize> Bake for TinyAsciiStr<N> {
|
||||
fn bake(&self, env: &CrateEnv) -> TokenStream {
|
||||
env.insert("tinystr");
|
||||
let string = self.as_str();
|
||||
quote! {
|
||||
::tinystr::tinystr!(#N, #string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
test_bake!(TinyAsciiStr<10>, const: crate::tinystr!(10usize, "foo"), tinystr);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// 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 displaydoc::Display;
|
||||
|
||||
#[derive(Display, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum TinyStrError {
|
||||
#[displaydoc("found string of larger length {len} when constructing string of length {max}")]
|
||||
TooLarge { max: usize, len: usize },
|
||||
#[displaydoc("tinystr types do not support strings with null bytes")]
|
||||
ContainsNull,
|
||||
#[displaydoc("attempted to construct TinyStrAuto from a non-ascii string")]
|
||||
NonAscii,
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
use std::num::NonZeroU32;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
use super::Error;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use std::string::String;
|
||||
|
||||
#[cfg(all(not(feature = "std"), not(test)))]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(not(feature = "std"), not(test)))]
|
||||
pub use alloc::string::String;
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn make_4byte_bytes(
|
||||
bytes: &[u8],
|
||||
len: usize,
|
||||
mask: u32,
|
||||
) -> Result<NonZeroU32, Error> {
|
||||
// Mask is always supplied as little-endian.
|
||||
let mask = u32::from_le(mask);
|
||||
let mut word: u32 = 0;
|
||||
copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u32 as *mut u8, len);
|
||||
if (word & mask) != 0 {
|
||||
return Err(Error::NonAscii);
|
||||
}
|
||||
if ((mask - word) & mask) != 0 {
|
||||
return Err(Error::InvalidNull);
|
||||
}
|
||||
Ok(NonZeroU32::new_unchecked(word))
|
||||
}
|
|
@ -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 crate::asciibyte::AsciiByte;
|
||||
|
||||
/// Internal helper struct that performs operations on aligned integers.
|
||||
/// Supports strings up to 4 bytes long.
|
||||
#[repr(transparent)]
|
||||
pub struct Aligned4(u32);
|
||||
|
||||
impl Aligned4 {
|
||||
/// # Panics
|
||||
/// Panics if N is greater than 4
|
||||
#[inline]
|
||||
pub const fn from_bytes<const N: usize>(src: &[u8; N]) -> Self {
|
||||
let mut bytes = [0; 4];
|
||||
let mut i = 0;
|
||||
// The function documentation defines when panics may occur
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N {
|
||||
bytes[i] = src[i];
|
||||
i += 1;
|
||||
}
|
||||
Self(u32::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn from_ascii_bytes<const N: usize>(src: &[AsciiByte; N]) -> Self {
|
||||
Self::from_bytes::<N>(unsafe { core::mem::transmute(src) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn to_bytes(&self) -> [u8; 4] {
|
||||
self.0.to_ne_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn to_ascii_bytes(&self) -> [AsciiByte; 4] {
|
||||
unsafe { core::mem::transmute(self.to_bytes()) }
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> usize {
|
||||
let word = self.0;
|
||||
#[cfg(target_endian = "little")]
|
||||
let len = (4 - word.leading_zeros() / 8) as usize;
|
||||
#[cfg(target_endian = "big")]
|
||||
let len = (4 - word.trailing_zeros() / 8) as usize;
|
||||
len
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic(&self) -> bool {
|
||||
let word = self.0;
|
||||
// Each of the following bitmasks set *the high bit* (0x8) to 0 for valid and 1 for invalid.
|
||||
// `mask` sets all NUL bytes to 0.
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
// `lower` converts the string to lowercase. It may also change the value of non-alpha
|
||||
// characters, but this does not matter for the alphabetic test that follows.
|
||||
let lower = word | 0x2020_2020;
|
||||
// `alpha` sets all alphabetic bytes to 0. We only need check for lowercase characters.
|
||||
let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
|
||||
// The overall string is valid if every character passes at least one test.
|
||||
// We performed two tests here: non-NUL (`mask`) and alphabetic (`alpha`).
|
||||
(alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphanumeric(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
|
||||
let lower = word | 0x2020_2020;
|
||||
let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
|
||||
(alpha & numeric & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_numeric(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
|
||||
(numeric & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_lowercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// For efficiency, this function tests for an invalid string rather than a valid string.
|
||||
// A string is ASCII lowercase iff it contains no uppercase ASCII characters.
|
||||
// `invalid_case` sets all uppercase ASCII characters to 0 and all others to 1.
|
||||
let invalid_case = !(word + 0x3f3f_3f3f) | (word + 0x2525_2525);
|
||||
// The string is valid if it contains no invalid characters (if all high bits are 1).
|
||||
(invalid_case & 0x8080_8080) == 0x8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_titlecase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_lowercase
|
||||
let invalid_case = if cfg!(target_endian = "little") {
|
||||
!(word + 0x3f3f_3f1f) | (word + 0x2525_2505)
|
||||
} else {
|
||||
!(word + 0x1f3f_3f3f) | (word + 0x0525_2525)
|
||||
};
|
||||
(invalid_case & 0x8080_8080) == 0x8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_uppercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_lowercase
|
||||
let invalid_case = !(word + 0x1f1f_1f1f) | (word + 0x0505_0505);
|
||||
(invalid_case & 0x8080_8080) == 0x8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// `mask` sets all NUL bytes to 0.
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
// `lower_alpha` sets all lowercase ASCII characters to 0 and all others to 1.
|
||||
let lower_alpha = !(word + 0x1f1f_1f1f) | (word + 0x0505_0505);
|
||||
// The overall string is valid if every character passes at least one test.
|
||||
// We performed two tests here: non-NUL (`mask`) and lowercase ASCII character (`alpha`).
|
||||
(lower_alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic_lowercase
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let title_case = if cfg!(target_endian = "little") {
|
||||
!(word + 0x1f1f_1f3f) | (word + 0x0505_0525)
|
||||
} else {
|
||||
!(word + 0x3f1f_1f1f) | (word + 0x2505_0505)
|
||||
};
|
||||
(title_case & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic_lowercase
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let upper_alpha = !(word + 0x3f3f_3f3f) | (word + 0x2525_2525);
|
||||
(upper_alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn to_ascii_lowercase(&self) -> Self {
|
||||
let word = self.0;
|
||||
let result = word | (((word + 0x3f3f_3f3f) & !(word + 0x2525_2525) & 0x8080_8080) >> 2);
|
||||
Self(result)
|
||||
}
|
||||
|
||||
pub const fn to_ascii_titlecase(&self) -> Self {
|
||||
let word = self.0.to_le();
|
||||
let mask = ((word + 0x3f3f_3f1f) & !(word + 0x2525_2505) & 0x8080_8080) >> 2;
|
||||
let result = (word | mask) & !(0x20 & mask);
|
||||
Self(u32::from_le(result))
|
||||
}
|
||||
|
||||
pub const fn to_ascii_uppercase(&self) -> Self {
|
||||
let word = self.0;
|
||||
let result = word & !(((word + 0x1f1f_1f1f) & !(word + 0x0505_0505) & 0x8080_8080) >> 2);
|
||||
Self(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal helper struct that performs operations on aligned integers.
|
||||
/// Supports strings up to 8 bytes long.
|
||||
#[repr(transparent)]
|
||||
pub struct Aligned8(u64);
|
||||
|
||||
impl Aligned8 {
|
||||
/// # Panics
|
||||
/// Panics if N is greater than 8
|
||||
#[inline]
|
||||
pub const fn from_bytes<const N: usize>(src: &[u8; N]) -> Self {
|
||||
let mut bytes = [0; 8];
|
||||
let mut i = 0;
|
||||
// The function documentation defines when panics may occur
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
while i < N {
|
||||
bytes[i] = src[i];
|
||||
i += 1;
|
||||
}
|
||||
Self(u64::from_ne_bytes(bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn from_ascii_bytes<const N: usize>(src: &[AsciiByte; N]) -> Self {
|
||||
Self::from_bytes::<N>(unsafe { core::mem::transmute(src) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn to_bytes(&self) -> [u8; 8] {
|
||||
self.0.to_ne_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn to_ascii_bytes(&self) -> [AsciiByte; 8] {
|
||||
unsafe { core::mem::transmute(self.to_bytes()) }
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> usize {
|
||||
let word = self.0;
|
||||
#[cfg(target_endian = "little")]
|
||||
let len = (8 - word.leading_zeros() / 8) as usize;
|
||||
#[cfg(target_endian = "big")]
|
||||
let len = (8 - word.trailing_zeros() / 8) as usize;
|
||||
len
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic(&self) -> bool {
|
||||
let word = self.0;
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
let lower = word | 0x2020_2020_2020_2020;
|
||||
let alpha = !(lower + 0x1f1f_1f1f_1f1f_1f1f) | (lower + 0x0505_0505_0505_0505);
|
||||
(alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphanumeric(&self) -> bool {
|
||||
let word = self.0;
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
let numeric = !(word + 0x5050_5050_5050_5050) | (word + 0x4646_4646_4646_4646);
|
||||
let lower = word | 0x2020_2020_2020_2020;
|
||||
let alpha = !(lower + 0x1f1f_1f1f_1f1f_1f1f) | (lower + 0x0505_0505_0505_0505);
|
||||
(alpha & numeric & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_numeric(&self) -> bool {
|
||||
let word = self.0;
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
let numeric = !(word + 0x5050_5050_5050_5050) | (word + 0x4646_4646_4646_4646);
|
||||
(numeric & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_lowercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
let invalid_case = !(word + 0x3f3f_3f3f_3f3f_3f3f) | (word + 0x2525_2525_2525_2525);
|
||||
(invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_titlecase(&self) -> bool {
|
||||
let word = self.0;
|
||||
let invalid_case = if cfg!(target_endian = "little") {
|
||||
!(word + 0x3f3f_3f3f_3f3f_3f1f) | (word + 0x2525_2525_2525_2505)
|
||||
} else {
|
||||
!(word + 0x1f3f_3f3f_3f3f_3f3f) | (word + 0x0525_2525_2525_2525)
|
||||
};
|
||||
(invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_uppercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
let invalid_case = !(word + 0x1f1f_1f1f_1f1f_1f1f) | (word + 0x0505_0505_0505_0505);
|
||||
(invalid_case & 0x8080_8080_8080_8080) == 0x8080_8080_8080_8080
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_lowercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// `mask` sets all NUL bytes to 0.
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
// `lower_alpha` sets all lowercase ASCII characters to 0 and all others to 1.
|
||||
let lower_alpha = !(word + 0x1f1f_1f1f_1f1f_1f1f) | (word + 0x0505_0505_0505_0505);
|
||||
// The overall string is valid if every character passes at least one test.
|
||||
// We performed two tests here: non-NUL (`mask`) and lowercase ASCII character (`alpha`).
|
||||
(lower_alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_titlecase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic_lowercase
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
let title_case = if cfg!(target_endian = "little") {
|
||||
!(word + 0x1f1f_1f1f_1f1f_1f3f) | (word + 0x0505_0505_0505_0525)
|
||||
} else {
|
||||
!(word + 0x3f1f_1f1f_1f1f_1f1f) | (word + 0x2505_0505_0505_0505)
|
||||
};
|
||||
(title_case & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn is_ascii_alphabetic_uppercase(&self) -> bool {
|
||||
let word = self.0;
|
||||
// See explanatory comments in is_ascii_alphabetic_lowercase
|
||||
let mask = (word + 0x7f7f_7f7f_7f7f_7f7f) & 0x8080_8080_8080_8080;
|
||||
let upper_alpha = !(word + 0x3f3f_3f3f_3f3f_3f3f) | (word + 0x2525_2525_2525_2525);
|
||||
(upper_alpha & mask) == 0
|
||||
}
|
||||
|
||||
pub const fn to_ascii_lowercase(&self) -> Self {
|
||||
let word = self.0;
|
||||
let result = word
|
||||
| (((word + 0x3f3f_3f3f_3f3f_3f3f)
|
||||
& !(word + 0x2525_2525_2525_2525)
|
||||
& 0x8080_8080_8080_8080)
|
||||
>> 2);
|
||||
Self(result)
|
||||
}
|
||||
|
||||
pub const fn to_ascii_titlecase(&self) -> Self {
|
||||
let word = self.0.to_le();
|
||||
let mask = ((word + 0x3f3f_3f3f_3f3f_3f1f)
|
||||
& !(word + 0x2525_2525_2525_2505)
|
||||
& 0x8080_8080_8080_8080)
|
||||
>> 2;
|
||||
let result = (word | mask) & !(0x20 & mask);
|
||||
Self(u64::from_le(result))
|
||||
}
|
||||
|
||||
pub const fn to_ascii_uppercase(&self) -> Self {
|
||||
let word = self.0;
|
||||
let result = word
|
||||
& !(((word + 0x1f1f_1f1f_1f1f_1f1f)
|
||||
& !(word + 0x0505_0505_0505_0505)
|
||||
& 0x8080_8080_8080_8080)
|
||||
>> 2);
|
||||
Self(result)
|
||||
}
|
||||
}
|
|
@ -1,105 +1,116 @@
|
|||
//! `tinystr` is a small ASCII-only bounded length string representation.
|
||||
// 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 ).
|
||||
|
||||
//! `tinystr` is a utility crate of the [`ICU4X`] project.
|
||||
//!
|
||||
//! The crate is meant to be used for scenarios where one needs a fast
|
||||
//! and memory efficient way to store and manipulate short ASCII-only strings.
|
||||
//! It includes [`TinyAsciiStr`], a core API for representing small ASCII-only bounded length strings.
|
||||
//!
|
||||
//! `tinystr` converts each string into an unsigned integer, and uses bitmasking
|
||||
//! to compare, convert cases and test for common characteristics of strings.
|
||||
//!
|
||||
//! # Details
|
||||
//! It is optimized for operations on strings of size 8 or smaller. When use cases involve comparison
|
||||
//! and conversion of strings for lowercase/uppercase/titlecase, or checking
|
||||
//! numeric/alphabetic/alphanumeric, `TinyAsciiStr` is the edge performance library.
|
||||
//!
|
||||
//! The crate provides three structs and an enum:
|
||||
//! * `TinyStr4` an ASCII-only string limited to 4 characters.
|
||||
//! * `TinyStr8` an ASCII-only string limited to 8 characters.
|
||||
//! * `TinyStr16` an ASCII-only string limited to 16 characters.
|
||||
//! * `TinyStrAuto` (enum):
|
||||
//! * `Tiny` when the string is 16 characters or less.
|
||||
//! * `Heap` when the string is 17 or more characters.
|
||||
//! # Examples
|
||||
//!
|
||||
//! `TinyStrAuto` stores the string as a TinyStr16 when it is short enough, or else falls back to a
|
||||
//! standard `String`. You should use TinyStrAuto when you expect most strings to be 16 characters
|
||||
//! or smaller, but occasionally you receive one that exceeds that length. Unlike the structs,
|
||||
//! `TinyStrAuto` does not implement `Copy`.
|
||||
//! ```rust
|
||||
//! use tinystr::TinyAsciiStr;
|
||||
//!
|
||||
//! # no_std
|
||||
//!
|
||||
//! Disable the `std` feature of this crate to make it `#[no_std]`. Doing so disables `TinyStrAuto`.
|
||||
//! You can re-enable `TinyStrAuto` in `#[no_std]` mode by enabling the `alloc` feature.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use tinystr::{TinyStr4, TinyStr8, TinyStr16, TinyStrAuto};
|
||||
//!
|
||||
//! let s1: TinyStr4 = "tEsT".parse()
|
||||
//! .expect("Failed to parse.");
|
||||
//! let s1: TinyAsciiStr<4> = "tEsT".parse().expect("Failed to parse.");
|
||||
//!
|
||||
//! assert_eq!(s1, "tEsT");
|
||||
//! assert_eq!(s1.to_ascii_uppercase(), "TEST");
|
||||
//! assert_eq!(s1.to_ascii_lowercase(), "test");
|
||||
//! assert_eq!(s1.to_ascii_titlecase(), "Test");
|
||||
//! assert_eq!(s1.is_ascii_alphanumeric(), true);
|
||||
//! assert_eq!(s1.is_ascii_numeric(), false);
|
||||
//!
|
||||
//! let s2: TinyStr8 = "New York".parse()
|
||||
//! .expect("Failed to parse.");
|
||||
//! let s2 = TinyAsciiStr::<8>::try_from_raw(*b"New York")
|
||||
//! .expect("Failed to parse.");
|
||||
//!
|
||||
//! assert_eq!(s2, "New York");
|
||||
//! assert_eq!(s2.to_ascii_uppercase(), "NEW YORK");
|
||||
//! assert_eq!(s2.to_ascii_lowercase(), "new york");
|
||||
//! assert_eq!(s2.to_ascii_titlecase(), "New york");
|
||||
//! assert_eq!(s2.is_ascii_alphanumeric(), false);
|
||||
//!
|
||||
//! let s3: TinyStr16 = "metaMoRphosis123".parse()
|
||||
//! .expect("Failed to parse.");
|
||||
//!
|
||||
//! assert_eq!(s3, "metaMoRphosis123");
|
||||
//! assert_eq!(s3.to_ascii_uppercase(), "METAMORPHOSIS123");
|
||||
//! assert_eq!(s3.to_ascii_lowercase(), "metamorphosis123");
|
||||
//! assert_eq!(s3.to_ascii_titlecase(), "Metamorphosis123");
|
||||
//! assert_eq!(s3.is_ascii_alphanumeric(), true);
|
||||
//!
|
||||
//! let s4: TinyStrAuto = "shortNoAlloc".parse().unwrap();
|
||||
//! assert!(matches!(s4, TinyStrAuto::Tiny { .. }));
|
||||
//! assert_eq!(s4, "shortNoAlloc");
|
||||
//!
|
||||
//! let s5: TinyStrAuto = "longFallbackToHeap".parse().unwrap();
|
||||
//! assert!(matches!(s5, TinyStrAuto::Heap { .. }));
|
||||
//! assert_eq!(s5, "longFallbackToHeap");
|
||||
//! ```
|
||||
//!
|
||||
//! # Details
|
||||
//!
|
||||
//! When strings are of size 8 or smaller, the struct transforms the strings as `u32`/`u64` and uses
|
||||
//! bitmasking to provide basic string manipulation operations:
|
||||
//! * `is_ascii_numeric`
|
||||
//! * `is_ascii_alphabetic`
|
||||
//! * `is_ascii_alphanumeric`
|
||||
//! * `to_ascii_lowercase`
|
||||
//! * `to_ascii_uppercase`
|
||||
//! * `to_ascii_titlecase`
|
||||
//! * `PartialEq`
|
||||
//!
|
||||
//! `TinyAsciiStr` will fall back to `u8` character manipulation for strings of length greater than 8.
|
||||
|
||||
#![no_std]
|
||||
//!
|
||||
//! [`ICU4X`]: ../icu/index.html
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(
|
||||
not(test),
|
||||
deny(
|
||||
clippy::indexing_slicing,
|
||||
clippy::unwrap_used,
|
||||
clippy::expect_used,
|
||||
clippy::panic,
|
||||
clippy::exhaustive_structs,
|
||||
clippy::exhaustive_enums,
|
||||
missing_debug_implementations,
|
||||
)
|
||||
)]
|
||||
|
||||
#[cfg(all(not(feature = "std"), not(test)))]
|
||||
extern crate core as std;
|
||||
mod macros;
|
||||
|
||||
mod helpers;
|
||||
mod tinystr16;
|
||||
mod tinystr4;
|
||||
mod tinystr8;
|
||||
mod ascii;
|
||||
mod asciibyte;
|
||||
mod error;
|
||||
mod int_ops;
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
mod tinystrauto;
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde;
|
||||
|
||||
pub use tinystr16::TinyStr16;
|
||||
pub use tinystr4::TinyStr4;
|
||||
pub use tinystr8::TinyStr8;
|
||||
#[cfg(feature = "databake")]
|
||||
mod databake;
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub use tinystrauto::TinyStrAuto;
|
||||
#[cfg(feature = "zerovec")]
|
||||
mod ule;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
pub use tinystr_macros as macros;
|
||||
#[cfg(any(feature = "serde", feature = "alloc"))]
|
||||
extern crate alloc;
|
||||
|
||||
/// Enum to store the various types of errors that can cause parsing a TinyStr to fail.
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
/// String is too large or too small to store as TinyStr.
|
||||
InvalidSize,
|
||||
/// String is empty.
|
||||
InvalidNull,
|
||||
/// String contains non-ASCII character(s).
|
||||
NonAscii,
|
||||
pub use ascii::TinyAsciiStr;
|
||||
pub use error::TinyStrError;
|
||||
|
||||
/// These are temporary compatability reexports that will be removed
|
||||
/// in a future version.
|
||||
pub type TinyStr4 = TinyAsciiStr<4>;
|
||||
/// These are temporary compatability reexports that will be removed
|
||||
/// in a future version.
|
||||
pub type TinyStr8 = TinyAsciiStr<8>;
|
||||
/// These are temporary compatability reexports that will be removed
|
||||
/// in a future version.
|
||||
pub type TinyStr16 = TinyAsciiStr<16>;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert_eq!(
|
||||
core::mem::size_of::<TinyStr4>(),
|
||||
core::mem::size_of::<Option<TinyStr4>>()
|
||||
);
|
||||
assert_eq!(
|
||||
core::mem::size_of::<TinyStr8>(),
|
||||
core::mem::size_of::<Option<TinyStr8>>()
|
||||
);
|
||||
}
|
||||
// /// Allows unit tests to use the macro
|
||||
// #[cfg(test)]
|
||||
// mod tinystr {
|
||||
// pub use super::{TinyAsciiStr, TinyStrError};
|
||||
// }
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// 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 ).
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! tinystr {
|
||||
($n:literal, $s:literal) => {{
|
||||
// Force it into a const context; otherwise it may get evaluated at runtime instead.
|
||||
const TINYSTR_MACRO_CONST: $crate::TinyAsciiStr<$n> = {
|
||||
match $crate::TinyAsciiStr::from_bytes($s.as_bytes()) {
|
||||
Ok(s) => s,
|
||||
// We are okay with panicking here because this is in a const context
|
||||
#[allow(clippy::panic)]
|
||||
// Cannot format the error since formatting isn't const yet
|
||||
Err(_) => panic!(concat!("Failed to construct tinystr from ", $s)),
|
||||
}
|
||||
};
|
||||
TINYSTR_MACRO_CONST
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_macro_construction() {
|
||||
let s1 = tinystr!(8, "foobar");
|
||||
assert_eq!(&*s1, "foobar");
|
||||
|
||||
let s1 = tinystr!(12, "foobarbaz");
|
||||
assert_eq!(&*s1, "foobarbaz");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// 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::TinyAsciiStr;
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::string::ToString;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Deref;
|
||||
use serde::de::{Error, SeqAccess, Visitor};
|
||||
use serde::ser::SerializeTuple;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
impl<const N: usize> Serialize for TinyAsciiStr<N> {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
self.deref().serialize(serializer)
|
||||
} else {
|
||||
let mut seq = serializer.serialize_tuple(N)?;
|
||||
for byte in self.all_bytes() {
|
||||
seq.serialize_element(byte)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TinyAsciiStrVisitor<const N: usize> {
|
||||
marker: PhantomData<TinyAsciiStr<N>>,
|
||||
}
|
||||
|
||||
impl<const N: usize> TinyAsciiStrVisitor<N> {
|
||||
fn new() -> Self {
|
||||
TinyAsciiStrVisitor {
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> Visitor<'de> for TinyAsciiStrVisitor<N> {
|
||||
type Value = TinyAsciiStr<N>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a TinyAsciiStr<{}>", N)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; N];
|
||||
let mut zeroes = false;
|
||||
for out in &mut bytes.iter_mut().take(N) {
|
||||
let byte = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| Error::invalid_length(N, &self))?;
|
||||
if byte == 0 {
|
||||
zeroes = true;
|
||||
} else if zeroes {
|
||||
return Err(Error::custom("TinyAsciiStr cannot contain null bytes"));
|
||||
}
|
||||
|
||||
if byte >= 0x80 {
|
||||
return Err(Error::custom("TinyAsciiStr cannot contain non-ascii bytes"));
|
||||
}
|
||||
*out = byte;
|
||||
}
|
||||
|
||||
Ok(unsafe { TinyAsciiStr::from_bytes_unchecked(bytes) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> Deserialize<'de> for TinyAsciiStr<N> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
let x: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
|
||||
TinyAsciiStr::from_str(&x).map_err(|e| Error::custom(e.to_string()))
|
||||
} else {
|
||||
deserializer.deserialize_tuple(N, TinyAsciiStrVisitor::<N>::new())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,327 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::convert::Into;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU128;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// A tiny string that is from 1 to 16 non-NUL ASCII characters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "Metamorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Metamorphosis");
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TinyStr16(NonZeroU128);
|
||||
|
||||
impl TinyStr16 {
|
||||
/// Creates a TinyStr16 from a byte slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1 = TinyStr16::from_bytes("Testing".as_bytes())
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Testing");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
let len = bytes.len();
|
||||
if len < 1 || len > 16 {
|
||||
return Err(Error::InvalidSize);
|
||||
}
|
||||
unsafe {
|
||||
let mut word: u128 = 0;
|
||||
copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u128 as *mut u8, len);
|
||||
let mask = 0x80808080_80808080_80808080_80808080u128 >> (8 * (16 - len));
|
||||
// TODO: could do this with #cfg(target_endian), but this is clearer and
|
||||
// more confidence-inspiring.
|
||||
let mask = u128::from_le(mask);
|
||||
if (word & mask) != 0 {
|
||||
return Err(Error::NonAscii);
|
||||
}
|
||||
if ((mask - word) & mask) != 0 {
|
||||
return Err(Error::InvalidNull);
|
||||
}
|
||||
Ok(Self(NonZeroU128::new_unchecked(word)))
|
||||
}
|
||||
}
|
||||
|
||||
/// An unsafe constructor intended for cases where the consumer
|
||||
/// guarantees that the input is a little endian integer which
|
||||
/// is a correct representation of a `TinyStr16` string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "Metamorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// let num: u128 = s1.into();
|
||||
///
|
||||
/// let s2 = unsafe { TinyStr16::new_unchecked(num) };
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
/// assert_eq!(s2.as_str(), "Metamorphosis");
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The method does not validate the `u128` to be properly encoded
|
||||
/// value for `TinyStr16`.
|
||||
/// The value can be retrieved via `Into<u128> for TinyStr16`.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new_unchecked(text: u128) -> Self {
|
||||
Self(NonZeroU128::new_unchecked(u128::from_le(text)))
|
||||
}
|
||||
|
||||
/// Extracts a string slice containing the entire `TinyStr16`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "Metamorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.as_str(), "Metamorphosis");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "Metamorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr16 = "Met3mo4pho!is".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// assert!(!s2.is_ascii_alphabetic());
|
||||
/// ```
|
||||
pub fn is_ascii_alphabetic(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask =
|
||||
(word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
|
||||
let lower = word | 0x20202020_20202020_20202020_20202020;
|
||||
let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
|
||||
| (lower + 0x05050505_05050505_05050505_05050505);
|
||||
(alpha & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphanumeric characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z', or
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "A15bingA1".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr16 = "[3@w00Fs1".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphanumeric());
|
||||
/// assert!(!s2.is_ascii_alphanumeric());
|
||||
/// ```
|
||||
pub fn is_ascii_alphanumeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask =
|
||||
(word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
|
||||
let numeric = !(word + 0x50505050_50505050_50505050_50505050)
|
||||
| (word + 0x46464646_46464646_46464646_46464646);
|
||||
let lower = word | 0x20202020_20202020_20202020_20202020;
|
||||
let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
|
||||
| (lower + 0x05050505_05050505_05050505_05050505);
|
||||
(alpha & numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII decimal digits:
|
||||
///
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "31212314141".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr16 = "3d3d3d3d".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_numeric());
|
||||
/// assert!(!s2.is_ascii_numeric());
|
||||
/// ```
|
||||
pub fn is_ascii_numeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask =
|
||||
(word + 0x7f7f7f7f_7f7f7f7f_7f7f7f7f_7f7f7f7f) & 0x80808080_80808080_80808080_80808080;
|
||||
let numeric = !(word + 0x50505050_50505050_50505050_50505050)
|
||||
| (word + 0x46464646_46464646_46464646_46464646);
|
||||
(numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII lower case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "MeTAmOrpHo3sis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_lowercase(), "metamorpho3sis");
|
||||
/// ```
|
||||
pub fn to_ascii_lowercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word
|
||||
| (((word + 0x3f3f3f3f_3f3f3f3f_3f3f3f3f_3f3f3f3f)
|
||||
& !(word + 0x25252525_25252525_25252525_25252525)
|
||||
& 0x80808080_80808080_80808080_80808080)
|
||||
>> 2);
|
||||
unsafe { Self(NonZeroU128::new_unchecked(result)) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII title case equivalent in-place.
|
||||
///
|
||||
/// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
|
||||
/// other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "metamorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_titlecase(), "Metamorphosis");
|
||||
/// ```
|
||||
pub fn to_ascii_titlecase(self) -> Self {
|
||||
let word = self.0.get().to_le();
|
||||
let mask = ((word + 0x3f3f3f3f_3f3f3f3f_3f3f3f3f_3f3f3f1f)
|
||||
& !(word + 0x25252525_25252525_25252525_25252505)
|
||||
& 0x80808080_80808080_80808080_80808080)
|
||||
>> 2;
|
||||
let result = (word | mask) & !(0x20 & mask);
|
||||
unsafe { Self(NonZeroU128::new_unchecked(u128::from_le(result))) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII upper case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr16;
|
||||
///
|
||||
/// let s1: TinyStr16 = "Met3amorphosis".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_uppercase(), "MET3AMORPHOSIS");
|
||||
/// ```
|
||||
pub fn to_ascii_uppercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word
|
||||
& !(((word + 0x1f1f1f1f_1f1f1f1f_1f1f1f1f_1f1f1f1f)
|
||||
& !(word + 0x05050505_05050505_05050505_05050505)
|
||||
& 0x80808080_80808080_80808080_80808080)
|
||||
>> 2);
|
||||
unsafe { Self(NonZeroU128::new_unchecked(result)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TinyStr16 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TinyStr16 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TinyStr16 {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
// Again, could use #cfg to hand-roll a big-endian implementation.
|
||||
let word = self.0.get().to_le();
|
||||
let len = (16 - word.leading_zeros() / 8) as usize;
|
||||
unsafe {
|
||||
let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
|
||||
std::str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for TinyStr16 {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.deref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TinyStr16 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TinyStr16 {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.get().to_be().cmp(&other.0.get().to_be())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TinyStr16 {
|
||||
type Err = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
Self::from_bytes(text.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u128> for TinyStr16 {
|
||||
fn into(self) -> u128 {
|
||||
self.0.get().to_le()
|
||||
}
|
||||
}
|
|
@ -1,299 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::convert::Into;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::helpers::make_4byte_bytes;
|
||||
use crate::Error;
|
||||
|
||||
/// A tiny string that is from 1 to 4 non-NUL ASCII characters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "Test".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Test");
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TinyStr4(NonZeroU32);
|
||||
|
||||
impl TinyStr4 {
|
||||
/// Creates a TinyStr4 from a byte slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1 = TinyStr4::from_bytes("Test".as_bytes())
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Test");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
unsafe {
|
||||
match bytes.len() {
|
||||
1 => make_4byte_bytes(bytes, 1, 0x80).map(Self),
|
||||
2 => make_4byte_bytes(bytes, 2, 0x8080).map(Self),
|
||||
3 => make_4byte_bytes(bytes, 3, 0x0080_8080).map(Self),
|
||||
4 => make_4byte_bytes(bytes, 4, 0x8080_8080).map(Self),
|
||||
_ => Err(Error::InvalidSize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An unsafe constructor intended for cases where the consumer
|
||||
/// guarantees that the input is a little endian integer which
|
||||
/// is a correct representation of a `TinyStr4` string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "Test".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// let num: u32 = s1.into();
|
||||
///
|
||||
/// let s2 = unsafe { TinyStr4::new_unchecked(num) };
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
/// assert_eq!(s2.as_str(), "Test");
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The method does not validate the `u32` to be properly encoded
|
||||
/// value for `TinyStr4`.
|
||||
/// The value can be retrieved via `Into<u32> for TinyStr4`.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new_unchecked(text: u32) -> Self {
|
||||
Self(NonZeroU32::new_unchecked(u32::from_le(text)))
|
||||
}
|
||||
|
||||
/// Extracts a string slice containing the entire `TinyStr4`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "Test".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.as_str(), "Test");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "Test".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr4 = "Te3t".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// assert!(!s2.is_ascii_alphabetic());
|
||||
/// ```
|
||||
pub fn is_ascii_alphabetic(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let lower = word | 0x2020_2020;
|
||||
let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
|
||||
(alpha & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphanumeric characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z', or
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "A15b".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr4 = "[3@w".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphanumeric());
|
||||
/// assert!(!s2.is_ascii_alphanumeric());
|
||||
/// ```
|
||||
pub fn is_ascii_alphanumeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
|
||||
let lower = word | 0x2020_2020;
|
||||
let alpha = !(lower + 0x1f1f_1f1f) | (lower + 0x0505_0505);
|
||||
(alpha & numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII decimal digits:
|
||||
///
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "312".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr4 = "3d".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_numeric());
|
||||
/// assert!(!s2.is_ascii_numeric());
|
||||
/// ```
|
||||
pub fn is_ascii_numeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f_7f7f) & 0x8080_8080;
|
||||
let numeric = !(word + 0x5050_5050) | (word + 0x4646_4646);
|
||||
(numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII lower case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "TeS3".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_lowercase(), "tes3");
|
||||
/// ```
|
||||
pub fn to_ascii_lowercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word | (((word + 0x3f3f_3f3f) & !(word + 0x2525_2525) & 0x8080_8080) >> 2);
|
||||
unsafe { Self(NonZeroU32::new_unchecked(result)) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII title case equivalent in-place.
|
||||
///
|
||||
/// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
|
||||
/// other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "test".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_titlecase(), "Test");
|
||||
/// ```
|
||||
pub fn to_ascii_titlecase(self) -> Self {
|
||||
let word = self.0.get().to_le();
|
||||
let mask = ((word + 0x3f3f_3f1f) & !(word + 0x2525_2505) & 0x8080_8080) >> 2;
|
||||
let result = (word | mask) & !(0x20 & mask);
|
||||
unsafe { Self(NonZeroU32::new_unchecked(u32::from_le(result))) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII upper case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr4;
|
||||
///
|
||||
/// let s1: TinyStr4 = "Tes3".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_uppercase(), "TES3");
|
||||
/// ```
|
||||
pub fn to_ascii_uppercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word & !(((word + 0x1f1f_1f1f) & !(word + 0x0505_0505) & 0x8080_8080) >> 2);
|
||||
unsafe { Self(NonZeroU32::new_unchecked(result)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TinyStr4 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TinyStr4 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TinyStr4 {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
// Again, could use #cfg to hand-roll a big-endian implementation.
|
||||
let word = self.0.get().to_le();
|
||||
let len = (4 - word.leading_zeros() / 8) as usize;
|
||||
unsafe {
|
||||
let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
|
||||
std::str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for TinyStr4 {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.deref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TinyStr4 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TinyStr4 {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.get().to_be().cmp(&other.0.get().to_be())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TinyStr4 {
|
||||
type Err = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
Self::from_bytes(text.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u32> for TinyStr4 {
|
||||
fn into(self) -> u32 {
|
||||
self.0.get().to_le()
|
||||
}
|
||||
}
|
|
@ -1,319 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::convert::Into;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// A tiny string that is from 1 to 8 non-NUL ASCII characters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "Testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Testing");
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TinyStr8(NonZeroU64);
|
||||
|
||||
impl TinyStr8 {
|
||||
/// Creates a TinyStr8 from a byte slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1 = TinyStr8::from_bytes("Testing".as_bytes())
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Testing");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
let len = bytes.len();
|
||||
if len < 1 || len > 8 {
|
||||
return Err(Error::InvalidSize);
|
||||
}
|
||||
unsafe {
|
||||
let mut word: u64 = 0;
|
||||
copy_nonoverlapping(bytes.as_ptr(), &mut word as *mut u64 as *mut u8, len);
|
||||
let mask = 0x80808080_80808080u64 >> (8 * (8 - len));
|
||||
// TODO: could do this with #cfg(target_endian), but this is clearer and
|
||||
// more confidence-inspiring.
|
||||
let mask = u64::from_le(mask);
|
||||
if (word & mask) != 0 {
|
||||
return Err(Error::NonAscii);
|
||||
}
|
||||
if ((mask - word) & mask) != 0 {
|
||||
return Err(Error::InvalidNull);
|
||||
}
|
||||
Ok(Self(NonZeroU64::new_unchecked(word)))
|
||||
}
|
||||
}
|
||||
|
||||
/// An unsafe constructor intended for cases where the consumer
|
||||
/// guarantees that the input is a little endian integer which
|
||||
/// is a correct representation of a `TinyStr8` string.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "Testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// let num: u64 = s1.into();
|
||||
///
|
||||
/// let s2 = unsafe { TinyStr8::new_unchecked(num) };
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
/// assert_eq!(s2.as_str(), "Testing");
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The method does not validate the `u64` to be properly encoded
|
||||
/// value for `TinyStr8`.
|
||||
/// The value can be retrieved via `Into<u64> for TinyStr8`.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new_unchecked(text: u64) -> Self {
|
||||
Self(NonZeroU64::new_unchecked(u64::from_le(text)))
|
||||
}
|
||||
|
||||
/// Extracts a string slice containing the entire `TinyStr8`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "Testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.as_str(), "Testing");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphabetic characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "Testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr8 = "Te3ting".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphabetic());
|
||||
/// assert!(!s2.is_ascii_alphabetic());
|
||||
/// ```
|
||||
pub fn is_ascii_alphabetic(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
|
||||
let lower = word | 0x20202020_20202020;
|
||||
let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f) | (lower + 0x05050505_05050505);
|
||||
(alpha & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII alphanumeric characters:
|
||||
///
|
||||
/// * U+0041 'A' ..= U+005A 'Z', or
|
||||
/// * U+0061 'a' ..= U+007A 'z', or
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "A15bing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr8 = "[3@wing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_alphanumeric());
|
||||
/// assert!(!s2.is_ascii_alphanumeric());
|
||||
/// ```
|
||||
pub fn is_ascii_alphanumeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
|
||||
let numeric = !(word + 0x50505050_50505050) | (word + 0x46464646_46464646);
|
||||
let lower = word | 0x20202020_20202020;
|
||||
let alpha = !(lower + 0x1f1f1f1f_1f1f1f1f) | (lower + 0x05050505_05050505);
|
||||
(alpha & numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Checks if the value is composed of ASCII decimal digits:
|
||||
///
|
||||
/// * U+0030 '0' ..= U+0039 '9'.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "3121029".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
/// let s2: TinyStr8 = "3d212d".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert!(s1.is_ascii_numeric());
|
||||
/// assert!(!s2.is_ascii_numeric());
|
||||
/// ```
|
||||
pub fn is_ascii_numeric(self) -> bool {
|
||||
let word = self.0.get();
|
||||
let mask = (word + 0x7f7f7f7f_7f7f7f7f) & 0x80808080_80808080;
|
||||
let numeric = !(word + 0x50505050_50505050) | (word + 0x46464646_46464646);
|
||||
(numeric & mask) == 0
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII lower case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "TeS3ing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_lowercase(), "tes3ing");
|
||||
/// ```
|
||||
pub fn to_ascii_lowercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word
|
||||
| (((word + 0x3f3f3f3f_3f3f3f3f)
|
||||
& !(word + 0x25252525_25252525)
|
||||
& 0x80808080_80808080)
|
||||
>> 2);
|
||||
unsafe { Self(NonZeroU64::new_unchecked(result)) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII title case equivalent in-place.
|
||||
///
|
||||
/// First character, if is an ASCII letter 'a' to 'z' is mapped to 'A' to 'Z',
|
||||
/// other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_titlecase(), "Testing");
|
||||
/// ```
|
||||
pub fn to_ascii_titlecase(self) -> Self {
|
||||
let word = self.0.get().to_le();
|
||||
let mask =
|
||||
((word + 0x3f3f3f3f_3f3f3f1f) & !(word + 0x25252525_25252505) & 0x80808080_80808080)
|
||||
>> 2;
|
||||
let result = (word | mask) & !(0x20 & mask);
|
||||
unsafe { Self(NonZeroU64::new_unchecked(u64::from_le(result))) }
|
||||
}
|
||||
|
||||
/// Converts this type to its ASCII upper case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', other characters are unchanged.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStr8;
|
||||
///
|
||||
/// let s1: TinyStr8 = "Tes3ing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1.to_ascii_uppercase(), "TES3ING");
|
||||
/// ```
|
||||
pub fn to_ascii_uppercase(self) -> Self {
|
||||
let word = self.0.get();
|
||||
let result = word
|
||||
& !(((word + 0x1f1f1f1f_1f1f1f1f)
|
||||
& !(word + 0x05050505_05050505)
|
||||
& 0x80808080_80808080)
|
||||
>> 2);
|
||||
unsafe { Self(NonZeroU64::new_unchecked(result)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TinyStr8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TinyStr8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TinyStr8 {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
// Again, could use #cfg to hand-roll a big-endian implementation.
|
||||
let word = self.0.get().to_le();
|
||||
let len = (8 - word.leading_zeros() / 8) as usize;
|
||||
unsafe {
|
||||
let slice = core::slice::from_raw_parts(&self.0 as *const _ as *const u8, len);
|
||||
std::str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for TinyStr8 {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.deref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for TinyStr8 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for TinyStr8 {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.get().to_be().cmp(&other.0.get().to_be())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TinyStr8 {
|
||||
type Err = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
TinyStr8::from_bytes(text.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u64> for TinyStr8 {
|
||||
fn into(self) -> u64 {
|
||||
self.0.get().to_le()
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::helpers::String;
|
||||
use crate::Error;
|
||||
use crate::TinyStr16;
|
||||
|
||||
/// An ASCII string that is tiny when <= 16 chars and a String otherwise.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tinystr::TinyStrAuto;
|
||||
///
|
||||
/// let s1: TinyStrAuto = "Testing".parse()
|
||||
/// .expect("Failed to parse.");
|
||||
///
|
||||
/// assert_eq!(s1, "Testing");
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
pub enum TinyStrAuto {
|
||||
/// Up to 16 characters stored on the stack.
|
||||
Tiny(TinyStr16),
|
||||
/// 17 or more characters stored on the heap.
|
||||
Heap(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TinyStrAuto {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.deref().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TinyStrAuto {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
use TinyStrAuto::*;
|
||||
match self {
|
||||
Tiny(value) => value.deref(),
|
||||
Heap(value) => value.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for TinyStrAuto {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.deref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for TinyStrAuto {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
if text.len() <= 16 {
|
||||
match TinyStr16::from_str(text) {
|
||||
Ok(result) => Ok(TinyStrAuto::Tiny(result)),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
} else {
|
||||
if !text.is_ascii() {
|
||||
return Err(Error::NonAscii);
|
||||
}
|
||||
match String::from_str(text) {
|
||||
Ok(result) => Ok(TinyStrAuto::Heap(result)),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// 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::TinyAsciiStr;
|
||||
use zerovec::maps::ZeroMapKV;
|
||||
use zerovec::ule::*;
|
||||
use zerovec::{ZeroSlice, ZeroVec};
|
||||
|
||||
// Safety (based on the safety checklist on the ULE trait):
|
||||
// 1. CharULE does not include any uninitialized or padding bytes.
|
||||
// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
|
||||
// 2. CharULE is aligned to 1 byte.
|
||||
// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant)
|
||||
// 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
|
||||
// 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
|
||||
// 5. The other ULE methods use the default impl.
|
||||
// 6. CharULE byte equality is semantic equality
|
||||
unsafe impl<const N: usize> ULE for TinyAsciiStr<N> {
|
||||
#[inline]
|
||||
fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> {
|
||||
if bytes.len() % N != 0 {
|
||||
return Err(ZeroVecError::length::<Self>(bytes.len()));
|
||||
}
|
||||
// Validate the bytes
|
||||
for chunk in bytes.chunks_exact(N) {
|
||||
let _ = TinyAsciiStr::<N>::from_bytes_inner(chunk, 0, N, true)
|
||||
.map_err(|_| ZeroVecError::parse::<Self>())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsULE for TinyAsciiStr<N> {
|
||||
type ULE = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_unaligned(self) -> Self::ULE {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_unaligned(unaligned: Self::ULE) -> Self {
|
||||
unaligned
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> ZeroMapKV<'a> for TinyAsciiStr<N> {
|
||||
type Container = ZeroVec<'a, TinyAsciiStr<N>>;
|
||||
type Slice = ZeroSlice<TinyAsciiStr<N>>;
|
||||
type GetType = TinyAsciiStr<N>;
|
||||
type OwnedType = TinyAsciiStr<N>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use zerovec::*;
|
||||
|
||||
#[test]
|
||||
fn test_zerovec() {
|
||||
let mut vec = ZeroVec::<TinyAsciiStr<7>>::new();
|
||||
|
||||
vec.with_mut(|v| v.push("foobar".parse().unwrap()));
|
||||
vec.with_mut(|v| v.push("baz".parse().unwrap()));
|
||||
vec.with_mut(|v| v.push("quux".parse().unwrap()));
|
||||
|
||||
let bytes = vec.as_bytes();
|
||||
|
||||
let vec: ZeroVec<TinyAsciiStr<7>> = ZeroVec::parse_byte_slice(bytes).unwrap();
|
||||
|
||||
assert_eq!(&*vec.get(0).unwrap(), "foobar");
|
||||
assert_eq!(&*vec.get(1).unwrap(), "baz");
|
||||
assert_eq!(&*vec.get(2).unwrap(), "quux");
|
||||
}
|
||||
}
|
|
@ -1,538 +0,0 @@
|
|||
use std::fmt::Write;
|
||||
use std::mem::size_of;
|
||||
use std::ops::Deref;
|
||||
use tinystr::{Error, TinyStr16, TinyStr4, TinyStr8};
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
use tinystr::TinyStrAuto;
|
||||
|
||||
#[test]
|
||||
fn tiny_sizes() {
|
||||
assert_eq!(4, size_of::<TinyStr4>());
|
||||
assert_eq!(8, size_of::<TinyStr8>());
|
||||
assert_eq!(16, size_of::<TinyStr16>());
|
||||
assert_eq!(24, size_of::<String>());
|
||||
// Note: TinyStrAuto is size 32 even when a smaller TinyStr type is used
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
assert_eq!(32, size_of::<TinyStrAuto>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_basic() {
|
||||
let s: TinyStr4 = "abc".parse().unwrap();
|
||||
assert_eq!(s.deref(), "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_from_bytes() {
|
||||
let s = TinyStr4::from_bytes("abc".as_bytes()).unwrap();
|
||||
assert_eq!(s.deref(), "abc");
|
||||
|
||||
assert_eq!(
|
||||
TinyStr4::from_bytes(&[0, 159, 146, 150]),
|
||||
Err(Error::NonAscii)
|
||||
);
|
||||
assert_eq!(TinyStr4::from_bytes(&[]), Err(Error::InvalidSize));
|
||||
assert_eq!(TinyStr4::from_bytes(&[0]), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_size() {
|
||||
assert_eq!("".parse::<TinyStr4>(), Err(Error::InvalidSize));
|
||||
assert!("1".parse::<TinyStr4>().is_ok());
|
||||
assert!("12".parse::<TinyStr4>().is_ok());
|
||||
assert!("123".parse::<TinyStr4>().is_ok());
|
||||
assert!("1234".parse::<TinyStr4>().is_ok());
|
||||
assert_eq!("12345".parse::<TinyStr4>(), Err(Error::InvalidSize));
|
||||
assert_eq!("123456789".parse::<TinyStr4>(), Err(Error::InvalidSize));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_null() {
|
||||
assert_eq!("a\u{0}b".parse::<TinyStr4>(), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_new_unchecked() {
|
||||
let reference: TinyStr4 = "en".parse().unwrap();
|
||||
let uval: u32 = reference.into();
|
||||
let s = unsafe { TinyStr4::new_unchecked(uval) };
|
||||
assert_eq!(s, reference);
|
||||
assert_eq!(s, "en");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_nonascii() {
|
||||
assert_eq!("\u{4000}".parse::<TinyStr4>(), Err(Error::NonAscii));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_alpha() {
|
||||
let s: TinyStr4 = "@aZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_alphabetic());
|
||||
assert!(!s.is_ascii_alphanumeric());
|
||||
assert_eq!(s.to_ascii_uppercase().as_str(), "@AZ[");
|
||||
assert_eq!(s.to_ascii_lowercase().as_str(), "@az[");
|
||||
|
||||
assert!("abYZ".parse::<TinyStr4>().unwrap().is_ascii_alphabetic());
|
||||
assert!("abYZ".parse::<TinyStr4>().unwrap().is_ascii_alphanumeric());
|
||||
assert!("a123".parse::<TinyStr4>().unwrap().is_ascii_alphanumeric());
|
||||
assert!(!"a123".parse::<TinyStr4>().unwrap().is_ascii_alphabetic());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_numeric() {
|
||||
let s: TinyStr4 = "@aZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_numeric());
|
||||
|
||||
assert!("0123".parse::<TinyStr4>().unwrap().is_ascii_numeric());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_titlecase() {
|
||||
assert_eq!(
|
||||
"abcd"
|
||||
.parse::<TinyStr4>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"ABCD"
|
||||
.parse::<TinyStr4>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"aBCD"
|
||||
.parse::<TinyStr4>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"A123"
|
||||
.parse::<TinyStr4>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"A123"
|
||||
);
|
||||
assert_eq!(
|
||||
"123a"
|
||||
.parse::<TinyStr4>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"123a"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_ord() {
|
||||
let mut v: Vec<TinyStr4> = vec!["zh".parse().unwrap(), "fr".parse().unwrap()];
|
||||
v.sort();
|
||||
|
||||
assert_eq!(v.get(0).unwrap().as_str(), "fr");
|
||||
assert_eq!(v.get(1).unwrap().as_str(), "zh");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_eq() {
|
||||
let s1: TinyStr4 = "en".parse().unwrap();
|
||||
let s2: TinyStr4 = "fr".parse().unwrap();
|
||||
let s3: TinyStr4 = "en".parse().unwrap();
|
||||
|
||||
assert_eq!(s1, s3);
|
||||
assert_ne!(s1, s2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_display() {
|
||||
let s: TinyStr4 = "abcd".parse().unwrap();
|
||||
let mut result = String::new();
|
||||
write!(result, "{}", s).unwrap();
|
||||
assert_eq!(result, "abcd");
|
||||
assert_eq!(format!("{}", s), "abcd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny4_debug() {
|
||||
let s: TinyStr4 = "abcd".parse().unwrap();
|
||||
assert_eq!(format!("{:#?}", s), "\"abcd\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_basic() {
|
||||
let s: TinyStr8 = "abcde".parse().unwrap();
|
||||
assert_eq!(s.deref(), "abcde");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_from_bytes() {
|
||||
let s = TinyStr8::from_bytes("abcde".as_bytes()).unwrap();
|
||||
assert_eq!(s.deref(), "abcde");
|
||||
|
||||
assert_eq!(
|
||||
TinyStr8::from_bytes(&[0, 159, 146, 150]),
|
||||
Err(Error::NonAscii)
|
||||
);
|
||||
assert_eq!(TinyStr8::from_bytes(&[]), Err(Error::InvalidSize));
|
||||
assert_eq!(TinyStr8::from_bytes(&[0]), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_size() {
|
||||
assert_eq!("".parse::<TinyStr8>(), Err(Error::InvalidSize));
|
||||
assert!("1".parse::<TinyStr8>().is_ok());
|
||||
assert!("12".parse::<TinyStr8>().is_ok());
|
||||
assert!("123".parse::<TinyStr8>().is_ok());
|
||||
assert!("1234".parse::<TinyStr8>().is_ok());
|
||||
assert!("12345".parse::<TinyStr8>().is_ok());
|
||||
assert!("123456".parse::<TinyStr8>().is_ok());
|
||||
assert!("1234567".parse::<TinyStr8>().is_ok());
|
||||
assert!("12345678".parse::<TinyStr8>().is_ok());
|
||||
assert_eq!("123456789".parse::<TinyStr8>(), Err(Error::InvalidSize));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_null() {
|
||||
assert_eq!("a\u{0}b".parse::<TinyStr8>(), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_new_unchecked() {
|
||||
let reference: TinyStr8 = "Windows".parse().unwrap();
|
||||
let uval: u64 = reference.into();
|
||||
let s = unsafe { TinyStr8::new_unchecked(uval) };
|
||||
assert_eq!(s, reference);
|
||||
assert_eq!(s, "Windows");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_nonascii() {
|
||||
assert_eq!("\u{4000}".parse::<TinyStr8>(), Err(Error::NonAscii));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_alpha() {
|
||||
let s: TinyStr8 = "@abcXYZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_alphabetic());
|
||||
assert!(!s.is_ascii_alphanumeric());
|
||||
assert_eq!(s.to_ascii_uppercase().as_str(), "@ABCXYZ[");
|
||||
assert_eq!(s.to_ascii_lowercase().as_str(), "@abcxyz[");
|
||||
|
||||
assert!("abcXYZ".parse::<TinyStr8>().unwrap().is_ascii_alphabetic());
|
||||
assert!("abcXYZ"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.is_ascii_alphanumeric());
|
||||
assert!(!"abc123".parse::<TinyStr8>().unwrap().is_ascii_alphabetic());
|
||||
assert!("abc123"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.is_ascii_alphanumeric());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_numeric() {
|
||||
let s: TinyStr8 = "@abcXYZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_numeric());
|
||||
|
||||
assert!("01234567".parse::<TinyStr8>().unwrap().is_ascii_numeric());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_titlecase() {
|
||||
assert_eq!(
|
||||
"abcdabcd"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"ABCDABCD"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"aBCDaBCD"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"A123a123"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"A123a123"
|
||||
);
|
||||
assert_eq!(
|
||||
"123a123A"
|
||||
.parse::<TinyStr8>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"123a123a"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_ord() {
|
||||
let mut v: Vec<TinyStr8> = vec!["nedis".parse().unwrap(), "macos".parse().unwrap()];
|
||||
v.sort();
|
||||
|
||||
assert_eq!(v.get(0).unwrap().as_str(), "macos");
|
||||
assert_eq!(v.get(1).unwrap().as_str(), "nedis");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_eq() {
|
||||
let s1: TinyStr8 = "windows".parse().unwrap();
|
||||
let s2: TinyStr8 = "mac".parse().unwrap();
|
||||
let s3: TinyStr8 = "windows".parse().unwrap();
|
||||
|
||||
assert_eq!(s1, s3);
|
||||
assert_ne!(s1, s2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_display() {
|
||||
let s: TinyStr8 = "abcdef".parse().unwrap();
|
||||
let mut result = String::new();
|
||||
write!(result, "{}", s).unwrap();
|
||||
assert_eq!(result, "abcdef");
|
||||
assert_eq!(format!("{}", s), "abcdef");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny8_debug() {
|
||||
let s: TinyStr8 = "abcdef".parse().unwrap();
|
||||
assert_eq!(format!("{:#?}", s), "\"abcdef\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_from_bytes() {
|
||||
let s = TinyStr16::from_bytes("abcdefghijk".as_bytes()).unwrap();
|
||||
assert_eq!(s.deref(), "abcdefghijk");
|
||||
|
||||
assert_eq!(
|
||||
TinyStr16::from_bytes(&[0, 159, 146, 150]),
|
||||
Err(Error::NonAscii)
|
||||
);
|
||||
assert_eq!(TinyStr16::from_bytes(&[]), Err(Error::InvalidSize));
|
||||
assert_eq!(TinyStr16::from_bytes(&[0]), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_size() {
|
||||
assert_eq!("".parse::<TinyStr16>(), Err(Error::InvalidSize));
|
||||
assert!("1".parse::<TinyStr16>().is_ok());
|
||||
assert!("12".parse::<TinyStr16>().is_ok());
|
||||
assert!("123".parse::<TinyStr16>().is_ok());
|
||||
assert!("1234".parse::<TinyStr16>().is_ok());
|
||||
assert!("12345".parse::<TinyStr16>().is_ok());
|
||||
assert!("123456".parse::<TinyStr16>().is_ok());
|
||||
assert!("1234567".parse::<TinyStr16>().is_ok());
|
||||
assert!("12345678".parse::<TinyStr16>().is_ok());
|
||||
assert!("123456781".parse::<TinyStr16>().is_ok());
|
||||
assert!("1234567812".parse::<TinyStr16>().is_ok());
|
||||
assert!("12345678123".parse::<TinyStr16>().is_ok());
|
||||
assert!("123456781234".parse::<TinyStr16>().is_ok());
|
||||
assert!("1234567812345".parse::<TinyStr16>().is_ok());
|
||||
assert!("12345678123456".parse::<TinyStr16>().is_ok());
|
||||
assert!("123456781234567".parse::<TinyStr16>().is_ok());
|
||||
assert!("1234567812345678".parse::<TinyStr16>().is_ok());
|
||||
assert_eq!(
|
||||
"12345678123456789".parse::<TinyStr16>(),
|
||||
Err(Error::InvalidSize)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_null() {
|
||||
assert_eq!("a\u{0}b".parse::<TinyStr16>(), Err(Error::InvalidNull));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_new_unchecked() {
|
||||
let reference: TinyStr16 = "WindowsCE/ME/NT".parse().unwrap();
|
||||
let uval: u128 = reference.into();
|
||||
let s = unsafe { TinyStr16::new_unchecked(uval) };
|
||||
assert_eq!(s, reference);
|
||||
assert_eq!(s, "WindowsCE/ME/NT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_nonascii() {
|
||||
assert_eq!("\u{4000}".parse::<TinyStr16>(), Err(Error::NonAscii));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_alpha() {
|
||||
let s: TinyStr16 = "@abcdefgTUVWXYZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_alphabetic());
|
||||
assert!(!s.is_ascii_alphanumeric());
|
||||
assert_eq!(s.to_ascii_uppercase().as_str(), "@ABCDEFGTUVWXYZ[");
|
||||
assert_eq!(s.to_ascii_lowercase().as_str(), "@abcdefgtuvwxyz[");
|
||||
|
||||
assert!("abcdefgTUVWXYZ"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.is_ascii_alphabetic());
|
||||
assert!("abcdefgTUVWXYZ"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.is_ascii_alphanumeric());
|
||||
assert!(!"abcdefg0123456"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.is_ascii_alphabetic());
|
||||
assert!("abcdefgTUVWXYZ"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.is_ascii_alphanumeric());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_numeric() {
|
||||
let s: TinyStr16 = "@abcdefgTUVWXYZ[".parse().unwrap();
|
||||
assert!(!s.is_ascii_numeric());
|
||||
|
||||
assert!("0123456789"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.is_ascii_numeric());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_titlecase() {
|
||||
assert_eq!(
|
||||
"abcdabcdabcdabcd"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcdabcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"ABCDABCDABCDABCD"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcdabcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"aBCDaBCDaBCDaBCD"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"Abcdabcdabcdabcd"
|
||||
);
|
||||
assert_eq!(
|
||||
"A123a123A123a123"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"A123a123a123a123"
|
||||
);
|
||||
assert_eq!(
|
||||
"123a123A123a123A"
|
||||
.parse::<TinyStr16>()
|
||||
.unwrap()
|
||||
.to_ascii_titlecase()
|
||||
.as_str(),
|
||||
"123a123a123a123a"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_ord() {
|
||||
let mut v: Vec<TinyStr16> = vec!["nedis_xxxx".parse().unwrap(), "macos_xxxx".parse().unwrap()];
|
||||
v.sort();
|
||||
|
||||
assert_eq!(v.get(0).unwrap().as_str(), "macos_xxxx");
|
||||
assert_eq!(v.get(1).unwrap().as_str(), "nedis_xxxx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_eq() {
|
||||
let s1: TinyStr16 = "windows98SE".parse().unwrap();
|
||||
let s2: TinyStr16 = "mac".parse().unwrap();
|
||||
let s3: TinyStr16 = "windows98SE".parse().unwrap();
|
||||
|
||||
assert_eq!(s1, s3);
|
||||
assert_ne!(s1, s2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_display() {
|
||||
let s: TinyStr16 = "abcdefghijkl".parse().unwrap();
|
||||
let mut result = String::new();
|
||||
write!(result, "{}", s).unwrap();
|
||||
assert_eq!(result, "abcdefghijkl");
|
||||
assert_eq!(format!("{}", s), "abcdefghijkl");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tiny16_debug() {
|
||||
let s: TinyStr16 = "abcdefghijkl".parse().unwrap();
|
||||
assert_eq!(format!("{:#?}", s), "\"abcdefghijkl\"");
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[test]
|
||||
fn tinyauto_basic() {
|
||||
let s1: TinyStrAuto = "abc".parse().unwrap();
|
||||
assert_eq!(s1, "abc");
|
||||
|
||||
let s2: TinyStrAuto = "veryveryveryveryverylong".parse().unwrap();
|
||||
assert_eq!(s2, "veryveryveryveryverylong");
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
#[test]
|
||||
fn tinyauto_nonascii() {
|
||||
assert_eq!("\u{4000}".parse::<TinyStrAuto>(), Err(Error::NonAscii));
|
||||
assert_eq!(
|
||||
"veryveryveryveryverylong\u{4000}".parse::<TinyStrAuto>(),
|
||||
Err(Error::NonAscii)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
const TS: TinyStr8 = tinystr::macros::tinystr8!("test");
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[test]
|
||||
fn tinystr_macros() {
|
||||
use tinystr::macros::*;
|
||||
|
||||
let x: TinyStr8 = "test".parse().unwrap();
|
||||
assert_eq!(TS, x);
|
||||
|
||||
let x: TinyStr4 = "foo".parse().unwrap();
|
||||
assert_eq!(tinystr4!("foo"), x);
|
||||
|
||||
let x: TinyStr8 = "barbaz".parse().unwrap();
|
||||
assert_eq!(tinystr8!("barbaz"), x);
|
||||
|
||||
let x: TinyStr16 = "metamorphosis".parse().unwrap();
|
||||
assert_eq!(tinystr16!("metamorphosis"), x);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// 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 tinystr::*;
|
||||
|
||||
// Tests largely adapted from `tinystr` crate
|
||||
// https://github.com/zbraniecki/tinystr/blob/4e4eab55dd6bded7f29a18b41452c506c461716c/tests/serde.rs
|
||||
|
||||
macro_rules! test_roundtrip {
|
||||
($f:ident, $n:literal, $val:expr) => {
|
||||
#[test]
|
||||
fn $f() {
|
||||
let tiny: TinyAsciiStr<$n> = $val.parse().unwrap();
|
||||
let json_string = serde_json::to_string(&tiny).unwrap();
|
||||
let expected_json = concat!("\"", $val, "\"");
|
||||
assert_eq!(json_string, expected_json);
|
||||
let recover: TinyAsciiStr<$n> = serde_json::from_str(&json_string).unwrap();
|
||||
assert_eq!(&*tiny, &*recover);
|
||||
|
||||
let bin = bincode::serialize(&tiny).unwrap();
|
||||
assert_eq!(bin, &tiny.all_bytes()[..]);
|
||||
let debin: TinyAsciiStr<$n> = bincode::deserialize(&bin).unwrap();
|
||||
assert_eq!(&*tiny, &*debin);
|
||||
|
||||
let post = postcard::to_stdvec(&tiny).unwrap();
|
||||
assert_eq!(post, &tiny.all_bytes()[..]);
|
||||
let unpost: TinyAsciiStr<$n> = postcard::from_bytes(&post).unwrap();
|
||||
assert_eq!(&*tiny, &*unpost);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_roundtrip!(test_roundtrip4_1, 4, "en");
|
||||
test_roundtrip!(test_roundtrip4_2, 4, "Latn");
|
||||
test_roundtrip!(test_roundtrip8, 8, "calendar");
|
||||
test_roundtrip!(test_roundtrip16, 16, "verylongstring");
|
||||
test_roundtrip!(test_roundtrip10, 11, "shortstring");
|
||||
test_roundtrip!(test_roundtrip30, 24, "veryveryverylongstring");
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.lock":"7a6bb71d558693114436f11f7089237447a936cc8365f8afe0305e0b68dae07b","Cargo.toml":"0495636d8071b0d804764190a2daa3f6f780336d80bd8712ef7c197963fd5ce4","README.md":"1ce686b1ba46e7fff771db934d1e8905e3d96c73d9e6712ca85011cf63cf4157","benches/canonicalize.rs":"1bc9db6454ed711c1c1d58a9236d1ed02c40645782338697812dc21b26c2295f","benches/langid.rs":"46dc68e1ee1c998fe957035c11c373cc5d2c2993dd3058aeb36014028618a46e","benches/likely_subtags.rs":"672b47687d20d4294e2de78dc6eb1d36400a4b4c672082d15415fb18faf2e508","benches/parser.rs":"f9cf158bf5d146ef413e63e952c0a866269ba2be0db44a0aa88a5e61fdf53b4a","data/cldr-misc-full/README.md":"b4bbc8e56b8c9f5d482d2dd211e53720cdca27b0e98eec274e56577d41906b92","src/bin/generate_layout.rs":"e1df742c318ba4de09a25edad082a398da1d5591ac24074f9eadf3f40d677a9a","src/bin/generate_likelysubtags.rs":"c38ef8e59044578e51681723dbaf7f9669ccb7c7487f24ef6a430aad194f160f","src/errors.rs":"091525fd3d704cc4698bab82af9fff1f2e6fc793910700790ec3ef943b3eddfe","src/layout_table.rs":"c311b5399a22efd69f14decb00aee7dfb2943a090832e2aef127e2853ad1ce0b","src/lib.rs":"75b5e8958ba0139ef60139e61441d29c80bde8552d1adead3905165c11ed97cd","src/likelysubtags/mod.rs":"66889568b883675465faa445e257b4b70e9dcba98c6c523eb34a08ebb50377c0","src/likelysubtags/tables.rs":"47ab40497145adc553f9f585d1ec79d01028495a57b1907392642afd26986e89","src/parser/errors.rs":"2dacf5bf388499c9fbecf64ef950038f123335e87b6691bcdb88d185678f38d5","src/parser/mod.rs":"30f9ff487ab7b023ec7e6cfbfe4f4ebc41ca524cef0eba96a6b77ad624c9f6b8","src/serde.rs":"2ce1fdea1217c4f72b2c8e00f333902ff496c54eef5b4534adb289375c50aef0","src/subtags/language.rs":"1f7dbc4a10b58a6d1b729dc986a20396ce75558dfb1f9be223d5723a5e241e73","src/subtags/mod.rs":"ba23712d7ce5e0fa896c97ddc91d7eacbf21a99d62c15f5cf19a6653876bf56b","src/subtags/region.rs":"8cfd794f41f48fdf9742c9149b7360ea7cbd84c93abd13fe5a5317c46b4c48fc","src/subtags/script.rs":"94760b06f50f8f32c45af4f238b66e32428e5710ce4eabab4c943b5ebda3b0fe","src/subtags/variant.rs":"a8647d82c49df0d8df7fda33033d0df7a235beecfb35f459122675ab16b2dd8e"},"package":"1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"}
|
||||
{"files":{"Cargo.lock":"da3d5fabc92472ac1f45fa321f1e188fd3fff22b3234d3f74e78a8f79503aac9","Cargo.toml":"67560e144c925cdb1467085067b51138da8461c4babd170777e90082a1664d61","README.md":"1ce686b1ba46e7fff771db934d1e8905e3d96c73d9e6712ca85011cf63cf4157","benches/canonicalize.rs":"1bc9db6454ed711c1c1d58a9236d1ed02c40645782338697812dc21b26c2295f","benches/langid.rs":"46dc68e1ee1c998fe957035c11c373cc5d2c2993dd3058aeb36014028618a46e","benches/likely_subtags.rs":"33debb6db4eabd0166525c7c06868f86e35958c2d0d9e5e7668f28b1aff3da23","benches/parser.rs":"bc5460589125aca2ca4c4cf475fde81e7de6aa70c7bf8443996b6246b44110a3","data/cldr-misc-full/README.md":"b4bbc8e56b8c9f5d482d2dd211e53720cdca27b0e98eec274e56577d41906b92","src/bin/generate_layout.rs":"e1df742c318ba4de09a25edad082a398da1d5591ac24074f9eadf3f40d677a9a","src/bin/generate_likelysubtags.rs":"9b0df3a008d0f6090ee441e4c649f00f9b78c7d23434f389fe3de2288951cf72","src/errors.rs":"091525fd3d704cc4698bab82af9fff1f2e6fc793910700790ec3ef943b3eddfe","src/layout_table.rs":"c311b5399a22efd69f14decb00aee7dfb2943a090832e2aef127e2853ad1ce0b","src/lib.rs":"75b5e8958ba0139ef60139e61441d29c80bde8552d1adead3905165c11ed97cd","src/likelysubtags/mod.rs":"66889568b883675465faa445e257b4b70e9dcba98c6c523eb34a08ebb50377c0","src/likelysubtags/tables.rs":"47ab40497145adc553f9f585d1ec79d01028495a57b1907392642afd26986e89","src/parser/errors.rs":"2dacf5bf388499c9fbecf64ef950038f123335e87b6691bcdb88d185678f38d5","src/parser/mod.rs":"30f9ff487ab7b023ec7e6cfbfe4f4ebc41ca524cef0eba96a6b77ad624c9f6b8","src/serde.rs":"2ce1fdea1217c4f72b2c8e00f333902ff496c54eef5b4534adb289375c50aef0","src/subtags/language.rs":"ea198788c10a9b24a88d2316d516a1b6325b20c57c1d05586edc7c50acc1e27d","src/subtags/mod.rs":"ba23712d7ce5e0fa896c97ddc91d7eacbf21a99d62c15f5cf19a6653876bf56b","src/subtags/region.rs":"f3455679067ed0da9103d5433586cb4809628847e923bbcf35cc2d2b25a0d35e","src/subtags/script.rs":"c5ca9a2ab6d8b3a1dc3f52a73665223f0dd30901b8e5d722dd93e58a69710f5d","src/subtags/variant.rs":"a0feec8b44933ff04a3d3f91e9e69f7537468e7c813c3780eb87e51512f6d1dd","tests/canonicalize_test.rs":"5ca005223ae159c15f8809c28c1cc950dc4d69063d9de70b885f18ef8e6cca28","tests/fixtures.rs":"d03a280b67080dac1bc333255e89d8903164688a6dfaed5adbea68efd65ef1bc","tests/language_identifier_test.rs":"90a4d7ac4961c1e214de166036c1b5cae21f3a99f157fe00d149d8b9106cb7b9","tests/likelysubtags.rs":"f5db0c3ec9f545eef148914486c7a74f1bbc629f4dcff3077242c174c0c1c6a3"},"package":"e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff"}
|
|
@ -1,5 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -13,21 +15,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.12"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41"
|
||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
|
@ -37,36 +39,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.2.1"
|
||||
version = "3.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
|
@ -75,9 +68,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.2"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63f696897c88b57f4ffe3c69d8e1a0613c7d0e6c4833363c8560fbde9c47b966"
|
||||
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
|
@ -92,6 +85,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
|
@ -100,70 +94,66 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.2"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddeaf7989f00f2e1d871a26a110f3ed713632feac17f65f03ca938c542618b60"
|
||||
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.3"
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.2"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.3"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"itoa 0.4.8",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
@ -178,40 +168,63 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
name = "displaydoc"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.12"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.39"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -224,101 +237,118 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.69"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.1"
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.2.14"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9b1d9ca091d370ea3a78d5619145d1b59426ab0c9eedbad2514a4cee08bf389"
|
||||
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.12"
|
||||
name = "plotters-backend"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
|
||||
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.4"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.0"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
|
||||
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
|
@ -326,55 +356,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.0"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
|
||||
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.7"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.17"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.4"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
|
@ -391,35 +408,30 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.106"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.106"
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -428,24 +440,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.52"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd"
|
||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"itoa 1.0.4",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.18"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
|
||||
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -459,15 +471,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.3.2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bac79c4b51eda1b090b1edebfb667821bbb51f713855164dc7cec2cb8ac2ba3"
|
||||
checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.0.4"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -475,7 +490,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"serde",
|
||||
|
@ -484,22 +499,22 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
|
@ -508,9 +523,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.62"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -518,13 +533,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.62"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -533,9 +548,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.62"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -543,9 +558,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.62"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -556,15 +571,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.62"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.39"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -572,9 +587,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
|
|
|
@ -3,19 +3,24 @@
|
|||
# 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
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
# 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 = "2018"
|
||||
name = "unic-langid-impl"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
authors = ["Zibi Braniecki <gandalf@mozilla.com>"]
|
||||
include = ["src/**/*", "benches/*.rs", "Cargo.toml", "README.md"]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"benches/*.rs",
|
||||
"tests/*.rs",
|
||||
"Cargo.toml",
|
||||
"README.md",
|
||||
]
|
||||
description = "API for managing Unicode Language Identifiers"
|
||||
readme = "README.md"
|
||||
categories = ["internationalization"]
|
||||
|
@ -51,6 +56,7 @@ harness = false
|
|||
name = "likely_subtags"
|
||||
harness = false
|
||||
required-features = ["likelysubtags"]
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
@ -60,7 +66,8 @@ version = "1.0"
|
|||
optional = true
|
||||
|
||||
[dependencies.tinystr]
|
||||
version = "0.3.2"
|
||||
version = "0.7.0"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
|
@ -72,5 +79,8 @@ features = ["derive"]
|
|||
version = "1.0"
|
||||
|
||||
[features]
|
||||
binary = ["serde", "serde_json"]
|
||||
binary = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
likelysubtags = []
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
|
||||
use tinystr::{TinyStr4, TinyStr8};
|
||||
use unic_langid_impl::subtags;
|
||||
use unic_langid_impl::LanguageIdentifier;
|
||||
|
||||
static STRINGS: &[&str] = &[
|
||||
|
@ -49,51 +48,37 @@ static STRINGS: &[&str] = &[
|
|||
];
|
||||
|
||||
fn maximize_bench(c: &mut Criterion) {
|
||||
let langids: Vec<LanguageIdentifier> = STRINGS
|
||||
.iter()
|
||||
.map(|s| -> LanguageIdentifier { s.parse().unwrap() })
|
||||
.collect();
|
||||
c.bench_function("maximize", move |b| {
|
||||
b.iter(|| {
|
||||
let langids: Vec<LanguageIdentifier> = STRINGS
|
||||
.iter()
|
||||
.map(|s| -> LanguageIdentifier { s.parse().unwrap() })
|
||||
.collect();
|
||||
for mut s in langids {
|
||||
for mut s in langids.clone().into_iter() {
|
||||
s.maximize();
|
||||
let _ = black_box(s.to_string());
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn extract_input(s: &str) -> (Option<TinyStr8>, Option<TinyStr4>, Option<TinyStr4>) {
|
||||
let chunks: Vec<&str> = s.split("-").collect();
|
||||
let mut lang: Option<TinyStr8> = chunks.get(0).map(|s| s.parse().unwrap());
|
||||
let mut script: Option<TinyStr4> = chunks.get(1).map(|s| s.parse().unwrap());
|
||||
let mut region: Option<TinyStr4> = chunks.get(2).map(|s| s.parse().unwrap());
|
||||
if let Some(l) = lang {
|
||||
if l.as_str() == "und" {
|
||||
lang = None;
|
||||
}
|
||||
}
|
||||
if let Some(s) = script {
|
||||
if s.as_str().chars().count() == 2 {
|
||||
region = script;
|
||||
script = None;
|
||||
}
|
||||
}
|
||||
(lang, script, region)
|
||||
fn extract_input(
|
||||
s: &str,
|
||||
) -> (
|
||||
subtags::Language,
|
||||
Option<subtags::Script>,
|
||||
Option<subtags::Region>,
|
||||
) {
|
||||
let langid: LanguageIdentifier = s.parse().unwrap();
|
||||
(langid.language, langid.script, langid.region)
|
||||
}
|
||||
|
||||
fn raw_maximize_bench(c: &mut Criterion) {
|
||||
let entries: Vec<(Option<TinyStr8>, Option<TinyStr4>, Option<TinyStr4>)> =
|
||||
STRINGS.iter().map(|s| extract_input(s)).collect();
|
||||
let entries: Vec<_> = STRINGS.iter().map(|s| extract_input(s)).collect();
|
||||
|
||||
c.bench_function("raw_maximize", move |b| {
|
||||
b.iter(|| {
|
||||
for (lang, script, region) in &entries {
|
||||
let _ = unic_langid_impl::likelysubtags::maximize(
|
||||
lang.clone(),
|
||||
script.clone(),
|
||||
region.clone(),
|
||||
);
|
||||
for (lang, script, region) in entries.clone().into_iter() {
|
||||
let _ = unic_langid_impl::likelysubtags::maximize(lang, script, region);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -21,6 +21,13 @@ fn language_identifier_parser_bench(c: &mut Criterion) {
|
|||
"fr-FR",
|
||||
"mk",
|
||||
"uk",
|
||||
"en-US",
|
||||
"en-GB",
|
||||
"es-AR",
|
||||
"th",
|
||||
"de",
|
||||
"zh-Cyrl-HN",
|
||||
"en-Latn-US",
|
||||
];
|
||||
|
||||
c.bench_function("language_identifier_parser", |b| {
|
||||
|
@ -49,6 +56,13 @@ fn language_identifier_parser_casing_bench(c: &mut Criterion) {
|
|||
"fr_fr",
|
||||
"Mk",
|
||||
"uK",
|
||||
"en-us",
|
||||
"en_gb",
|
||||
"ES-AR",
|
||||
"tH",
|
||||
"DE",
|
||||
"ZH_cyrl_hN",
|
||||
"eN-lAtN-uS",
|
||||
];
|
||||
c.bench_function("language_identifier_parser_casing", |b| {
|
||||
let slices: Vec<&[u8]> = strings.iter().map(|s| s.as_bytes()).collect();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use serde_json::Value;
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
use tinystr::TinyStr8;
|
||||
use unic_langid_impl::{subtags, LanguageIdentifier};
|
||||
|
||||
|
@ -77,7 +76,7 @@ fn main() {
|
|||
|
||||
match (lang, script, region) {
|
||||
(None, None, None) => lang_only.push((
|
||||
TinyStr8::from_str("und").unwrap().into(),
|
||||
u64::from_le_bytes(*TinyStr8::from_str("und").unwrap().all_bytes()),
|
||||
(val_lang, val_script, val_region),
|
||||
)),
|
||||
(Some(l), None, None) => lang_only.push((
|
||||
|
|
|
@ -34,7 +34,7 @@ impl Language {
|
|||
/// This function accepts any u64 that is exected to be a valid
|
||||
/// `TinyStr8` and a valid `Language` subtag.
|
||||
pub const unsafe fn from_raw_unchecked(v: u64) -> Self {
|
||||
Self(Some(TinyStr8::new_unchecked(v)))
|
||||
Self(Some(TinyStr8::from_bytes_unchecked(v.to_le_bytes())))
|
||||
}
|
||||
|
||||
pub fn matches<O: Borrow<Self>>(
|
||||
|
@ -59,13 +59,13 @@ impl Language {
|
|||
|
||||
impl From<Language> for Option<u64> {
|
||||
fn from(input: Language) -> Self {
|
||||
input.0.map(|i| i.into())
|
||||
input.0.map(|i| u64::from_le_bytes(*i.all_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Language> for Option<u64> {
|
||||
fn from(input: &Language) -> Self {
|
||||
input.0.map(|i| i.into())
|
||||
input.0.map(|i| u64::from_le_bytes(*i.all_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,13 +37,13 @@ impl Region {
|
|||
/// This function accepts any u64 that is exected to be a valid
|
||||
/// `TinyStr4` and a valid `Region` subtag.
|
||||
pub const unsafe fn from_raw_unchecked(v: u32) -> Self {
|
||||
Self(TinyStr4::new_unchecked(v))
|
||||
Self(TinyStr4::from_bytes_unchecked(v.to_le_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Region> for u32 {
|
||||
fn from(input: Region) -> Self {
|
||||
input.0.into()
|
||||
u32::from_le_bytes(*input.0.all_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,13 +25,13 @@ impl Script {
|
|||
/// This function accepts any u64 that is exected to be a valid
|
||||
/// `TinyStr4` and a valid `Script` subtag.
|
||||
pub const unsafe fn from_raw_unchecked(v: u32) -> Self {
|
||||
Self(TinyStr4::new_unchecked(v))
|
||||
Self(TinyStr4::from_bytes_unchecked(v.to_le_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Script> for u32 {
|
||||
fn from(input: Script) -> Self {
|
||||
input.0.into()
|
||||
u32::from_le_bytes(*input.0.all_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,19 +35,19 @@ impl Variant {
|
|||
/// This function accepts any u64 that is exected to be a valid
|
||||
/// `TinyStr8` and a valid `Variant` subtag.
|
||||
pub const unsafe fn from_raw_unchecked(v: u64) -> Self {
|
||||
Self(TinyStr8::new_unchecked(v))
|
||||
Self(TinyStr8::from_bytes_unchecked(v.to_le_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Variant> for u64 {
|
||||
fn from(input: Variant) -> Self {
|
||||
input.0.into()
|
||||
u64::from_le_bytes(*input.0.all_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Variant> for u64 {
|
||||
fn from(input: &Variant) -> Self {
|
||||
input.0.into()
|
||||
u64::from_le_bytes(*input.0.all_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
use unic_langid_impl::canonicalize;
|
||||
|
||||
fn assert_canonicalize(input: &str, output: &str) {
|
||||
assert_eq!(&canonicalize(input).unwrap(), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_canonicalize() {
|
||||
assert_canonicalize("Pl", "pl");
|
||||
assert_canonicalize("eN-uS", "en-US");
|
||||
assert_canonicalize("ZH_hans_hK", "zh-Hans-HK");
|
||||
assert_canonicalize("en-scouse-fonipa", "en-fonipa-scouse");
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use unic_langid_impl::LanguageIdentifier;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LangIdTestInputData {
|
||||
string: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct LangIdTestOutputObject {
|
||||
language: Option<String>,
|
||||
script: Option<String>,
|
||||
region: Option<String>,
|
||||
#[serde(default)]
|
||||
variants: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
enum LangIdTestOutput {
|
||||
String(String),
|
||||
Object(LangIdTestOutputObject),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LangIdTestSet {
|
||||
input: LangIdTestInputData,
|
||||
output: LangIdTestOutput,
|
||||
}
|
||||
|
||||
fn read_langid_testsets<P: AsRef<Path>>(path: P) -> Result<Vec<LangIdTestSet>, Box<dyn Error>> {
|
||||
let file = File::open(path)?;
|
||||
let sets = serde_json::from_reader(file)?;
|
||||
Ok(sets)
|
||||
}
|
||||
|
||||
fn test_langid_fixtures(path: &str) {
|
||||
let tests = read_langid_testsets(path).unwrap();
|
||||
|
||||
for test in tests {
|
||||
let s = test.input.string;
|
||||
|
||||
let langid: LanguageIdentifier = s.parse().expect("Parsing failed.");
|
||||
|
||||
match test.output {
|
||||
LangIdTestOutput::Object(o) => {
|
||||
let expected = LanguageIdentifier::from_parts(
|
||||
o.language.try_into().unwrap(),
|
||||
o.script.as_ref().map(|s| s.parse().unwrap()),
|
||||
o.region.as_ref().map(|r| r.parse().unwrap()),
|
||||
o.variants
|
||||
.iter()
|
||||
.map(|s| s.parse().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.as_ref(),
|
||||
);
|
||||
assert_eq!(langid, expected);
|
||||
}
|
||||
LangIdTestOutput::String(s) => {
|
||||
assert_eq!(langid.to_string(), s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
test_langid_fixtures("./tests/fixtures/parsing.json");
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
use unic_langid_impl::parser::parse_language_identifier;
|
||||
use unic_langid_impl::subtags;
|
||||
use unic_langid_impl::CharacterDirection;
|
||||
use unic_langid_impl::LanguageIdentifier;
|
||||
|
||||
fn assert_language_identifier(
|
||||
loc: &LanguageIdentifier,
|
||||
language: Option<&str>,
|
||||
script: Option<&str>,
|
||||
region: Option<&str>,
|
||||
variants: Option<&[&str]>,
|
||||
) {
|
||||
assert_eq!(
|
||||
loc.language,
|
||||
language.map_or(subtags::Language::default(), |l| {
|
||||
subtags::Language::from_bytes(l.as_bytes()).unwrap()
|
||||
})
|
||||
);
|
||||
assert_eq!(loc.script, script.map(|s| s.parse().unwrap()));
|
||||
assert_eq!(loc.region, region.map(|r| r.parse().unwrap()));
|
||||
let v = variants
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.map(|v| -> subtags::Variant { v.parse().unwrap() })
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
loc.variants().collect::<Vec<_>>(),
|
||||
v.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_parsed_language_identifier(
|
||||
input: &str,
|
||||
language: Option<&str>,
|
||||
script: Option<&str>,
|
||||
region: Option<&str>,
|
||||
variants: Option<&[&str]>,
|
||||
) {
|
||||
let langid = parse_language_identifier(input.as_bytes()).unwrap();
|
||||
assert_language_identifier(&langid, language, script, region, variants);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_language_identifier_parser() {
|
||||
assert_parsed_language_identifier("pl", Some("pl"), None, None, None);
|
||||
assert_parsed_language_identifier("und", None, None, None, None);
|
||||
assert_parsed_language_identifier("en-US", Some("en"), None, Some("US"), None);
|
||||
assert_parsed_language_identifier("en-Latn-US", Some("en"), Some("Latn"), Some("US"), None);
|
||||
assert_parsed_language_identifier("sl-nedis", Some("sl"), None, None, Some(&["nedis"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_language_casing() {
|
||||
assert_parsed_language_identifier("Pl", Some("pl"), None, None, None);
|
||||
assert_parsed_language_identifier("En-uS", Some("en"), None, Some("US"), None);
|
||||
assert_parsed_language_identifier("eN-lAtN-uS", Some("en"), Some("Latn"), Some("US"), None);
|
||||
assert_parsed_language_identifier("ZH_cyrl_hN", Some("zh"), Some("Cyrl"), Some("HN"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_langid() {
|
||||
let langid: LanguageIdentifier = "en-Latn-US".parse().unwrap();
|
||||
assert_eq!(&langid.to_string(), "en-Latn-US");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sorted_variants() {
|
||||
let langid: LanguageIdentifier = "en-nedis-macos".parse().unwrap();
|
||||
assert_eq!(&langid.to_string(), "en-macos-nedis");
|
||||
|
||||
let langid = LanguageIdentifier::from_parts(
|
||||
"en".parse().unwrap(),
|
||||
None,
|
||||
None,
|
||||
&["nedis".parse().unwrap(), "macos".parse().unwrap()],
|
||||
);
|
||||
assert_eq!(&langid.to_string(), "en-macos-nedis");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_parts_unchecked() {
|
||||
let langid: LanguageIdentifier = "en-nedis-macos".parse().unwrap();
|
||||
let (lang, script, region, variants) = langid.into_parts();
|
||||
let langid = LanguageIdentifier::from_raw_parts_unchecked(
|
||||
lang,
|
||||
script,
|
||||
region,
|
||||
Some(variants.into_boxed_slice()),
|
||||
);
|
||||
assert_eq!(&langid.to_string(), "en-macos-nedis");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches() {
|
||||
let langid_en: LanguageIdentifier = "en".parse().unwrap();
|
||||
let langid_en_us: LanguageIdentifier = "en-US".parse().unwrap();
|
||||
let langid_en_us2: LanguageIdentifier = "en-US".parse().unwrap();
|
||||
let langid_pl: LanguageIdentifier = "pl".parse().unwrap();
|
||||
assert_eq!(langid_en.matches(&langid_en_us, false, false), false);
|
||||
assert_eq!(langid_en_us.matches(&langid_en_us2, false, false), true);
|
||||
assert_eq!(langid_en.matches(&langid_pl, false, false), false);
|
||||
assert_eq!(langid_en.matches(&langid_en_us, true, false), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_fields() {
|
||||
let mut langid = LanguageIdentifier::default();
|
||||
assert_eq!(&langid.to_string(), "und");
|
||||
|
||||
langid.language = "pl".parse().expect("Setting language failed");
|
||||
assert_eq!(&langid.to_string(), "pl");
|
||||
|
||||
langid.language = "de".parse().expect("Setting language failed");
|
||||
assert_eq!(&langid.to_string(), "de");
|
||||
langid.region = Some("AT".parse().expect("Setting region failed"));
|
||||
assert_eq!(&langid.to_string(), "de-AT");
|
||||
langid.script = Some("Latn".parse().expect("Setting script failed"));
|
||||
assert_eq!(&langid.to_string(), "de-Latn-AT");
|
||||
langid.set_variants(&["macos".parse().expect("Setting variants failed")]);
|
||||
assert_eq!(&langid.to_string(), "de-Latn-AT-macos");
|
||||
|
||||
assert_eq!(langid.has_variant("macos".parse().unwrap()), true);
|
||||
assert_eq!(langid.has_variant("windows".parse().unwrap()), false);
|
||||
|
||||
langid.language.clear();
|
||||
assert_eq!(&langid.to_string(), "und-Latn-AT-macos");
|
||||
langid.region = None;
|
||||
assert_eq!(&langid.to_string(), "und-Latn-macos");
|
||||
langid.script = None;
|
||||
assert_eq!(&langid.to_string(), "und-macos");
|
||||
langid.clear_variants();
|
||||
assert_eq!(&langid.to_string(), "und");
|
||||
|
||||
assert_eq!(langid.has_variant("macos".parse().unwrap()), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches_as_range() {
|
||||
let langid: LanguageIdentifier = "en-US".parse().unwrap();
|
||||
let langid2: LanguageIdentifier = "en-US-windows".parse().unwrap();
|
||||
assert_eq!(langid.matches(&langid2, false, false), false);
|
||||
assert_eq!(langid.matches(&langid2, true, false), true);
|
||||
assert_eq!(langid.matches(&langid2, false, true), false);
|
||||
assert_eq!(langid.matches(&langid2, true, true), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_character_direction() {
|
||||
let langid: LanguageIdentifier = "en-US".parse().unwrap();
|
||||
let langid2: LanguageIdentifier = "ar-AF".parse().unwrap();
|
||||
assert_eq!(langid.character_direction(), CharacterDirection::LTR);
|
||||
assert_eq!(langid2.character_direction(), CharacterDirection::RTL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_langid_ord() {
|
||||
let input = &[
|
||||
"en-US-macos-zarab",
|
||||
"en-US-macos-nedis",
|
||||
"en-US-macos",
|
||||
"en-GB",
|
||||
"en",
|
||||
"en-US",
|
||||
"ar",
|
||||
"fr",
|
||||
"de",
|
||||
];
|
||||
|
||||
let mut langids = input
|
||||
.iter()
|
||||
.map(|l| -> LanguageIdentifier { l.parse().unwrap() })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
langids.sort();
|
||||
|
||||
let result = langids.iter().map(|l| l.to_string()).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
&result,
|
||||
&[
|
||||
"ar",
|
||||
"de",
|
||||
"en",
|
||||
"en-GB",
|
||||
"en-US",
|
||||
"en-US-macos",
|
||||
"en-US-macos-nedis",
|
||||
"en-US-macos-zarab",
|
||||
"fr"
|
||||
]
|
||||
);
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
use unic_langid_impl::likelysubtags::{maximize, minimize, CLDR_VERSION};
|
||||
use unic_langid_impl::subtags;
|
||||
|
||||
static STRINGS: &[(&str, Option<&str>)] = &[
|
||||
("en-US", Some("en-Latn-US")),
|
||||
("en-GB", Some("en-Latn-GB")),
|
||||
("es-AR", Some("es-Latn-AR")),
|
||||
("it", Some("it-Latn-IT")),
|
||||
("zh-Hans-CN", None),
|
||||
("de-AT", Some("de-Latn-AT")),
|
||||
("pl", Some("pl-Latn-PL")),
|
||||
("fr-FR", Some("fr-Latn-FR")),
|
||||
("de-AT", Some("de-Latn-AT")),
|
||||
("sr-Cyrl-SR", None),
|
||||
("nb-NO", Some("nb-Latn-NO")),
|
||||
("fr-FR", Some("fr-Latn-FR")),
|
||||
("mk", Some("mk-Cyrl-MK")),
|
||||
("uk", Some("uk-Cyrl-UA")),
|
||||
("und-PL", Some("pl-Latn-PL")),
|
||||
("und-Latn-AM", Some("ku-Latn-AM")),
|
||||
("ug-Cyrl", Some("ug-Cyrl-KZ")),
|
||||
("sr-ME", Some("sr-Latn-ME")),
|
||||
("mn-Mong", Some("mn-Mong-CN")),
|
||||
("lif-Limb", Some("lif-Limb-IN")),
|
||||
("gan", Some("gan-Hans-CN")),
|
||||
("zh-Hant", Some("zh-Hant-TW")),
|
||||
("yue-Hans", Some("yue-Hans-CN")),
|
||||
("unr", Some("unr-Beng-IN")),
|
||||
("unr-Deva", Some("unr-Deva-NP")),
|
||||
("und-Thai-CN", Some("lcp-Thai-CN")),
|
||||
("ug-Cyrl", Some("ug-Cyrl-KZ")),
|
||||
("en-Latn-DE", None),
|
||||
("pl-FR", Some("pl-Latn-FR")),
|
||||
("de-CH", Some("de-Latn-CH")),
|
||||
("tuq", Some("tuq-Latn")),
|
||||
("sr-ME", Some("sr-Latn-ME")),
|
||||
("ng", Some("ng-Latn-NA")),
|
||||
("klx", Some("klx-Latn")),
|
||||
("kk-Arab", Some("kk-Arab-CN")),
|
||||
("en-Cyrl", Some("en-Cyrl-US")),
|
||||
("und-Cyrl-UK", Some("ru-Cyrl-UK")),
|
||||
("und-Arab", Some("ar-Arab-EG")),
|
||||
("und-Arab-FO", Some("ar-Arab-FO")),
|
||||
("zh-TW", Some("zh-Hant-TW")),
|
||||
];
|
||||
|
||||
fn extract_input(
|
||||
s: &str,
|
||||
) -> (
|
||||
subtags::Language,
|
||||
Option<subtags::Script>,
|
||||
Option<subtags::Region>,
|
||||
) {
|
||||
let chunks: Vec<&str> = s.split("-").collect();
|
||||
let lang: subtags::Language = chunks[0].parse().unwrap();
|
||||
let (script, region) = if let Some(s) = chunks.get(1) {
|
||||
if let Ok(script) = s.parse() {
|
||||
let region = chunks.get(2).map(|r| r.parse().unwrap());
|
||||
(Some(script), region)
|
||||
} else {
|
||||
let region = s.parse().unwrap();
|
||||
(None, Some(region))
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
(lang, script, region)
|
||||
}
|
||||
|
||||
fn extract_output(
|
||||
s: Option<&str>,
|
||||
) -> Option<(
|
||||
subtags::Language,
|
||||
Option<subtags::Script>,
|
||||
Option<subtags::Region>,
|
||||
)> {
|
||||
s.map(|s| {
|
||||
let chunks: Vec<&str> = s.split("-").collect();
|
||||
(
|
||||
chunks[0].parse().unwrap(),
|
||||
chunks.get(1).map(|s| s.parse().unwrap()),
|
||||
chunks.get(2).map(|s| s.parse().unwrap()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maximize_test() {
|
||||
for i in STRINGS {
|
||||
let chunks = extract_input(i.0);
|
||||
let result = maximize(chunks.0, chunks.1, chunks.2);
|
||||
assert_eq!(extract_output(i.1), result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_works() {
|
||||
assert_eq!(CLDR_VERSION, "36");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimize_test() {
|
||||
let lang = "zh".parse().unwrap();
|
||||
let script = "Hant".parse().unwrap();
|
||||
let result = minimize(lang, Some(script), None);
|
||||
assert_eq!(result, Some(extract_input("zh-TW")));
|
||||
|
||||
let lang = "en".parse().unwrap();
|
||||
let script = "Latn".parse().unwrap();
|
||||
let region = "US".parse().unwrap();
|
||||
let result = minimize(lang, Some(script), Some(region));
|
||||
assert_eq!(result, Some(extract_input("en")));
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"37b883b01864161cffe562ddcc7226d60a6a104229da7b5f269a18738782efb7","README.md":"6bab9883bd094c16d22f7aeddb59b391c1567c167ffed2f28aee9e2a82de1a1d","src/lib.rs":"f850878a5d545496073593ef1ca340ef385f78bc6585c5d3b46fe8ffc2087bd5"},"package":"18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"}
|
||||
{"files":{"Cargo.toml":"debce559451d7edd257495d2f7a8c3bd081e1ec15625d42668165cec8f2a4cf7","README.md":"6bab9883bd094c16d22f7aeddb59b391c1567c167ffed2f28aee9e2a82de1a1d","src/lib.rs":"f850878a5d545496073593ef1ca340ef385f78bc6585c5d3b46fe8ffc2087bd5"},"package":"055e618bf694161ffff0466d95cef3e1a5edc59f6ba1888e97801f2b4ebdc4fe"}
|
|
@ -3,29 +3,37 @@
|
|||
# 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
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
# 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 = "2018"
|
||||
name = "unic-langid-macros"
|
||||
version = "0.9.0"
|
||||
authors = ["Manish Goregaokar <manishsmail@gmail.com>", "Zibi Braniecki <gandalf@mozilla.com>"]
|
||||
include = ["src/**/*", "benches/*.rs", "Cargo.toml", "README.md"]
|
||||
version = "0.9.1"
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Zibi Braniecki <gandalf@mozilla.com>",
|
||||
]
|
||||
include = [
|
||||
"src/**/*",
|
||||
"benches/*.rs",
|
||||
"Cargo.toml",
|
||||
"README.md",
|
||||
]
|
||||
description = "API for managing Unicode Language Identifiers"
|
||||
readme = "README.md"
|
||||
categories = ["internationalization"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/zbraniecki/unic-locale"
|
||||
|
||||
[dependencies.proc-macro-hack]
|
||||
version = "0.5"
|
||||
|
||||
[dependencies.tinystr]
|
||||
version = "0.3.2"
|
||||
version = "0.7.0"
|
||||
|
||||
[dependencies.unic-langid-impl]
|
||||
version = "0.9"
|
||||
|
|
Загрузка…
Ссылка в новой задаче