├── impl ├── LICENSE-MIT ├── LICENSE-APACHE ├── build.rs ├── Cargo.toml └── src │ └── lib.rs ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── tests ├── ui │ ├── bad_integer.rs │ ├── overflow.rs │ ├── ptrsized.rs │ ├── float.rs │ ├── bad_integer.stderr │ ├── overflow.stderr │ ├── ptrsized.stderr │ ├── incorrect_len.rs │ ├── float.stderr │ └── incorrect_len.stderr ├── compiletest.rs └── test.rs ├── src ├── eq.rs ├── format.rs ├── alphabet.rs ├── hash.rs ├── octal.rs ├── binary.rs ├── lower_exp.rs ├── lower_hex.rs ├── upper_exp.rs ├── upper_hex.rs ├── default.rs ├── ord.rs ├── display.rs ├── partial_eq.rs ├── debug.rs ├── serialize.rs ├── partial_ord.rs ├── lib.rs ├── value.rs ├── string.rs └── deserialize.rs ├── LICENSE-MIT ├── Cargo.toml ├── README.md └── LICENSE-APACHE /impl/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /impl/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dtolnay 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /tests/ui/bad_integer.rs: -------------------------------------------------------------------------------- 1 | use monostate::MustBe; 2 | 3 | fn main() { 4 | let _ = MustBe!(1u15); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ui/overflow.rs: -------------------------------------------------------------------------------- 1 | use monostate::MustBe; 2 | 3 | fn main() { 4 | let _ = MustBe!(1000u8); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ui/ptrsized.rs: -------------------------------------------------------------------------------- 1 | use monostate::MustBe; 2 | 3 | fn main() { 4 | let _ = MustBe!(1usize); 5 | let _ = MustBe!(1isize); 6 | } 7 | -------------------------------------------------------------------------------- /tests/ui/float.rs: -------------------------------------------------------------------------------- 1 | use monostate::MustBe; 2 | 3 | fn main() { 4 | let _ = MustBe!(1.0); 5 | let _ = MustBe!(b"..."); 6 | let _ = MustBe!(c"..."); 7 | } 8 | -------------------------------------------------------------------------------- /tests/ui/bad_integer.stderr: -------------------------------------------------------------------------------- 1 | error: unsupported integers suffix `u15` 2 | --> tests/ui/bad_integer.rs:4:21 3 | | 4 | 4 | let _ = MustBe!(1u15); 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | #[rustversion::attr(not(nightly), ignore = "requires nightly")] 2 | #[cfg_attr(miri, ignore = "incompatible with miri")] 3 | #[test] 4 | fn ui() { 5 | let t = trybuild::TestCases::new(); 6 | t.compile_fail("tests/ui/*.rs"); 7 | } 8 | -------------------------------------------------------------------------------- /impl/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Warning: build.rs is not published to crates.io. 3 | 4 | println!("cargo:rerun-if-changed=build.rs"); 5 | println!("cargo:rustc-cfg=check_cfg"); 6 | println!("cargo:rustc-check-cfg=cfg(check_cfg)"); 7 | println!("cargo:rustc-check-cfg=cfg(exhaustive)"); 8 | } 9 | -------------------------------------------------------------------------------- /tests/ui/overflow.stderr: -------------------------------------------------------------------------------- 1 | error: literal out of range for `u8` 2 | --> tests/ui/overflow.rs:4:21 3 | | 4 | 4 | let _ = MustBe!(1000u8); 5 | | ^^^^^^ 6 | | 7 | = note: the literal `1000u8` does not fit into the type `u8` whose range is `0..=255` 8 | = note: `#[deny(overflowing_literals)]` on by default 9 | -------------------------------------------------------------------------------- /tests/ui/ptrsized.stderr: -------------------------------------------------------------------------------- 1 | error: serde data model only uses consistently sized integer types, not usize 2 | --> tests/ui/ptrsized.rs:4:21 3 | | 4 | 4 | let _ = MustBe!(1usize); 5 | | ^^^^^^ 6 | 7 | error: serde data model only uses consistently sized integer types, not isize 8 | --> tests/ui/ptrsized.rs:5:21 9 | | 10 | 5 | let _ = MustBe!(1isize); 11 | | ^^^^^^ 12 | -------------------------------------------------------------------------------- /tests/ui/incorrect_len.rs: -------------------------------------------------------------------------------- 1 | // This error should be unreachable using the public API of monostate. Testing 2 | // it anyway. 3 | type __Private = ( 4 | monostate::alphabet::len<8>, 5 | ( 6 | monostate::alphabet::a, 7 | monostate::alphabet::s, 8 | monostate::alphabet::d, 9 | monostate::alphabet::f, 10 | ), 11 | ); 12 | 13 | struct N; 14 | 15 | fn main() { 16 | let _ = N::<{ monostate::MustBeStr::<__Private>::VALUE.len() }>; 17 | } 18 | -------------------------------------------------------------------------------- /tests/ui/float.stderr: -------------------------------------------------------------------------------- 1 | error: unsupported monostate literal kind 2 | --> tests/ui/float.rs:4:21 3 | | 4 | 4 | let _ = MustBe!(1.0); 5 | | ^^^ 6 | 7 | error: unsupported monostate literal kind 8 | --> tests/ui/float.rs:5:21 9 | | 10 | 5 | let _ = MustBe!(b"..."); 11 | | ^^^^^^ 12 | 13 | error: unsupported monostate literal kind 14 | --> tests/ui/float.rs:6:21 15 | | 16 | 6 | let _ = MustBe!(c"..."); 17 | | ^^^^^^ 18 | -------------------------------------------------------------------------------- /src/eq.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | 3 | impl Eq for crate::MustBeChar {} 4 | impl Eq for crate::MustBePosInt {} 5 | impl Eq for crate::MustBeNegInt {} 6 | impl Eq for crate::MustBeU8 {} 7 | impl Eq for crate::MustBeU16 {} 8 | impl Eq for crate::MustBeU32 {} 9 | impl Eq for crate::MustBeU64 {} 10 | impl Eq for crate::MustBeU128 {} 11 | impl Eq for crate::MustBeI8 {} 12 | impl Eq for crate::MustBeI16 {} 13 | impl Eq for crate::MustBeI32 {} 14 | impl Eq for crate::MustBeI64 {} 15 | impl Eq for crate::MustBeI128 {} 16 | impl Eq for crate::MustBeBool {} 17 | impl Eq for crate::MustBeStr {} 18 | -------------------------------------------------------------------------------- /src/format.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Write}; 2 | use core::str; 3 | 4 | pub struct Buf<'a> { 5 | bytes: &'a mut [u8], 6 | offset: usize, 7 | } 8 | 9 | impl<'a> Buf<'a> { 10 | pub fn new(bytes: &'a mut [u8]) -> Self { 11 | Buf { bytes, offset: 0 } 12 | } 13 | 14 | pub fn as_str(&self) -> &str { 15 | let slice = &self.bytes[..self.offset]; 16 | unsafe { str::from_utf8_unchecked(slice) } 17 | } 18 | } 19 | 20 | impl<'a> Write for Buf<'a> { 21 | fn write_str(&mut self, s: &str) -> fmt::Result { 22 | if self.offset + s.len() > self.bytes.len() { 23 | Err(fmt::Error) 24 | } else { 25 | self.bytes[self.offset..self.offset + s.len()].copy_from_slice(s.as_bytes()); 26 | self.offset += s.len(); 27 | Ok(()) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/alphabet.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | pub struct len; 3 | 4 | #[doc(hidden)] 5 | pub struct char; 6 | 7 | macro_rules! letters { 8 | ($($letter:ident)*) => { 9 | $( 10 | #[doc(hidden)] 11 | pub type $letter = char<{ 12 | stringify!($letter).as_bytes()[0] as core::primitive::char 13 | }>; 14 | )* 15 | }; 16 | } 17 | 18 | letters! { 19 | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 20 | a b c d e f g h i j k l m n o p q r s t u v w x y z 21 | } 22 | 23 | #[doc(hidden)] 24 | pub mod two { 25 | #[doc(hidden)] 26 | pub struct char; 27 | } 28 | 29 | #[doc(hidden)] 30 | pub mod three { 31 | #[doc(hidden)] 32 | pub struct char; 33 | } 34 | 35 | #[doc(hidden)] 36 | pub mod four { 37 | #[doc(hidden)] 38 | pub struct char; 39 | } 40 | -------------------------------------------------------------------------------- /impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monostate-impl" 3 | version = "1.0.2" 4 | authors = ["David Tolnay "] 5 | description = "Implementation detail of the monostate crate" 6 | edition = "2021" 7 | exclude = ["build.rs"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/dtolnay/monostate" 10 | rust-version = "1.79" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | proc-macro2 = "1.0.80" 17 | quote = "1.0.35" 18 | syn = { version = "2.0.59", default-features = false, features = ["parsing", "proc-macro"] } 19 | 20 | [package.metadata.docs.rs] 21 | targets = ["x86_64-unknown-linux-gnu"] 22 | rustdoc-args = [ 23 | "--generate-link-to-definition", 24 | "--generate-macro-expansion", 25 | "--extern-html-root-url=core=https://doc.rust-lang.org", 26 | "--extern-html-root-url=alloc=https://doc.rust-lang.org", 27 | "--extern-html-root-url=std=https://doc.rust-lang.org", 28 | "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", 29 | ] 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /tests/ui/incorrect_len.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation panicked: assertion failed: N == mem::size_of::() 2 | --> src/string.rs 3 | | 4 | | assert!(N == mem::size_of::()); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `<(monostate::alphabet::len<8>, (monostate::alphabet::char<'a'>, monostate::alphabet::char<'s'>, monostate::alphabet::char<'d'>, monostate::alphabet::char<'f'>)) as monostate::string::Sealed>::__private::{constant#0}` failed here 6 | 7 | note: erroneous constant encountered 8 | --> src/string.rs 9 | | 10 | | / const { 11 | | | assert!(N == mem::size_of::()); 12 | | | } 13 | | |_________^ 14 | 15 | note: erroneous constant encountered 16 | --> src/string.rs 17 | | 18 | | const VALUE: &'static str = T::__private.0; 19 | | ^^^^^^^^^^^^ 20 | 21 | note: erroneous constant encountered 22 | --> src/value.rs 23 | | 24 | | pub const VALUE: &'static str = V::VALUE; 25 | | ^^^^^^^^ 26 | 27 | note: erroneous constant encountered 28 | --> tests/ui/incorrect_len.rs:16:19 29 | | 30 | 16 | let _ = N::<{ monostate::MustBeStr::<__Private>::VALUE.len() }>; 31 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monostate" 3 | version = "1.0.2" 4 | authors = ["David Tolnay "] 5 | categories = ["encoding", "rust-patterns", "no-std", "no-std::no-alloc"] 6 | description = "Type that deserializes only from one specific value" 7 | documentation = "https://docs.rs/monostate" 8 | edition = "2021" 9 | keywords = ["serde", "serialization"] 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/dtolnay/monostate" 12 | rust-version = "1.79" 13 | 14 | [dependencies] 15 | monostate-impl = { version = "=1.0.2", path = "impl" } 16 | serde_core = { version = "1.0.220", default-features = false } 17 | 18 | [target.'cfg(any())'.dependencies] 19 | serde = { version = "1.0.220", default-features = false } 20 | 21 | [dev-dependencies] 22 | rustversion = "1.0.6" 23 | serde = { version = "1.0.220", features = ["derive"] } 24 | serde_json = "1.0.99" 25 | trybuild = { version = "1.0.112", features = ["diff"] } 26 | 27 | [package.metadata.docs.rs] 28 | targets = ["x86_64-unknown-linux-gnu"] 29 | rustdoc-args = [ 30 | "--generate-link-to-definition", 31 | "--generate-macro-expansion", 32 | "--extern-html-root-url=core=https://doc.rust-lang.org", 33 | "--extern-html-root-url=alloc=https://doc.rust-lang.org", 34 | "--extern-html-root-url=std=https://doc.rust-lang.org", 35 | ] 36 | 37 | [workspace] 38 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::hash::{Hash, Hasher}; 3 | 4 | impl Hash for crate::MustBeChar { 5 | fn hash(&self, _: &mut H) {} 6 | } 7 | 8 | impl Hash for crate::MustBePosInt { 9 | fn hash(&self, _: &mut H) {} 10 | } 11 | 12 | impl Hash for crate::MustBeNegInt { 13 | fn hash(&self, _: &mut H) {} 14 | } 15 | 16 | impl Hash for crate::MustBeU8 { 17 | fn hash(&self, _: &mut H) {} 18 | } 19 | 20 | impl Hash for crate::MustBeU16 { 21 | fn hash(&self, _: &mut H) {} 22 | } 23 | 24 | impl Hash for crate::MustBeU32 { 25 | fn hash(&self, _: &mut H) {} 26 | } 27 | 28 | impl Hash for crate::MustBeU64 { 29 | fn hash(&self, _: &mut H) {} 30 | } 31 | 32 | impl Hash for crate::MustBeU128 { 33 | fn hash(&self, _: &mut H) {} 34 | } 35 | 36 | impl Hash for crate::MustBeI8 { 37 | fn hash(&self, _: &mut H) {} 38 | } 39 | 40 | impl Hash for crate::MustBeI16 { 41 | fn hash(&self, _: &mut H) {} 42 | } 43 | 44 | impl Hash for crate::MustBeI32 { 45 | fn hash(&self, _: &mut H) {} 46 | } 47 | 48 | impl Hash for crate::MustBeI64 { 49 | fn hash(&self, _: &mut H) {} 50 | } 51 | 52 | impl Hash for crate::MustBeI128 { 53 | fn hash(&self, _: &mut H) {} 54 | } 55 | 56 | impl Hash for crate::MustBeBool { 57 | fn hash(&self, _: &mut H) {} 58 | } 59 | 60 | impl Hash for crate::MustBeStr 61 | where 62 | V: ConstStr, 63 | { 64 | fn hash(&self, _: &mut H) {} 65 | } 66 | -------------------------------------------------------------------------------- /src/octal.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Octal}; 2 | 3 | impl Octal for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl Octal for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl Octal for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl Octal for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl Octal for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl Octal for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl Octal for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl Octal for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl Octal for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl Octal for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl Octal for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl Octal for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/binary.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Binary}; 2 | 3 | impl Binary for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl Binary for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl Binary for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl Binary for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl Binary for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl Binary for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl Binary for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl Binary for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl Binary for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl Binary for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl Binary for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl Binary for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/lower_exp.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, LowerExp}; 2 | 3 | impl LowerExp for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl LowerExp for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl LowerExp for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl LowerExp for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl LowerExp for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl LowerExp for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl LowerExp for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl LowerExp for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl LowerExp for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl LowerExp for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl LowerExp for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl LowerExp for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/lower_hex.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, LowerHex}; 2 | 3 | impl LowerHex for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl LowerHex for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl LowerHex for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl LowerHex for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl LowerHex for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl LowerHex for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl LowerHex for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl LowerHex for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl LowerHex for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl LowerHex for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl LowerHex for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl LowerHex for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/upper_exp.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, UpperExp}; 2 | 3 | impl UpperExp for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl UpperExp for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl UpperExp for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl UpperExp for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl UpperExp for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl UpperExp for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl UpperExp for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl UpperExp for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl UpperExp for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl UpperExp for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl UpperExp for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl UpperExp for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/upper_hex.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, UpperHex}; 2 | 3 | impl UpperHex for crate::MustBePosInt { 4 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 5 | V.fmt(f) 6 | } 7 | } 8 | 9 | impl UpperHex for crate::MustBeNegInt { 10 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 11 | V.fmt(f) 12 | } 13 | } 14 | 15 | impl UpperHex for crate::MustBeU8 { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | V.fmt(f) 18 | } 19 | } 20 | 21 | impl UpperHex for crate::MustBeU16 { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | V.fmt(f) 24 | } 25 | } 26 | 27 | impl UpperHex for crate::MustBeU32 { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | V.fmt(f) 30 | } 31 | } 32 | 33 | impl UpperHex for crate::MustBeU64 { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | V.fmt(f) 36 | } 37 | } 38 | 39 | impl UpperHex for crate::MustBeU128 { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | V.fmt(f) 42 | } 43 | } 44 | 45 | impl UpperHex for crate::MustBeI8 { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | V.fmt(f) 48 | } 49 | } 50 | 51 | impl UpperHex for crate::MustBeI16 { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | V.fmt(f) 54 | } 55 | } 56 | 57 | impl UpperHex for crate::MustBeI32 { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | V.fmt(f) 60 | } 61 | } 62 | 63 | impl UpperHex for crate::MustBeI64 { 64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 65 | V.fmt(f) 66 | } 67 | } 68 | 69 | impl UpperHex for crate::MustBeI128 { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | V.fmt(f) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/default.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | 3 | impl Default for crate::MustBeChar { 4 | fn default() -> Self { 5 | crate::MustBeChar:: 6 | } 7 | } 8 | 9 | impl Default for crate::MustBePosInt { 10 | fn default() -> Self { 11 | crate::MustBePosInt:: 12 | } 13 | } 14 | 15 | impl Default for crate::MustBeNegInt { 16 | fn default() -> Self { 17 | crate::MustBeNegInt:: 18 | } 19 | } 20 | 21 | impl Default for crate::MustBeU8 { 22 | fn default() -> Self { 23 | crate::MustBeU8:: 24 | } 25 | } 26 | 27 | impl Default for crate::MustBeU16 { 28 | fn default() -> Self { 29 | crate::MustBeU16:: 30 | } 31 | } 32 | 33 | impl Default for crate::MustBeU32 { 34 | fn default() -> Self { 35 | crate::MustBeU32:: 36 | } 37 | } 38 | 39 | impl Default for crate::MustBeU64 { 40 | fn default() -> Self { 41 | crate::MustBeU64:: 42 | } 43 | } 44 | 45 | impl Default for crate::MustBeU128 { 46 | fn default() -> Self { 47 | crate::MustBeU128:: 48 | } 49 | } 50 | 51 | impl Default for crate::MustBeI8 { 52 | fn default() -> Self { 53 | crate::MustBeI8:: 54 | } 55 | } 56 | 57 | impl Default for crate::MustBeI16 { 58 | fn default() -> Self { 59 | crate::MustBeI16:: 60 | } 61 | } 62 | 63 | impl Default for crate::MustBeI32 { 64 | fn default() -> Self { 65 | crate::MustBeI32:: 66 | } 67 | } 68 | 69 | impl Default for crate::MustBeI64 { 70 | fn default() -> Self { 71 | crate::MustBeI64:: 72 | } 73 | } 74 | 75 | impl Default for crate::MustBeI128 { 76 | fn default() -> Self { 77 | crate::MustBeI128:: 78 | } 79 | } 80 | 81 | impl Default for crate::MustBeBool { 82 | fn default() -> Self { 83 | crate::MustBeBool:: 84 | } 85 | } 86 | 87 | impl Default for crate::MustBeStr 88 | where 89 | V: ConstStr, 90 | { 91 | fn default() -> Self { 92 | crate::MustBeStr:: 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/ord.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::cmp::Ordering; 3 | 4 | impl Ord for crate::MustBeChar { 5 | fn cmp(&self, _: &Self) -> Ordering { 6 | Ordering::Equal 7 | } 8 | } 9 | 10 | impl Ord for crate::MustBePosInt { 11 | fn cmp(&self, _: &Self) -> Ordering { 12 | Ordering::Equal 13 | } 14 | } 15 | 16 | impl Ord for crate::MustBeNegInt { 17 | fn cmp(&self, _: &Self) -> Ordering { 18 | Ordering::Equal 19 | } 20 | } 21 | 22 | impl Ord for crate::MustBeU8 { 23 | fn cmp(&self, _: &Self) -> Ordering { 24 | Ordering::Equal 25 | } 26 | } 27 | 28 | impl Ord for crate::MustBeU16 { 29 | fn cmp(&self, _: &Self) -> Ordering { 30 | Ordering::Equal 31 | } 32 | } 33 | 34 | impl Ord for crate::MustBeU32 { 35 | fn cmp(&self, _: &Self) -> Ordering { 36 | Ordering::Equal 37 | } 38 | } 39 | 40 | impl Ord for crate::MustBeU64 { 41 | fn cmp(&self, _: &Self) -> Ordering { 42 | Ordering::Equal 43 | } 44 | } 45 | 46 | impl Ord for crate::MustBeU128 { 47 | fn cmp(&self, _: &Self) -> Ordering { 48 | Ordering::Equal 49 | } 50 | } 51 | 52 | impl Ord for crate::MustBeI8 { 53 | fn cmp(&self, _: &Self) -> Ordering { 54 | Ordering::Equal 55 | } 56 | } 57 | 58 | impl Ord for crate::MustBeI16 { 59 | fn cmp(&self, _: &Self) -> Ordering { 60 | Ordering::Equal 61 | } 62 | } 63 | 64 | impl Ord for crate::MustBeI32 { 65 | fn cmp(&self, _: &Self) -> Ordering { 66 | Ordering::Equal 67 | } 68 | } 69 | 70 | impl Ord for crate::MustBeI64 { 71 | fn cmp(&self, _: &Self) -> Ordering { 72 | Ordering::Equal 73 | } 74 | } 75 | 76 | impl Ord for crate::MustBeI128 { 77 | fn cmp(&self, _: &Self) -> Ordering { 78 | Ordering::Equal 79 | } 80 | } 81 | 82 | impl Ord for crate::MustBeBool { 83 | fn cmp(&self, _: &Self) -> Ordering { 84 | Ordering::Equal 85 | } 86 | } 87 | 88 | impl Ord for crate::MustBeStr 89 | where 90 | V: ConstStr, 91 | { 92 | fn cmp(&self, _: &Self) -> Ordering { 93 | Ordering::Equal 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Monostate 2 | ========= 3 | 4 | [github](https://github.com/dtolnay/monostate) 5 | [crates.io](https://crates.io/crates/monostate) 6 | [docs.rs](https://docs.rs/monostate) 7 | [build status](https://github.com/dtolnay/monostate/actions?query=branch%3Amaster) 8 | 9 | This library implements a type macro for a zero-sized type that is Serde 10 | deserializable only from one specific value. 11 | 12 | ```toml 13 | [dependencies] 14 | monostate = "1" 15 | ``` 16 | 17 |
18 | 19 | ## Examples 20 | 21 | ```rust 22 | use monostate::MustBe; 23 | use serde::Deserialize; 24 | 25 | #[derive(Deserialize)] 26 | struct Example { 27 | kind: MustBe!("success"), 28 | code: MustBe!(200), 29 | } 30 | ``` 31 | 32 | The above struct would deserialize from `{"kind":"success", "code":200}` in 33 | JSON, but would fail the deserialization if "kind" or "code" were any other 34 | value. 35 | 36 | This can sometimes be helpful in processing untagged enums in which the variant 37 | identification is more convoluted than what is handled by Serde's externally 38 | tagged and internally tagged representations, for example because the variant 39 | tag has an inconsistent type or key. 40 | 41 | ```rust 42 | use monostate::MustBe; 43 | use serde::Deserialize; 44 | 45 | #[derive(Deserialize)] 46 | #[serde(untagged)] 47 | pub enum ApiResponse { 48 | Success { 49 | success: MustBe!(true), 50 | }, 51 | Error { 52 | kind: MustBe!("error"), 53 | message: String, 54 | }, 55 | } 56 | ``` 57 | 58 |
59 | 60 | #### License 61 | 62 | 63 | Licensed under either of Apache License, Version 64 | 2.0 or MIT license at your option. 65 | 66 | 67 |
68 | 69 | 70 | Unless you explicitly state otherwise, any contribution intentionally submitted 71 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 72 | be dual licensed as above, without any additional terms or conditions. 73 | 74 | -------------------------------------------------------------------------------- /src/display.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::fmt::{self, Display}; 3 | 4 | impl Display for crate::MustBeChar { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | V.fmt(f) 7 | } 8 | } 9 | 10 | impl Display for crate::MustBePosInt { 11 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 12 | V.fmt(f) 13 | } 14 | } 15 | 16 | impl Display for crate::MustBeNegInt { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | V.fmt(f) 19 | } 20 | } 21 | 22 | impl Display for crate::MustBeU8 { 23 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 24 | V.fmt(f) 25 | } 26 | } 27 | 28 | impl Display for crate::MustBeU16 { 29 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 30 | V.fmt(f) 31 | } 32 | } 33 | 34 | impl Display for crate::MustBeU32 { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | V.fmt(f) 37 | } 38 | } 39 | 40 | impl Display for crate::MustBeU64 { 41 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 | V.fmt(f) 43 | } 44 | } 45 | 46 | impl Display for crate::MustBeU128 { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | V.fmt(f) 49 | } 50 | } 51 | 52 | impl Display for crate::MustBeI8 { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 54 | V.fmt(f) 55 | } 56 | } 57 | 58 | impl Display for crate::MustBeI16 { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | V.fmt(f) 61 | } 62 | } 63 | 64 | impl Display for crate::MustBeI32 { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | V.fmt(f) 67 | } 68 | } 69 | 70 | impl Display for crate::MustBeI64 { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | V.fmt(f) 73 | } 74 | } 75 | 76 | impl Display for crate::MustBeI128 { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 | V.fmt(f) 79 | } 80 | } 81 | 82 | impl Display for crate::MustBeBool { 83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 84 | V.fmt(f) 85 | } 86 | } 87 | 88 | impl Display for crate::MustBeStr { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 90 | V::VALUE.fmt(f) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/partial_eq.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | 3 | impl PartialEq> for crate::MustBeChar { 4 | fn eq(&self, _: &crate::MustBeChar) -> bool { 5 | V == W 6 | } 7 | } 8 | 9 | impl PartialEq> for crate::MustBePosInt { 10 | fn eq(&self, _: &crate::MustBePosInt) -> bool { 11 | V == W 12 | } 13 | } 14 | 15 | impl PartialEq> for crate::MustBeNegInt { 16 | fn eq(&self, _: &crate::MustBeNegInt) -> bool { 17 | V == W 18 | } 19 | } 20 | 21 | impl PartialEq> for crate::MustBeU8 { 22 | fn eq(&self, _: &crate::MustBeU8) -> bool { 23 | V == W 24 | } 25 | } 26 | 27 | impl PartialEq> for crate::MustBeU16 { 28 | fn eq(&self, _: &crate::MustBeU16) -> bool { 29 | V == W 30 | } 31 | } 32 | 33 | impl PartialEq> for crate::MustBeU32 { 34 | fn eq(&self, _: &crate::MustBeU32) -> bool { 35 | V == W 36 | } 37 | } 38 | 39 | impl PartialEq> for crate::MustBeU64 { 40 | fn eq(&self, _: &crate::MustBeU64) -> bool { 41 | V == W 42 | } 43 | } 44 | 45 | impl PartialEq> for crate::MustBeU128 { 46 | fn eq(&self, _: &crate::MustBeU128) -> bool { 47 | V == W 48 | } 49 | } 50 | 51 | impl PartialEq> for crate::MustBeI8 { 52 | fn eq(&self, _: &crate::MustBeI8) -> bool { 53 | V == W 54 | } 55 | } 56 | 57 | impl PartialEq> for crate::MustBeI16 { 58 | fn eq(&self, _: &crate::MustBeI16) -> bool { 59 | V == W 60 | } 61 | } 62 | 63 | impl PartialEq> for crate::MustBeI32 { 64 | fn eq(&self, _: &crate::MustBeI32) -> bool { 65 | V == W 66 | } 67 | } 68 | 69 | impl PartialEq> for crate::MustBeI64 { 70 | fn eq(&self, _: &crate::MustBeI64) -> bool { 71 | V == W 72 | } 73 | } 74 | 75 | impl PartialEq> for crate::MustBeI128 { 76 | fn eq(&self, _: &crate::MustBeI128) -> bool { 77 | V == W 78 | } 79 | } 80 | 81 | impl PartialEq> for crate::MustBeBool { 82 | fn eq(&self, _: &crate::MustBeBool) -> bool { 83 | V == W 84 | } 85 | } 86 | 87 | impl PartialEq> for crate::MustBeStr 88 | where 89 | V: ConstStr, 90 | W: ConstStr, 91 | { 92 | fn eq(&self, _: &crate::MustBeStr) -> bool { 93 | V::VALUE == W::VALUE 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::fmt::{self, Debug}; 3 | 4 | impl Debug for crate::MustBeChar { 5 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 6 | write!(formatter, "MustBe!({:?})", V) 7 | } 8 | } 9 | 10 | impl Debug for crate::MustBePosInt { 11 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 12 | write!(formatter, "MustBe!({})", V) 13 | } 14 | } 15 | 16 | impl Debug for crate::MustBeNegInt { 17 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 18 | write!(formatter, "MustBe!({})", V) 19 | } 20 | } 21 | 22 | impl Debug for crate::MustBeU8 { 23 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 24 | write!(formatter, "MustBe!({}u8)", V) 25 | } 26 | } 27 | 28 | impl Debug for crate::MustBeU16 { 29 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 30 | write!(formatter, "MustBe!({}u16)", V) 31 | } 32 | } 33 | 34 | impl Debug for crate::MustBeU32 { 35 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 36 | write!(formatter, "MustBe!({}u32)", V) 37 | } 38 | } 39 | 40 | impl Debug for crate::MustBeU64 { 41 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 42 | write!(formatter, "MustBe!({}u64)", V) 43 | } 44 | } 45 | 46 | impl Debug for crate::MustBeU128 { 47 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 48 | write!(formatter, "MustBe!({}u128)", V) 49 | } 50 | } 51 | 52 | impl Debug for crate::MustBeI8 { 53 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 54 | write!(formatter, "MustBe!({}i8)", V) 55 | } 56 | } 57 | 58 | impl Debug for crate::MustBeI16 { 59 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 60 | write!(formatter, "MustBe!({}i16)", V) 61 | } 62 | } 63 | 64 | impl Debug for crate::MustBeI32 { 65 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 66 | write!(formatter, "MustBe!({}i32)", V) 67 | } 68 | } 69 | 70 | impl Debug for crate::MustBeI64 { 71 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 72 | write!(formatter, "MustBe!({}i64)", V) 73 | } 74 | } 75 | 76 | impl Debug for crate::MustBeI128 { 77 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 78 | write!(formatter, "MustBe!({}i128)", V) 79 | } 80 | } 81 | 82 | impl Debug for crate::MustBeBool { 83 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 84 | write!(formatter, "MustBe!({})", V) 85 | } 86 | } 87 | 88 | impl Debug for crate::MustBeStr { 89 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 90 | write!(formatter, "MustBe!({:?})", V::VALUE) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: [cron: "40 1 * * *"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | env: 13 | RUSTFLAGS: -Dwarnings 14 | 15 | jobs: 16 | pre_ci: 17 | uses: dtolnay/.github/.github/workflows/pre_ci.yml@master 18 | 19 | test: 20 | name: Rust ${{matrix.rust}} 21 | needs: pre_ci 22 | if: needs.pre_ci.outputs.continue 23 | runs-on: ubuntu-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | rust: [nightly, beta, stable, 1.82.0, 1.79.0] 28 | timeout-minutes: 45 29 | steps: 30 | - uses: actions/checkout@v6 31 | - uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: ${{matrix.rust}} 34 | - name: Enable type layout randomization 35 | run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV 36 | if: matrix.rust == 'nightly' 37 | - run: cargo test --workspace 38 | env: 39 | RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.rust == 'nightly' && '--cfg exhaustive' || ''}} 40 | - uses: actions/upload-artifact@v6 41 | if: matrix.rust == 'nightly' && always() 42 | with: 43 | name: Cargo.lock 44 | path: Cargo.lock 45 | continue-on-error: true 46 | 47 | minimal: 48 | name: Minimal versions 49 | needs: pre_ci 50 | if: needs.pre_ci.outputs.continue 51 | runs-on: ubuntu-latest 52 | timeout-minutes: 45 53 | steps: 54 | - uses: actions/checkout@v6 55 | - uses: dtolnay/rust-toolchain@nightly 56 | - run: cargo generate-lockfile -Z minimal-versions 57 | - run: cargo check --locked 58 | 59 | doc: 60 | name: Documentation 61 | needs: pre_ci 62 | if: needs.pre_ci.outputs.continue 63 | runs-on: ubuntu-latest 64 | timeout-minutes: 45 65 | env: 66 | RUSTDOCFLAGS: -Dwarnings 67 | steps: 68 | - uses: actions/checkout@v6 69 | - uses: dtolnay/rust-toolchain@nightly 70 | - uses: dtolnay/install@cargo-docs-rs 71 | - run: cargo docs-rs 72 | 73 | clippy: 74 | name: Clippy 75 | runs-on: ubuntu-latest 76 | if: github.event_name != 'pull_request' 77 | timeout-minutes: 45 78 | steps: 79 | - uses: actions/checkout@v6 80 | - uses: dtolnay/rust-toolchain@clippy 81 | - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic 82 | 83 | miri: 84 | name: Miri 85 | needs: pre_ci 86 | if: needs.pre_ci.outputs.continue 87 | runs-on: ubuntu-latest 88 | timeout-minutes: 45 89 | steps: 90 | - uses: actions/checkout@v6 91 | - uses: dtolnay/rust-toolchain@miri 92 | - run: cargo miri setup 93 | - run: cargo miri test 94 | env: 95 | MIRIFLAGS: -Zmiri-strict-provenance 96 | 97 | outdated: 98 | name: Outdated 99 | runs-on: ubuntu-latest 100 | if: github.event_name != 'pull_request' 101 | timeout-minutes: 45 102 | steps: 103 | - uses: actions/checkout@v6 104 | - uses: dtolnay/rust-toolchain@stable 105 | - uses: dtolnay/install@cargo-outdated 106 | - run: cargo outdated --workspace --exit-code 1 107 | -------------------------------------------------------------------------------- /src/serialize.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use serde::{Serialize, Serializer}; 3 | 4 | impl Serialize for crate::MustBeChar { 5 | fn serialize(&self, serializer: S) -> Result 6 | where 7 | S: Serializer, 8 | { 9 | serializer.serialize_char(V) 10 | } 11 | } 12 | 13 | impl Serialize for crate::MustBeU8 { 14 | fn serialize(&self, serializer: S) -> Result 15 | where 16 | S: Serializer, 17 | { 18 | serializer.serialize_u8(V) 19 | } 20 | } 21 | 22 | impl Serialize for crate::MustBeU16 { 23 | fn serialize(&self, serializer: S) -> Result 24 | where 25 | S: Serializer, 26 | { 27 | serializer.serialize_u16(V) 28 | } 29 | } 30 | 31 | impl Serialize for crate::MustBeU32 { 32 | fn serialize(&self, serializer: S) -> Result 33 | where 34 | S: Serializer, 35 | { 36 | serializer.serialize_u32(V) 37 | } 38 | } 39 | 40 | impl Serialize for crate::MustBeU64 { 41 | fn serialize(&self, serializer: S) -> Result 42 | where 43 | S: Serializer, 44 | { 45 | serializer.serialize_u64(V) 46 | } 47 | } 48 | 49 | impl Serialize for crate::MustBeU128 { 50 | fn serialize(&self, serializer: S) -> Result 51 | where 52 | S: Serializer, 53 | { 54 | serializer.serialize_u128(V) 55 | } 56 | } 57 | 58 | impl Serialize for crate::MustBeI8 { 59 | fn serialize(&self, serializer: S) -> Result 60 | where 61 | S: Serializer, 62 | { 63 | serializer.serialize_i8(V) 64 | } 65 | } 66 | 67 | impl Serialize for crate::MustBeI16 { 68 | fn serialize(&self, serializer: S) -> Result 69 | where 70 | S: Serializer, 71 | { 72 | serializer.serialize_i16(V) 73 | } 74 | } 75 | 76 | impl Serialize for crate::MustBeI32 { 77 | fn serialize(&self, serializer: S) -> Result 78 | where 79 | S: Serializer, 80 | { 81 | serializer.serialize_i32(V) 82 | } 83 | } 84 | 85 | impl Serialize for crate::MustBeI64 { 86 | fn serialize(&self, serializer: S) -> Result 87 | where 88 | S: Serializer, 89 | { 90 | serializer.serialize_i64(V) 91 | } 92 | } 93 | 94 | impl Serialize for crate::MustBeI128 { 95 | fn serialize(&self, serializer: S) -> Result 96 | where 97 | S: Serializer, 98 | { 99 | serializer.serialize_i128(V) 100 | } 101 | } 102 | 103 | impl Serialize for crate::MustBeBool { 104 | fn serialize(&self, serializer: S) -> Result 105 | where 106 | S: Serializer, 107 | { 108 | serializer.serialize_bool(V) 109 | } 110 | } 111 | 112 | impl Serialize for crate::MustBeStr { 113 | fn serialize(&self, serializer: S) -> Result 114 | where 115 | S: Serializer, 116 | { 117 | serializer.serialize_str(V::VALUE) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/partial_ord.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::cmp::Ordering; 3 | 4 | impl PartialOrd> for crate::MustBeChar { 5 | fn partial_cmp(&self, _: &crate::MustBeChar) -> Option { 6 | Some(V.cmp(&W)) 7 | } 8 | } 9 | 10 | impl PartialOrd> for crate::MustBePosInt { 11 | fn partial_cmp(&self, _: &crate::MustBePosInt) -> Option { 12 | Some(V.cmp(&W)) 13 | } 14 | } 15 | 16 | impl PartialOrd> for crate::MustBeNegInt { 17 | fn partial_cmp(&self, _: &crate::MustBeNegInt) -> Option { 18 | Some(V.cmp(&W)) 19 | } 20 | } 21 | 22 | impl PartialOrd> for crate::MustBeU8 { 23 | fn partial_cmp(&self, _: &crate::MustBeU8) -> Option { 24 | Some(V.cmp(&W)) 25 | } 26 | } 27 | 28 | impl PartialOrd> for crate::MustBeU16 { 29 | fn partial_cmp(&self, _: &crate::MustBeU16) -> Option { 30 | Some(V.cmp(&W)) 31 | } 32 | } 33 | 34 | impl PartialOrd> for crate::MustBeU32 { 35 | fn partial_cmp(&self, _: &crate::MustBeU32) -> Option { 36 | Some(V.cmp(&W)) 37 | } 38 | } 39 | 40 | impl PartialOrd> for crate::MustBeU64 { 41 | fn partial_cmp(&self, _: &crate::MustBeU64) -> Option { 42 | Some(V.cmp(&W)) 43 | } 44 | } 45 | 46 | impl PartialOrd> for crate::MustBeU128 { 47 | fn partial_cmp(&self, _: &crate::MustBeU128) -> Option { 48 | Some(V.cmp(&W)) 49 | } 50 | } 51 | 52 | impl PartialOrd> for crate::MustBeI8 { 53 | fn partial_cmp(&self, _: &crate::MustBeI8) -> Option { 54 | Some(V.cmp(&W)) 55 | } 56 | } 57 | 58 | impl PartialOrd> for crate::MustBeI16 { 59 | fn partial_cmp(&self, _: &crate::MustBeI16) -> Option { 60 | Some(V.cmp(&W)) 61 | } 62 | } 63 | 64 | impl PartialOrd> for crate::MustBeI32 { 65 | fn partial_cmp(&self, _: &crate::MustBeI32) -> Option { 66 | Some(V.cmp(&W)) 67 | } 68 | } 69 | 70 | impl PartialOrd> for crate::MustBeI64 { 71 | fn partial_cmp(&self, _: &crate::MustBeI64) -> Option { 72 | Some(V.cmp(&W)) 73 | } 74 | } 75 | 76 | impl PartialOrd> for crate::MustBeI128 { 77 | fn partial_cmp(&self, _: &crate::MustBeI128) -> Option { 78 | Some(V.cmp(&W)) 79 | } 80 | } 81 | 82 | impl PartialOrd> for crate::MustBeBool { 83 | fn partial_cmp(&self, _: &crate::MustBeBool) -> Option { 84 | Some(V.cmp(&W)) 85 | } 86 | } 87 | 88 | impl PartialOrd> for crate::MustBeStr 89 | where 90 | V: ConstStr, 91 | W: ConstStr, 92 | { 93 | fn partial_cmp(&self, _: &crate::MustBeStr) -> Option { 94 | Some(V::VALUE.cmp(W::VALUE)) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! [![github]](https://github.com/dtolnay/monostate) [![crates-io]](https://crates.io/crates/monostate) [![docs-rs]](https://docs.rs/monostate) 2 | //! 3 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github 4 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 6 | //! 7 | //!
8 | //! 9 | //! This library implements a type macro for a zero-sized type that is Serde 10 | //! deserializable only from one specific value. 11 | //! 12 | //! # Examples 13 | //! 14 | //! ``` 15 | //! use monostate::MustBe; 16 | //! use serde::Deserialize; 17 | //! 18 | //! #[derive(Deserialize)] 19 | //! struct Example { 20 | //! kind: MustBe!("success"), 21 | //! code: MustBe!(200), 22 | //! } 23 | //! ``` 24 | //! 25 | //! The above struct would deserialize from `{"kind":"success", "code":200}` in 26 | //! JSON, but would fail the deserialization if "kind" or "code" were any other 27 | //! value. 28 | //! 29 | //! This can sometimes be helpful in processing untagged enums in which the 30 | //! variant identification is more convoluted than what is handled by Serde's 31 | //! externally tagged and internally tagged representations, for example because 32 | //! the variant tag has an inconsistent type or key. 33 | //! 34 | //! ``` 35 | //! use monostate::MustBe; 36 | //! use serde::Deserialize; 37 | //! 38 | //! #[derive(Deserialize)] 39 | //! #[serde(untagged)] 40 | //! pub enum ApiResponse { 41 | //! Success { 42 | //! success: MustBe!(true), 43 | //! }, 44 | //! Error { 45 | //! kind: MustBe!("error"), 46 | //! message: String, 47 | //! }, 48 | //! } 49 | //! ``` 50 | 51 | #![no_std] 52 | #![doc(html_root_url = "https://docs.rs/monostate/1.0.2")] 53 | #![allow(non_camel_case_types, non_upper_case_globals)] 54 | #![allow( 55 | clippy::cast_lossless, 56 | clippy::cast_sign_loss, 57 | clippy::derivable_impls, 58 | clippy::elidable_lifetime_names, 59 | clippy::expl_impl_clone_on_copy, 60 | clippy::missing_safety_doc, 61 | clippy::uninlined_format_args 62 | )] 63 | 64 | extern crate serde_core as serde; 65 | 66 | #[doc(hidden)] 67 | pub mod alphabet; 68 | mod binary; 69 | mod debug; 70 | mod default; 71 | mod deserialize; 72 | mod display; 73 | mod eq; 74 | mod format; 75 | mod hash; 76 | mod lower_exp; 77 | mod lower_hex; 78 | mod octal; 79 | mod ord; 80 | mod partial_eq; 81 | mod partial_ord; 82 | mod serialize; 83 | mod string; 84 | mod upper_exp; 85 | mod upper_hex; 86 | mod value; 87 | 88 | pub use crate::string::ConstStr; 89 | pub use crate::value::MustBe; 90 | pub use monostate_impl::MustBe; 91 | 92 | #[derive(Copy, Clone)] 93 | pub struct MustBeChar; 94 | 95 | #[derive(Copy, Clone)] 96 | #[doc(hidden)] 97 | pub struct MustBePosInt; 98 | 99 | #[derive(Copy, Clone)] 100 | #[doc(hidden)] 101 | pub struct MustBeNegInt; 102 | 103 | #[derive(Copy, Clone)] 104 | pub struct MustBeU8; 105 | 106 | #[derive(Copy, Clone)] 107 | pub struct MustBeU16; 108 | 109 | #[derive(Copy, Clone)] 110 | pub struct MustBeU32; 111 | 112 | #[derive(Copy, Clone)] 113 | pub struct MustBeU64; 114 | 115 | #[derive(Copy, Clone)] 116 | pub struct MustBeU128; 117 | 118 | #[derive(Copy, Clone)] 119 | pub struct MustBeI8; 120 | 121 | #[derive(Copy, Clone)] 122 | pub struct MustBeI16; 123 | 124 | #[derive(Copy, Clone)] 125 | pub struct MustBeI32; 126 | 127 | #[derive(Copy, Clone)] 128 | pub struct MustBeI64; 129 | 130 | #[derive(Copy, Clone)] 131 | pub struct MustBeI128; 132 | 133 | #[derive(Copy, Clone)] 134 | pub struct MustBeBool; 135 | 136 | #[allow(type_alias_bounds)] 137 | pub type MustBeStr = crate::string::MustBeStr; 138 | 139 | impl Copy for MustBeStr {} 140 | 141 | impl Clone for MustBeStr { 142 | fn clone(&self) -> Self { 143 | *self 144 | } 145 | } 146 | 147 | #[doc(hidden)] 148 | pub use self::string::value::*; 149 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use crate::string::ConstStr; 2 | use core::fmt::{Debug, Display}; 3 | use core::hash::Hash; 4 | use core::panic::{RefUnwindSafe, UnwindSafe}; 5 | use serde::Serialize; 6 | 7 | pub trait MustBe: Sealed { 8 | type Type: Copy 9 | + Debug 10 | + Default 11 | + Display 12 | + Hash 13 | + Ord 14 | + RefUnwindSafe 15 | + Send 16 | + Serialize 17 | + Sync 18 | + Unpin 19 | + UnwindSafe; 20 | const VALUE: Self::Type; 21 | } 22 | 23 | impl crate::MustBeChar { 24 | pub const VALUE: char = V; 25 | } 26 | 27 | impl MustBe for crate::MustBeChar { 28 | type Type = char; 29 | const VALUE: Self::Type = V; 30 | } 31 | 32 | impl crate::MustBeU8 { 33 | pub const VALUE: u8 = V; 34 | } 35 | 36 | impl MustBe for crate::MustBeU8 { 37 | type Type = u8; 38 | const VALUE: Self::Type = V; 39 | } 40 | 41 | impl crate::MustBeU16 { 42 | pub const VALUE: u16 = V; 43 | } 44 | 45 | impl MustBe for crate::MustBeU16 { 46 | type Type = u16; 47 | const VALUE: Self::Type = V; 48 | } 49 | 50 | impl crate::MustBeU32 { 51 | pub const VALUE: u32 = V; 52 | } 53 | 54 | impl MustBe for crate::MustBeU32 { 55 | type Type = u32; 56 | const VALUE: Self::Type = V; 57 | } 58 | 59 | impl crate::MustBeU64 { 60 | pub const VALUE: u64 = V; 61 | } 62 | 63 | impl MustBe for crate::MustBeU64 { 64 | type Type = u64; 65 | const VALUE: Self::Type = V; 66 | } 67 | 68 | impl crate::MustBeU128 { 69 | pub const VALUE: u128 = V; 70 | } 71 | 72 | impl MustBe for crate::MustBeU128 { 73 | type Type = u128; 74 | const VALUE: Self::Type = V; 75 | } 76 | 77 | impl crate::MustBeI8 { 78 | pub const VALUE: i8 = V; 79 | } 80 | 81 | impl MustBe for crate::MustBeI8 { 82 | type Type = i8; 83 | const VALUE: Self::Type = V; 84 | } 85 | 86 | impl crate::MustBeI16 { 87 | pub const VALUE: i16 = V; 88 | } 89 | 90 | impl MustBe for crate::MustBeI16 { 91 | type Type = i16; 92 | const VALUE: Self::Type = V; 93 | } 94 | 95 | impl crate::MustBeI32 { 96 | pub const VALUE: i32 = V; 97 | } 98 | 99 | impl MustBe for crate::MustBeI32 { 100 | type Type = i32; 101 | const VALUE: Self::Type = V; 102 | } 103 | 104 | impl crate::MustBeI64 { 105 | pub const VALUE: i64 = V; 106 | } 107 | 108 | impl MustBe for crate::MustBeI64 { 109 | type Type = i64; 110 | const VALUE: Self::Type = V; 111 | } 112 | 113 | impl crate::MustBeI128 { 114 | pub const VALUE: i128 = V; 115 | } 116 | 117 | impl MustBe for crate::MustBeI128 { 118 | type Type = i128; 119 | const VALUE: Self::Type = V; 120 | } 121 | 122 | impl crate::MustBeBool { 123 | pub const VALUE: bool = V; 124 | } 125 | 126 | impl MustBe for crate::MustBeBool { 127 | type Type = bool; 128 | const VALUE: Self::Type = V; 129 | } 130 | 131 | impl crate::MustBeStr { 132 | pub const VALUE: &'static str = V::VALUE; 133 | } 134 | 135 | impl MustBe for crate::MustBeStr { 136 | type Type = &'static str; 137 | const VALUE: Self::Type = V::VALUE; 138 | } 139 | 140 | pub trait Sealed {} 141 | impl Sealed for crate::MustBeChar {} 142 | impl Sealed for crate::MustBeU8 {} 143 | impl Sealed for crate::MustBeU16 {} 144 | impl Sealed for crate::MustBeU32 {} 145 | impl Sealed for crate::MustBeU64 {} 146 | impl Sealed for crate::MustBeU128 {} 147 | impl Sealed for crate::MustBeI8 {} 148 | impl Sealed for crate::MustBeI16 {} 149 | impl Sealed for crate::MustBeI32 {} 150 | impl Sealed for crate::MustBeI64 {} 151 | impl Sealed for crate::MustBeI128 {} 152 | impl Sealed for crate::MustBeBool {} 153 | impl Sealed for crate::MustBeStr {} 154 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | use crate::alphabet::{self, len}; 2 | use core::mem::{self, ManuallyDrop}; 3 | use core::slice; 4 | use core::str; 5 | 6 | // Equivalent to `pub struct MustBeStr;` but using 7 | // the type encoding described in impl/src/lib.rs to avoid depending on 8 | // #![feature(adt_const_params)] for now. 9 | pub enum MustBeStr { 10 | #[doc(hidden)] 11 | __Phantom(#[doc(hidden)] void::MustBeStr), 12 | MustBeStr, 13 | } 14 | 15 | pub mod value { 16 | #[doc(hidden)] 17 | pub use crate::string::MustBeStr::MustBeStr; 18 | } 19 | 20 | pub trait ConstStr: Sealed { 21 | const VALUE: &'static str; 22 | } 23 | 24 | #[doc(hidden)] 25 | impl ConstStr for T 26 | where 27 | T: Sealed, 28 | { 29 | const VALUE: &'static str = T::__private.0; 30 | } 31 | 32 | pub trait Sealed { 33 | #[allow(private_interfaces)] 34 | const __private: StringValue; 35 | } 36 | 37 | struct StringValue(&'static str); 38 | 39 | impl Sealed for (len, T) 40 | where 41 | T: StringBuffer, 42 | { 43 | #[allow(private_interfaces)] 44 | const __private: StringValue = { 45 | const { 46 | assert!(N == mem::size_of::()); 47 | } 48 | StringValue(unsafe { 49 | str::from_utf8_unchecked(slice::from_raw_parts( 50 | const { 51 | &Cast:: { 52 | encoded: ManuallyDrop::new(T::BYTES), 53 | } 54 | .array 55 | } 56 | .as_ptr(), 57 | N, 58 | )) 59 | }) 60 | }; 61 | } 62 | 63 | union Cast { 64 | encoded: ManuallyDrop, 65 | array: [u8; N], 66 | } 67 | 68 | const TAG_CONT: u8 = 0b1000_0000; 69 | const TAG_TWO_B: u8 = 0b1100_0000; 70 | const TAG_THREE_B: u8 = 0b1110_0000; 71 | const TAG_FOUR_B: u8 = 0b1111_0000; 72 | 73 | pub unsafe trait StringBuffer { 74 | // SAFETY: Must contain no padding bytes. Must have alignment of 1. 75 | type Type: 'static; 76 | // SAFETY: Contents viewed as bytes must be a valid UTF-8 encoding. 77 | const BYTES: Self::Type; 78 | } 79 | 80 | unsafe impl StringBuffer for alphabet::char { 81 | type Type = u8; 82 | const BYTES: Self::Type = CH as u8; 83 | } 84 | 85 | unsafe impl StringBuffer for alphabet::two::char { 86 | type Type = [u8; 2]; 87 | const BYTES: Self::Type = [ 88 | ((CH as u32 >> 6) & 0x1F) as u8 | TAG_TWO_B, 89 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 90 | ]; 91 | } 92 | 93 | unsafe impl StringBuffer for alphabet::three::char { 94 | type Type = [u8; 3]; 95 | const BYTES: Self::Type = [ 96 | ((CH as u32 >> 12) & 0x0F) as u8 | TAG_THREE_B, 97 | ((CH as u32 >> 6) & 0x3F) as u8 | TAG_CONT, 98 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 99 | ]; 100 | } 101 | 102 | unsafe impl StringBuffer for alphabet::four::char { 103 | type Type = [u8; 4]; 104 | const BYTES: Self::Type = [ 105 | ((CH as u32 >> 18) & 0x07) as u8 | TAG_FOUR_B, 106 | ((CH as u32 >> 12) & 0x3F) as u8 | TAG_CONT, 107 | ((CH as u32 >> 6) & 0x3F) as u8 | TAG_CONT, 108 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 109 | ]; 110 | } 111 | 112 | unsafe impl StringBuffer for () { 113 | type Type = (); 114 | const BYTES: Self::Type = (); 115 | } 116 | 117 | #[repr(C)] 118 | pub struct Concat2(A, B); 119 | 120 | unsafe impl StringBuffer for (A, B) 121 | where 122 | A: StringBuffer, 123 | B: StringBuffer, 124 | { 125 | type Type = Concat2; 126 | const BYTES: Self::Type = Concat2(A::BYTES, B::BYTES); 127 | } 128 | 129 | #[repr(C)] 130 | pub struct Concat3(A, B, C); 131 | 132 | unsafe impl StringBuffer for (A, B, C) 133 | where 134 | A: StringBuffer, 135 | B: StringBuffer, 136 | C: StringBuffer, 137 | { 138 | type Type = Concat3; 139 | const BYTES: Self::Type = Concat3(A::BYTES, B::BYTES, C::BYTES); 140 | } 141 | 142 | #[repr(C)] 143 | pub struct Concat4(A, B, C, D); 144 | 145 | unsafe impl StringBuffer for (A, B, C, D) 146 | where 147 | A: StringBuffer, 148 | B: StringBuffer, 149 | C: StringBuffer, 150 | D: StringBuffer, 151 | { 152 | type Type = Concat4; 153 | const BYTES: Self::Type = Concat4(A::BYTES, B::BYTES, C::BYTES, D::BYTES); 154 | } 155 | 156 | #[repr(C)] 157 | pub struct Concat5(A, B, C, D, E); 158 | 159 | unsafe impl StringBuffer for (A, B, C, D, E) 160 | where 161 | A: StringBuffer, 162 | B: StringBuffer, 163 | C: StringBuffer, 164 | D: StringBuffer, 165 | E: StringBuffer, 166 | { 167 | type Type = Concat5; 168 | const BYTES: Self::Type = Concat5(A::BYTES, B::BYTES, C::BYTES, D::BYTES, E::BYTES); 169 | } 170 | 171 | #[repr(C)] 172 | pub struct Concat6(A, B, C, D, E, F); 173 | 174 | unsafe impl StringBuffer for (A, B, C, D, E, F) 175 | where 176 | A: StringBuffer, 177 | B: StringBuffer, 178 | C: StringBuffer, 179 | D: StringBuffer, 180 | E: StringBuffer, 181 | F: StringBuffer, 182 | { 183 | type Type = Concat6; 184 | const BYTES: Self::Type = Concat6(A::BYTES, B::BYTES, C::BYTES, D::BYTES, E::BYTES, F::BYTES); 185 | } 186 | 187 | mod void { 188 | use core::marker::PhantomData; 189 | 190 | #[doc(hidden)] 191 | pub enum Void {} 192 | 193 | impl Copy for Void {} 194 | 195 | impl Clone for Void { 196 | fn clone(&self) -> Self { 197 | *self 198 | } 199 | } 200 | 201 | pub struct MustBeStr(PhantomData, #[doc(hidden)] pub Void); 202 | 203 | impl Copy for MustBeStr {} 204 | 205 | impl Clone for MustBeStr { 206 | fn clone(&self) -> Self { 207 | *self 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::derive_partial_eq_without_eq, 3 | clippy::let_underscore_untyped, 4 | clippy::uninlined_format_args 5 | )] 6 | 7 | use monostate::MustBe; 8 | use serde::{Deserialize, Serialize}; 9 | use std::fmt::Debug; 10 | use std::mem; 11 | 12 | #[test] 13 | fn test_serialize_deserialize() { 14 | #[derive(Deserialize, Serialize, Debug, PartialEq)] 15 | struct Struct { 16 | kind: MustBe!("Struct"), 17 | } 18 | 19 | let s = Struct { 20 | kind: MustBe!("Struct"), 21 | }; 22 | let j = serde_json::to_string(&s).unwrap(); 23 | assert_eq!(j, "{\"kind\":\"Struct\"}"); 24 | 25 | let s2 = serde_json::from_str::(&j).unwrap(); 26 | assert_eq!(s, s2); 27 | 28 | let bad_j = "{\"kind\":\"unknown\"}"; 29 | let err = serde_json::from_str::(bad_j).unwrap_err(); 30 | assert_eq!( 31 | err.to_string(), 32 | "invalid value: string \"unknown\", expected string \"Struct\" at line 1 column 17", 33 | ); 34 | } 35 | 36 | #[test] 37 | fn test_untagged_enum() { 38 | #[derive(Serialize, Deserialize)] 39 | #[serde(untagged)] 40 | enum ApiResponse { 41 | Success { 42 | success: MustBe!(true), 43 | }, 44 | Error { 45 | success: MustBe!(false), 46 | message: String, 47 | }, 48 | } 49 | 50 | let success = "{\"success\":true}"; 51 | let response: ApiResponse = serde_json::from_str(success).unwrap(); 52 | match response { 53 | ApiResponse::Success { 54 | success: MustBe!(true), 55 | } => {} 56 | ApiResponse::Error { .. } => panic!(), 57 | } 58 | 59 | let error = "{\"success\":false,\"message\":\"...\"}"; 60 | let response: ApiResponse = serde_json::from_str(error).unwrap(); 61 | match response { 62 | ApiResponse::Error { 63 | success: MustBe!(false), 64 | .. 65 | } => {} 66 | ApiResponse::Success { .. } => panic!(), 67 | } 68 | } 69 | 70 | #[test] 71 | fn test_debug() { 72 | #[track_caller] 73 | fn assert_debug(must_be: impl Debug, expected: &str) { 74 | assert_eq!(format!("{:?}", must_be), expected); 75 | } 76 | 77 | assert_debug(MustBe!('x'), "MustBe!('x')"); 78 | assert_debug(MustBe!(1), "MustBe!(1)"); 79 | assert_debug(MustBe!(-1), "MustBe!(-1)"); 80 | assert_debug(MustBe!(1u8), "MustBe!(1u8)"); 81 | assert_debug(MustBe!(1u16), "MustBe!(1u16)"); 82 | assert_debug(MustBe!(1u32), "MustBe!(1u32)"); 83 | assert_debug(MustBe!(1u64), "MustBe!(1u64)"); 84 | assert_debug(MustBe!(1u128), "MustBe!(1u128)"); 85 | assert_debug(MustBe!(1i8), "MustBe!(1i8)"); 86 | assert_debug(MustBe!(1i16), "MustBe!(1i16)"); 87 | assert_debug(MustBe!(1i32), "MustBe!(1i32)"); 88 | assert_debug(MustBe!(1i64), "MustBe!(1i64)"); 89 | assert_debug(MustBe!(1i128), "MustBe!(1i128)"); 90 | assert_debug(MustBe!(true), "MustBe!(true)"); 91 | assert_debug(MustBe!("string"), "MustBe!(\"string\")"); 92 | } 93 | 94 | #[test] 95 | fn test_display() { 96 | macro_rules! assert_display_eq { 97 | ($lit:literal) => { 98 | assert_eq!(format!("{}", $lit), format!("{}", MustBe!($lit))); 99 | }; 100 | } 101 | 102 | assert_display_eq!('x'); 103 | assert_display_eq!(1); 104 | assert_display_eq!(-1); 105 | assert_display_eq!(1u8); 106 | assert_display_eq!(1u16); 107 | assert_display_eq!(1u32); 108 | assert_display_eq!(1u64); 109 | assert_display_eq!(1u128); 110 | assert_display_eq!(1i8); 111 | assert_display_eq!(1i16); 112 | assert_display_eq!(1i32); 113 | assert_display_eq!(1i64); 114 | assert_display_eq!(1i128); 115 | assert_display_eq!(true); 116 | assert_display_eq!("string"); 117 | } 118 | 119 | #[test] 120 | fn test_cmp() { 121 | assert_eq!(MustBe!(4), MustBe!(4)); 122 | assert_ne!(MustBe!(4), MustBe!(5)); 123 | assert!(MustBe!(4) < MustBe!(5)); 124 | } 125 | 126 | #[test] 127 | fn test_long_string() { 128 | let _ = MustBe!("\ 129 | Rust is blazingly fast and memory-efficient: with no runtime or garbage collector, it can power performance-critical services, run on embedded devices, and easily integrate with other languages. \ 130 | Rust’s rich type system and ownership model guarantee memory-safety and thread-safety — enabling you to eliminate many classes of bugs at compile-time. \ 131 | Rust has great documentation, a friendly compiler with useful error messages, and top-notch tooling — an integrated package manager and build tool, smart multi-editor support with auto-completion and type inspections, an auto-formatter, and more.\ 132 | "); 133 | } 134 | 135 | #[test] 136 | fn test_utf8() { 137 | let string = "$£€𐍈"; 138 | let mut chars = string.chars(); 139 | assert_eq!(chars.next().unwrap().len_utf8(), 1); 140 | assert_eq!(chars.next().unwrap().len_utf8(), 2); 141 | assert_eq!(chars.next().unwrap().len_utf8(), 3); 142 | assert_eq!(chars.next().unwrap().len_utf8(), 4); 143 | assert!(chars.next().is_none()); 144 | 145 | let must_be = MustBe!("$£€𐍈"); 146 | assert_eq!( 147 | serde_json::to_string(string).unwrap(), 148 | serde_json::to_string(&must_be).unwrap(), 149 | ); 150 | } 151 | 152 | #[test] 153 | fn test_layout() { 154 | let must_be = MustBe!("s"); 155 | assert_eq!(0, mem::size_of_val(&must_be)); 156 | assert_eq!(1, mem::align_of_val(&must_be)); 157 | 158 | let must_be = MustBe!(1); 159 | assert_eq!(0, mem::size_of_val(&must_be)); 160 | assert_eq!(1, mem::align_of_val(&must_be)); 161 | } 162 | 163 | #[test] 164 | fn test_autotraits() { 165 | fn assert_send(_: impl Send) {} 166 | fn assert_sync(_: impl Sync) {} 167 | 168 | assert_send(MustBe!("")); 169 | assert_sync(MustBe!("")); 170 | assert_send(MustBe!(true)); 171 | assert_sync(MustBe!(true)); 172 | } 173 | 174 | #[test] 175 | fn test_pattern() { 176 | let MustBe!('x') = MustBe!('x'); 177 | let MustBe!(1) = MustBe!(1); 178 | let MustBe!(-1) = MustBe!(-1); 179 | let MustBe!(1u8) = MustBe!(1u8); 180 | let MustBe!(1u16) = MustBe!(1u16); 181 | let MustBe!(1u32) = MustBe!(1u32); 182 | let MustBe!(1u64) = MustBe!(1u64); 183 | let MustBe!(1u128) = MustBe!(1u128); 184 | let MustBe!(1i8) = MustBe!(1i8); 185 | let MustBe!(1i16) = MustBe!(1i16); 186 | let MustBe!(1i32) = MustBe!(1i32); 187 | let MustBe!(1i64) = MustBe!(1i64); 188 | let MustBe!(1i128) = MustBe!(1i128); 189 | let MustBe!(true) = MustBe!(true); 190 | } 191 | 192 | #[rustversion::since(1.82)] 193 | #[test] 194 | fn test_string_pattern() { 195 | // Since https://github.com/rust-lang/rust/pull/122792 196 | let MustBe!("string") = MustBe!("string"); 197 | } 198 | -------------------------------------------------------------------------------- /impl/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] 2 | #![allow( 3 | clippy::cast_lossless, 4 | clippy::manual_range_contains, 5 | clippy::match_same_arms, 6 | clippy::needless_pass_by_value, 7 | clippy::uninlined_format_args, 8 | clippy::unnecessary_wraps 9 | )] 10 | #![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))] 11 | 12 | use proc_macro::TokenStream; 13 | use proc_macro2::{Ident, Literal, Span, TokenStream as TokenStream2}; 14 | use quote::{quote, ToTokens}; 15 | use std::mem; 16 | use syn::{parse_macro_input, Error, Lit, LitInt, Result}; 17 | 18 | // Branching factor of the MustBeStr tuple type parameter. 19 | const K: usize = 6; 20 | 21 | #[allow(non_snake_case)] 22 | #[proc_macro] 23 | pub fn MustBe(input: TokenStream) -> TokenStream { 24 | let lit = parse_macro_input!(input as Lit); 25 | 26 | #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] 27 | let expanded = match lit { 28 | Lit::Str(lit) => must_be_str(lit.value()), 29 | Lit::Byte(lit) => must_be_byte(lit.value()), 30 | Lit::Char(lit) => must_be_char(lit.value()), 31 | Lit::Int(lit) => must_be_int(lit), 32 | Lit::Bool(lit) => must_be_bool(lit.value), 33 | Lit::ByteStr(_) | Lit::CStr(_) | Lit::Float(_) | Lit::Verbatim(_) => unsupported(lit), 34 | _ => unsupported(lit), 35 | }; 36 | 37 | expanded.unwrap_or_else(Error::into_compile_error).into() 38 | } 39 | 40 | // We encode the chars at two consecutive levels of a K-ary tree. 41 | // 42 | // Suppose K=3, then strings "", "a", "ab", "abc", … would be encoded to: 43 | // () 44 | // (a) 45 | // (a, b) 46 | // (a, b, c) 47 | // (a, b, (c, d)) 48 | // (a, b, (c, d, e)) 49 | // (a, (b, c), (d, e, f)) 50 | // (a, (b, c, d), (e, f, g)) 51 | // ((a, b), (c, d, e), (f, g, h)) 52 | // ((a, b, c), (d, e, f), (g, h, i)) 53 | // ((a, b, c), (d, e, f), (g, h, (i, j))) 54 | // ((a, b, c), (d, e, f), (g, h, (i, j, k))) 55 | // ((a, b, c), (d, e, f), (g, (h, i), (j, k l))) 56 | // 57 | // That last one in tree form is: 58 | // ╷ 59 | // ┌────┴┬──────┐ 60 | // ┌┴┬─┐ ┌┴┬─┐ ┌─┴┬───┐ 61 | // a b c d e f g ┌┴┐ ┌┴┬─┐ 62 | // h i j k l 63 | 64 | enum StrNode { 65 | Char(char), 66 | Tuple(Vec), 67 | } 68 | 69 | impl ToTokens for StrNode { 70 | fn to_tokens(&self, tokens: &mut TokenStream2) { 71 | tokens.extend(match self { 72 | StrNode::Char(ch) => { 73 | if let 'A'..='Z' | 'a'..='z' = ch { 74 | let mut buf = [0]; 75 | let name = ch.encode_utf8(&mut buf); 76 | let ident = Ident::new(name, Span::call_site()); 77 | quote!(::monostate::alphabet::#ident) 78 | } else { 79 | match ch.len_utf8() { 80 | 1 => quote!(::monostate::alphabet::char<#ch>), 81 | 2 => quote!(::monostate::alphabet::two::char<#ch>), 82 | 3 => quote!(::monostate::alphabet::three::char<#ch>), 83 | 4 => quote!(::monostate::alphabet::four::char<#ch>), 84 | _ => unreachable!(), 85 | } 86 | } 87 | } 88 | StrNode::Tuple(vec) => { 89 | let len = vec.len(); 90 | assert!(len >= 2 && len <= K, "len={}", len); 91 | quote!((#(#vec),*)) 92 | } 93 | }); 94 | } 95 | } 96 | 97 | fn must_be_str(value: String) -> Result { 98 | if value.is_empty() { 99 | return Ok(quote! { 100 | ::monostate::MustBeStr::<(::monostate::alphabet::len<0>, ())> 101 | }); 102 | } 103 | let mut nodes = Vec::new(); 104 | for ch in value.chars() { 105 | nodes.push(StrNode::Char(ch)); 106 | } 107 | // Find largest power of K smaller than len. 108 | let mut pow = 1; 109 | while pow * K < nodes.len() { 110 | pow *= K; 111 | } 112 | while nodes.len() > 1 { 113 | // Number of nodes in excess of the smaller of the two tree levels. 114 | let overage = nodes.len() - pow; 115 | // Every group of K-1 nodes which are beyond the smaller tree level can 116 | // be combined with 1 node from the smaller tree level to form a 117 | // K-tuple node. The number of tuples that need to be formed is 118 | // ceil[overage / (K-1)]. 119 | let num_tuple_nodes = (overage + K - 2) / (K - 1); 120 | // Number of nodes left needing to be inserted into a tuple. 121 | let mut remainder = num_tuple_nodes + overage; 122 | // Index of next node to be inserted into a tuple. 123 | let mut read = nodes.len() - remainder; 124 | // Index of the tuple currently being inserted into. 125 | let mut write = read; 126 | // True if we haven't yet made a Vec to hold the current tuple. 127 | let mut make_tuple = true; 128 | while let Some(node) = nodes.get_mut(read) { 129 | let next = mem::replace(node, StrNode::Char('\0')); 130 | if make_tuple { 131 | nodes[write] = StrNode::Tuple(Vec::with_capacity(K)); 132 | } 133 | if let StrNode::Tuple(vec) = &mut nodes[write] { 134 | vec.push(next); 135 | } else { 136 | unreachable!(); 137 | } 138 | remainder -= 1; 139 | make_tuple = remainder % K == 0; 140 | write += make_tuple as usize; 141 | read += 1; 142 | } 143 | nodes.truncate(pow); 144 | pow /= K; 145 | } 146 | let len = Literal::usize_unsuffixed(value.len()); 147 | let encoded = &nodes[0]; 148 | Ok(quote! { 149 | ::monostate::MustBeStr::<(::monostate::alphabet::len<#len>, #encoded)> 150 | }) 151 | } 152 | 153 | fn must_be_byte(value: u8) -> Result { 154 | Ok(quote!(::monostate::MustBeU8::<#value>)) 155 | } 156 | 157 | fn must_be_char(value: char) -> Result { 158 | Ok(quote!(::monostate::MustBeChar::<#value>)) 159 | } 160 | 161 | fn must_be_int(lit: LitInt) -> Result { 162 | let token = lit.token(); 163 | match lit.suffix() { 164 | "u8" => Ok(quote!(::monostate::MustBeU8::<#token>)), 165 | "u16" => Ok(quote!(::monostate::MustBeU16::<#token>)), 166 | "u32" => Ok(quote!(::monostate::MustBeU32::<#token>)), 167 | "u64" => Ok(quote!(::monostate::MustBeU64::<#token>)), 168 | "u128" => Ok(quote!(::monostate::MustBeU128::<#token>)), 169 | "i8" => Ok(quote!(::monostate::MustBeI8::<#token>)), 170 | "i16" => Ok(quote!(::monostate::MustBeI16::<#token>)), 171 | "i32" => Ok(quote!(::monostate::MustBeI32::<#token>)), 172 | "i64" => Ok(quote!(::monostate::MustBeI64::<#token>)), 173 | "i128" => Ok(quote!(::monostate::MustBeI128::<#token>)), 174 | "" => { 175 | if lit.base10_digits().starts_with('-') { 176 | Ok(quote!(::monostate::MustBeNegInt::<#token>)) 177 | } else { 178 | Ok(quote!(::monostate::MustBePosInt::<#token>)) 179 | } 180 | } 181 | suffix @ ("usize" | "isize") => { 182 | let msg = format!( 183 | "serde data model only uses consistently sized integer types, not {}", 184 | suffix, 185 | ); 186 | Err(Error::new(lit.span(), msg)) 187 | } 188 | suffix => { 189 | let msg = format!("unsupported integers suffix `{}`", suffix); 190 | Err(Error::new(lit.span(), msg)) 191 | } 192 | } 193 | } 194 | 195 | fn must_be_bool(value: bool) -> Result { 196 | Ok(quote!(::monostate::MustBeBool::<#value>)) 197 | } 198 | 199 | fn unsupported(lit: Lit) -> Result { 200 | Err(Error::new(lit.span(), "unsupported monostate literal kind")) 201 | } 202 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /src/deserialize.rs: -------------------------------------------------------------------------------- 1 | use crate::format; 2 | use crate::string::ConstStr; 3 | use core::fmt::{self, Write as _}; 4 | use core::str; 5 | use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor}; 6 | 7 | impl<'de, const V: char> Deserialize<'de> for crate::MustBeChar { 8 | fn deserialize(deserializer: D) -> Result 9 | where 10 | D: Deserializer<'de>, 11 | { 12 | struct MustBeCharVisitor(char); 13 | 14 | impl<'de> Visitor<'de> for MustBeCharVisitor { 15 | type Value = (); 16 | 17 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 18 | write!(formatter, "char {:?}", self.0) 19 | } 20 | 21 | fn visit_char(self, v: char) -> Result 22 | where 23 | E: Error, 24 | { 25 | if v == self.0 { 26 | Ok(()) 27 | } else { 28 | Err(E::invalid_value(Unexpected::Char(v), &self)) 29 | } 30 | } 31 | } 32 | 33 | deserializer 34 | .deserialize_char(MustBeCharVisitor(V)) 35 | .map(|()| crate::MustBeChar) 36 | } 37 | } 38 | 39 | impl<'de, const V: u128> Deserialize<'de> for crate::MustBePosInt { 40 | fn deserialize(deserializer: D) -> Result 41 | where 42 | D: Deserializer<'de>, 43 | { 44 | struct MustBePosIntVisitor(u128); 45 | 46 | impl<'de> Visitor<'de> for MustBePosIntVisitor { 47 | type Value = (); 48 | 49 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 50 | write!(formatter, "integer `{}`", self.0) 51 | } 52 | 53 | fn visit_i64(self, v: i64) -> Result 54 | where 55 | E: Error, 56 | { 57 | if v >= 0 && v as u128 == self.0 { 58 | Ok(()) 59 | } else { 60 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 61 | } 62 | } 63 | 64 | fn visit_i128(self, v: i128) -> Result 65 | where 66 | E: Error, 67 | { 68 | if v >= 0 && v as u128 == self.0 { 69 | Ok(()) 70 | } else { 71 | let mut buf = [0u8; 50]; 72 | let mut writer = format::Buf::new(&mut buf); 73 | write!(writer, "integer `{}`", v).unwrap(); 74 | Err(Error::invalid_value( 75 | Unexpected::Other(writer.as_str()), 76 | &self, 77 | )) 78 | } 79 | } 80 | 81 | fn visit_u64(self, v: u64) -> Result 82 | where 83 | E: Error, 84 | { 85 | if v as u128 == self.0 { 86 | Ok(()) 87 | } else { 88 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 89 | } 90 | } 91 | 92 | fn visit_u128(self, v: u128) -> Result 93 | where 94 | E: Error, 95 | { 96 | if v == self.0 { 97 | Ok(()) 98 | } else { 99 | let mut buf = [0u8; 49]; 100 | let mut writer = format::Buf::new(&mut buf); 101 | write!(writer, "integer `{}`", v).unwrap(); 102 | Err(Error::invalid_value( 103 | Unexpected::Other(writer.as_str()), 104 | &self, 105 | )) 106 | } 107 | } 108 | } 109 | 110 | deserializer 111 | .deserialize_any(MustBePosIntVisitor(V)) 112 | .map(|()| crate::MustBePosInt) 113 | } 114 | } 115 | 116 | impl<'de, const V: i128> Deserialize<'de> for crate::MustBeNegInt { 117 | fn deserialize(deserializer: D) -> Result 118 | where 119 | D: Deserializer<'de>, 120 | { 121 | struct MustBeNegIntVisitor(i128); 122 | 123 | impl<'de> Visitor<'de> for MustBeNegIntVisitor { 124 | type Value = (); 125 | 126 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 127 | write!(formatter, "integer `{}`", self.0) 128 | } 129 | 130 | fn visit_i64(self, v: i64) -> Result 131 | where 132 | E: Error, 133 | { 134 | if v as i128 == self.0 { 135 | Ok(()) 136 | } else { 137 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 138 | } 139 | } 140 | 141 | fn visit_i128(self, v: i128) -> Result 142 | where 143 | E: Error, 144 | { 145 | if v == self.0 { 146 | Ok(()) 147 | } else { 148 | let mut buf = [0u8; 50]; 149 | let mut writer = format::Buf::new(&mut buf); 150 | write!(writer, "integer `{}`", v).unwrap(); 151 | Err(Error::invalid_value( 152 | Unexpected::Other(writer.as_str()), 153 | &self, 154 | )) 155 | } 156 | } 157 | 158 | fn visit_u64(self, v: u64) -> Result 159 | where 160 | E: Error, 161 | { 162 | if v as i128 == self.0 { 163 | Ok(()) 164 | } else { 165 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 166 | } 167 | } 168 | 169 | fn visit_u128(self, v: u128) -> Result 170 | where 171 | E: Error, 172 | { 173 | if self.0 >= 0 && v == self.0 as u128 { 174 | Ok(()) 175 | } else { 176 | let mut buf = [0u8; 49]; 177 | let mut writer = format::Buf::new(&mut buf); 178 | write!(writer, "integer `{}`", v).unwrap(); 179 | Err(Error::invalid_value( 180 | Unexpected::Other(writer.as_str()), 181 | &self, 182 | )) 183 | } 184 | } 185 | } 186 | 187 | deserializer 188 | .deserialize_any(MustBeNegIntVisitor(V)) 189 | .map(|()| crate::MustBeNegInt) 190 | } 191 | } 192 | 193 | impl<'de, const V: u8> Deserialize<'de> for crate::MustBeU8 { 194 | fn deserialize(deserializer: D) -> Result 195 | where 196 | D: Deserializer<'de>, 197 | { 198 | struct MustBeU8Visitor(u8); 199 | 200 | impl<'de> Visitor<'de> for MustBeU8Visitor { 201 | type Value = (); 202 | 203 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 204 | write!(formatter, "integer `{}` as u8", self.0) 205 | } 206 | 207 | fn visit_u8(self, v: u8) -> Result 208 | where 209 | E: Error, 210 | { 211 | if v == self.0 { 212 | Ok(()) 213 | } else { 214 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 215 | } 216 | } 217 | } 218 | 219 | deserializer 220 | .deserialize_any(MustBeU8Visitor(V)) 221 | .map(|()| crate::MustBeU8) 222 | } 223 | } 224 | 225 | impl<'de, const V: u16> Deserialize<'de> for crate::MustBeU16 { 226 | fn deserialize(deserializer: D) -> Result 227 | where 228 | D: Deserializer<'de>, 229 | { 230 | struct MustBeU16Visitor(u16); 231 | 232 | impl<'de> Visitor<'de> for MustBeU16Visitor { 233 | type Value = (); 234 | 235 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 236 | write!(formatter, "integer `{}` as u16", self.0) 237 | } 238 | 239 | fn visit_u16(self, v: u16) -> Result 240 | where 241 | E: Error, 242 | { 243 | if v == self.0 { 244 | Ok(()) 245 | } else { 246 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 247 | } 248 | } 249 | } 250 | 251 | deserializer 252 | .deserialize_any(MustBeU16Visitor(V)) 253 | .map(|()| crate::MustBeU16) 254 | } 255 | } 256 | 257 | impl<'de, const V: u32> Deserialize<'de> for crate::MustBeU32 { 258 | fn deserialize(deserializer: D) -> Result 259 | where 260 | D: Deserializer<'de>, 261 | { 262 | struct MustBeU32Visitor(u32); 263 | 264 | impl<'de> Visitor<'de> for MustBeU32Visitor { 265 | type Value = (); 266 | 267 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 268 | write!(formatter, "integer `{}` as u32", self.0) 269 | } 270 | 271 | fn visit_u32(self, v: u32) -> Result 272 | where 273 | E: Error, 274 | { 275 | if v == self.0 { 276 | Ok(()) 277 | } else { 278 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 279 | } 280 | } 281 | } 282 | 283 | deserializer 284 | .deserialize_any(MustBeU32Visitor(V)) 285 | .map(|()| crate::MustBeU32) 286 | } 287 | } 288 | 289 | impl<'de, const V: u64> Deserialize<'de> for crate::MustBeU64 { 290 | fn deserialize(deserializer: D) -> Result 291 | where 292 | D: Deserializer<'de>, 293 | { 294 | struct MustBeU64Visitor(u64); 295 | 296 | impl<'de> Visitor<'de> for MustBeU64Visitor { 297 | type Value = (); 298 | 299 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 300 | write!(formatter, "integer `{}` as u64", self.0) 301 | } 302 | 303 | fn visit_u8(self, v: u8) -> Result 304 | where 305 | E: Error, 306 | { 307 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 308 | } 309 | 310 | fn visit_u16(self, v: u16) -> Result 311 | where 312 | E: Error, 313 | { 314 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 315 | } 316 | 317 | fn visit_u32(self, v: u32) -> Result 318 | where 319 | E: Error, 320 | { 321 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 322 | } 323 | 324 | fn visit_u64(self, v: u64) -> Result 325 | where 326 | E: Error, 327 | { 328 | if v == self.0 { 329 | Ok(()) 330 | } else { 331 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 332 | } 333 | } 334 | } 335 | 336 | deserializer 337 | .deserialize_any(MustBeU64Visitor(V)) 338 | .map(|()| crate::MustBeU64) 339 | } 340 | } 341 | 342 | impl<'de, const V: u128> Deserialize<'de> for crate::MustBeU128 { 343 | fn deserialize(deserializer: D) -> Result 344 | where 345 | D: Deserializer<'de>, 346 | { 347 | struct MustBeU128Visitor(u128); 348 | 349 | impl<'de> Visitor<'de> for MustBeU128Visitor { 350 | type Value = (); 351 | 352 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 353 | write!(formatter, "integer `{}` as u128", self.0) 354 | } 355 | 356 | fn visit_u128(self, v: u128) -> Result 357 | where 358 | E: Error, 359 | { 360 | if v == self.0 { 361 | Ok(()) 362 | } else { 363 | let mut buf = [0u8; 49]; 364 | let mut writer = format::Buf::new(&mut buf); 365 | write!(writer, "integer `{}`", v).unwrap(); 366 | Err(Error::invalid_value( 367 | Unexpected::Other(writer.as_str()), 368 | &self, 369 | )) 370 | } 371 | } 372 | } 373 | 374 | deserializer 375 | .deserialize_any(MustBeU128Visitor(V)) 376 | .map(|()| crate::MustBeU128) 377 | } 378 | } 379 | 380 | impl<'de, const V: i8> Deserialize<'de> for crate::MustBeI8 { 381 | fn deserialize(deserializer: D) -> Result 382 | where 383 | D: Deserializer<'de>, 384 | { 385 | struct MustBeI8Visitor(i8); 386 | 387 | impl<'de> Visitor<'de> for MustBeI8Visitor { 388 | type Value = (); 389 | 390 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 391 | write!(formatter, "integer `{}` as i8", self.0) 392 | } 393 | 394 | fn visit_i8(self, v: i8) -> Result 395 | where 396 | E: Error, 397 | { 398 | if v == self.0 { 399 | Ok(()) 400 | } else { 401 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 402 | } 403 | } 404 | } 405 | 406 | deserializer 407 | .deserialize_any(MustBeI8Visitor(V)) 408 | .map(|()| crate::MustBeI8) 409 | } 410 | } 411 | 412 | impl<'de, const V: i16> Deserialize<'de> for crate::MustBeI16 { 413 | fn deserialize(deserializer: D) -> Result 414 | where 415 | D: Deserializer<'de>, 416 | { 417 | struct MustBeI16Visitor(i16); 418 | 419 | impl<'de> Visitor<'de> for MustBeI16Visitor { 420 | type Value = (); 421 | 422 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 423 | write!(formatter, "integer `{}` as i16", self.0) 424 | } 425 | 426 | fn visit_i16(self, v: i16) -> Result 427 | where 428 | E: Error, 429 | { 430 | if v == self.0 { 431 | Ok(()) 432 | } else { 433 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 434 | } 435 | } 436 | } 437 | 438 | deserializer 439 | .deserialize_any(MustBeI16Visitor(V)) 440 | .map(|()| crate::MustBeI16) 441 | } 442 | } 443 | 444 | impl<'de, const V: i32> Deserialize<'de> for crate::MustBeI32 { 445 | fn deserialize(deserializer: D) -> Result 446 | where 447 | D: Deserializer<'de>, 448 | { 449 | struct MustBeI32Visitor(i32); 450 | 451 | impl<'de> Visitor<'de> for MustBeI32Visitor { 452 | type Value = (); 453 | 454 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 455 | write!(formatter, "integer `{}` as i32", self.0) 456 | } 457 | 458 | fn visit_i32(self, v: i32) -> Result 459 | where 460 | E: Error, 461 | { 462 | if v == self.0 { 463 | Ok(()) 464 | } else { 465 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 466 | } 467 | } 468 | } 469 | 470 | deserializer 471 | .deserialize_any(MustBeI32Visitor(V)) 472 | .map(|()| crate::MustBeI32) 473 | } 474 | } 475 | 476 | impl<'de, const V: i64> Deserialize<'de> for crate::MustBeI64 { 477 | fn deserialize(deserializer: D) -> Result 478 | where 479 | D: Deserializer<'de>, 480 | { 481 | struct MustBeI64Visitor(i64); 482 | 483 | impl<'de> Visitor<'de> for MustBeI64Visitor { 484 | type Value = (); 485 | 486 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 487 | write!(formatter, "integer `{}` as i64", self.0) 488 | } 489 | 490 | fn visit_i8(self, v: i8) -> Result 491 | where 492 | E: Error, 493 | { 494 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 495 | } 496 | 497 | fn visit_i16(self, v: i16) -> Result 498 | where 499 | E: Error, 500 | { 501 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 502 | } 503 | 504 | fn visit_i32(self, v: i32) -> Result 505 | where 506 | E: Error, 507 | { 508 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 509 | } 510 | 511 | fn visit_i64(self, v: i64) -> Result 512 | where 513 | E: Error, 514 | { 515 | if v == self.0 { 516 | Ok(()) 517 | } else { 518 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 519 | } 520 | } 521 | } 522 | 523 | deserializer 524 | .deserialize_any(MustBeI64Visitor(V)) 525 | .map(|()| crate::MustBeI64) 526 | } 527 | } 528 | 529 | impl<'de, const V: i128> Deserialize<'de> for crate::MustBeI128 { 530 | fn deserialize(deserializer: D) -> Result 531 | where 532 | D: Deserializer<'de>, 533 | { 534 | struct MustBeI128Visitor(i128); 535 | 536 | impl<'de> Visitor<'de> for MustBeI128Visitor { 537 | type Value = (); 538 | 539 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 540 | write!(formatter, "integer `{}` as i128", self.0) 541 | } 542 | 543 | fn visit_i128(self, v: i128) -> Result 544 | where 545 | E: Error, 546 | { 547 | if v == self.0 { 548 | Ok(()) 549 | } else { 550 | let mut buf = [0u8; 50]; 551 | let mut writer = format::Buf::new(&mut buf); 552 | write!(writer, "integer `{}`", v).unwrap(); 553 | Err(Error::invalid_value( 554 | Unexpected::Other(writer.as_str()), 555 | &self, 556 | )) 557 | } 558 | } 559 | } 560 | 561 | deserializer 562 | .deserialize_any(MustBeI128Visitor(V)) 563 | .map(|()| crate::MustBeI128) 564 | } 565 | } 566 | 567 | impl<'de, const V: bool> Deserialize<'de> for crate::MustBeBool { 568 | fn deserialize(deserializer: D) -> Result 569 | where 570 | D: Deserializer<'de>, 571 | { 572 | struct MustBeBoolVisitor(bool); 573 | 574 | impl<'de> Visitor<'de> for MustBeBoolVisitor { 575 | type Value = (); 576 | 577 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 578 | write!(formatter, "boolean `{}`", self.0) 579 | } 580 | 581 | fn visit_bool(self, v: bool) -> Result 582 | where 583 | E: Error, 584 | { 585 | if v == self.0 { 586 | Ok(()) 587 | } else { 588 | Err(E::invalid_value(Unexpected::Bool(v), &self)) 589 | } 590 | } 591 | } 592 | 593 | deserializer 594 | .deserialize_any(MustBeBoolVisitor(V)) 595 | .map(|()| crate::MustBeBool) 596 | } 597 | } 598 | 599 | impl<'de, V: ConstStr> Deserialize<'de> for crate::MustBeStr { 600 | fn deserialize(deserializer: D) -> Result 601 | where 602 | D: Deserializer<'de>, 603 | { 604 | struct MustBeStrVisitor(&'static str); 605 | 606 | impl<'de> Visitor<'de> for MustBeStrVisitor { 607 | type Value = (); 608 | 609 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 610 | write!(formatter, "string {:?}", self.0) 611 | } 612 | 613 | fn visit_str(self, v: &str) -> Result 614 | where 615 | E: Error, 616 | { 617 | if v == self.0 { 618 | Ok(()) 619 | } else { 620 | Err(E::invalid_value(Unexpected::Str(v), &self)) 621 | } 622 | } 623 | } 624 | 625 | deserializer 626 | .deserialize_any(MustBeStrVisitor(V::VALUE)) 627 | .map(|()| crate::MustBeStr) 628 | } 629 | } 630 | --------------------------------------------------------------------------------