├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── impl ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── build.rs └── src │ └── lib.rs ├── src ├── alphabet.rs ├── debug.rs ├── default.rs ├── deserialize.rs ├── eq.rs ├── format.rs ├── hash.rs ├── lib.rs ├── ord.rs ├── partial_eq.rs ├── partial_ord.rs ├── serialize.rs └── string.rs └── tests └── test.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dtolnay 2 | -------------------------------------------------------------------------------- /.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.61.0] 28 | timeout-minutes: 45 29 | steps: 30 | - uses: actions/checkout@v4 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@v4 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@v4 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@v4 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@v4 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@v4 91 | - uses: dtolnay/rust-toolchain@miri 92 | with: 93 | toolchain: nightly-2025-05-16 # https://github.com/rust-lang/miri/issues/4323 94 | - run: cargo miri setup 95 | - run: cargo miri test 96 | env: 97 | MIRIFLAGS: -Zmiri-strict-provenance 98 | 99 | outdated: 100 | name: Outdated 101 | runs-on: ubuntu-latest 102 | if: github.event_name != 'pull_request' 103 | timeout-minutes: 45 104 | steps: 105 | - uses: actions/checkout@v4 106 | - uses: dtolnay/rust-toolchain@stable 107 | - uses: dtolnay/install@cargo-outdated 108 | - run: cargo outdated --workspace --exit-code 1 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monostate" 3 | version = "0.1.14" 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.61" 13 | 14 | [dependencies] 15 | monostate-impl = { version = "=0.1.14", path = "impl" } 16 | serde = { version = "1.0.166", default-features = false } 17 | 18 | [dev-dependencies] 19 | serde = { version = "1.0.166", features = ["derive"] } 20 | serde_json = "1.0.99" 21 | 22 | [package.metadata.docs.rs] 23 | targets = ["x86_64-unknown-linux-gnu"] 24 | rustdoc-args = [ 25 | "--generate-link-to-definition", 26 | "--extern-html-root-url=core=https://doc.rust-lang.org", 27 | "--extern-html-root-url=alloc=https://doc.rust-lang.org", 28 | "--extern-html-root-url=std=https://doc.rust-lang.org", 29 | ] 30 | 31 | [workspace] 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 = "0.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 | -------------------------------------------------------------------------------- /impl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monostate-impl" 3 | version = "0.1.14" 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.61" 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 | "--extern-html-root-url=core=https://doc.rust-lang.org", 25 | "--extern-html-root-url=alloc=https://doc.rust-lang.org", 26 | "--extern-html-root-url=std=https://doc.rust-lang.org", 27 | "--extern-html-root-url=proc_macro=https://doc.rust-lang.org", 28 | ] 29 | -------------------------------------------------------------------------------- /impl/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /impl/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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, 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!(::monostate::MustBeStr::<()>)); 100 | } 101 | let mut nodes = Vec::new(); 102 | for ch in value.chars() { 103 | nodes.push(StrNode::Char(ch)); 104 | } 105 | // Find largest power of K smaller than len. 106 | let mut pow = 1; 107 | while pow * K < nodes.len() { 108 | pow *= K; 109 | } 110 | while nodes.len() > 1 { 111 | // Number of nodes in excess of the smaller of the two tree levels. 112 | let overage = nodes.len() - pow; 113 | // Every group of K-1 nodes which are beyond the smaller tree level can 114 | // be combined with 1 node from the smaller tree level to form a 115 | // K-tuple node. The number of tuples that need to be formed is 116 | // ceil[overage / (K-1)]. 117 | let num_tuple_nodes = (overage + K - 2) / (K - 1); 118 | // Number of nodes left needing to be inserted into a tuple. 119 | let mut remainder = num_tuple_nodes + overage; 120 | // Index of next node to be inserted into a tuple. 121 | let mut read = nodes.len() - remainder; 122 | // Index of the tuple currently being inserted into. 123 | let mut write = read; 124 | // True if we haven't yet made a Vec to hold the current tuple. 125 | let mut make_tuple = true; 126 | while let Some(node) = nodes.get_mut(read) { 127 | let next = mem::replace(node, StrNode::Char('\0')); 128 | if make_tuple { 129 | nodes[write] = StrNode::Tuple(Vec::with_capacity(K)); 130 | } 131 | if let StrNode::Tuple(vec) = &mut nodes[write] { 132 | vec.push(next); 133 | } else { 134 | unreachable!(); 135 | } 136 | remainder -= 1; 137 | make_tuple = remainder % K == 0; 138 | write += make_tuple as usize; 139 | read += 1; 140 | } 141 | nodes.truncate(pow); 142 | pow /= K; 143 | } 144 | let encoded = &nodes[0]; 145 | Ok(quote!(::monostate::MustBeStr::<#encoded>)) 146 | } 147 | 148 | fn must_be_byte(value: u8) -> Result { 149 | Ok(quote!(::monostate::MustBeU8::<#value>)) 150 | } 151 | 152 | fn must_be_char(value: char) -> Result { 153 | Ok(quote!(::monostate::MustBeChar::<#value>)) 154 | } 155 | 156 | fn must_be_int(lit: LitInt) -> Result { 157 | let token = lit.token(); 158 | match lit.suffix() { 159 | "u8" => Ok(quote!(::monostate::MustBeU8::<#token>)), 160 | "u16" => Ok(quote!(::monostate::MustBeU16::<#token>)), 161 | "u32" => Ok(quote!(::monostate::MustBeU32::<#token>)), 162 | "u64" => Ok(quote!(::monostate::MustBeU64::<#token>)), 163 | "u128" => Ok(quote!(::monostate::MustBeU128::<#token>)), 164 | "i8" => Ok(quote!(::monostate::MustBeI8::<#token>)), 165 | "i16" => Ok(quote!(::monostate::MustBeI16::<#token>)), 166 | "i32" => Ok(quote!(::monostate::MustBeI32::<#token>)), 167 | "i64" => Ok(quote!(::monostate::MustBeI64::<#token>)), 168 | "i128" => Ok(quote!(::monostate::MustBeI128::<#token>)), 169 | "" => { 170 | if lit.base10_digits().starts_with('-') { 171 | Ok(quote!(::monostate::MustBeNegInt::<#token>)) 172 | } else { 173 | Ok(quote!(::monostate::MustBePosInt::<#token>)) 174 | } 175 | } 176 | suffix @ ("usize" | "isize") => { 177 | let msg = format!( 178 | "serde data model only uses consistently sized integer types, not {}", 179 | suffix, 180 | ); 181 | Err(Error::new(lit.span(), msg)) 182 | } 183 | suffix => { 184 | let msg = format!("unsupported integers suffix `{}`", suffix); 185 | Err(Error::new(lit.span(), msg)) 186 | } 187 | } 188 | } 189 | 190 | fn must_be_bool(value: bool) -> Result { 191 | Ok(quote!(::monostate::MustBeBool::<#value>)) 192 | } 193 | 194 | fn unsupported(lit: Lit) -> Result { 195 | Err(Error::new(lit.span(), "unsupported monostate literal kind")) 196 | } 197 | -------------------------------------------------------------------------------- /src/alphabet.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | pub struct char; 3 | 4 | macro_rules! letters { 5 | ($($letter:ident)*) => { 6 | $( 7 | #[doc(hidden)] 8 | pub type $letter = char<{ 9 | stringify!($letter).as_bytes()[0] as core::primitive::char 10 | }>; 11 | )* 12 | }; 13 | } 14 | 15 | letters! { 16 | 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 17 | 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 18 | } 19 | 20 | #[doc(hidden)] 21 | pub mod two { 22 | #[doc(hidden)] 23 | pub struct char; 24 | } 25 | 26 | #[doc(hidden)] 27 | pub mod three { 28 | #[doc(hidden)] 29 | pub struct char; 30 | } 31 | 32 | #[doc(hidden)] 33 | pub mod four { 34 | #[doc(hidden)] 35 | pub struct char; 36 | } 37 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 2 | use core::fmt::{self, Debug}; 3 | use core::mem; 4 | use core::slice; 5 | use core::str; 6 | 7 | impl Debug for crate::MustBeChar { 8 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 9 | write!(formatter, "MustBe!({:?})", V) 10 | } 11 | } 12 | 13 | impl Debug for crate::MustBePosInt { 14 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 15 | write!(formatter, "MustBe!({})", V) 16 | } 17 | } 18 | 19 | impl Debug for crate::MustBeNegInt { 20 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 21 | write!(formatter, "MustBe!({})", V) 22 | } 23 | } 24 | 25 | impl Debug for crate::MustBeU8 { 26 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 27 | write!(formatter, "MustBe!({}u8)", V) 28 | } 29 | } 30 | 31 | impl Debug for crate::MustBeU16 { 32 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 33 | write!(formatter, "MustBe!({}u16)", V) 34 | } 35 | } 36 | 37 | impl Debug for crate::MustBeU32 { 38 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 39 | write!(formatter, "MustBe!({}u32)", V) 40 | } 41 | } 42 | 43 | impl Debug for crate::MustBeU64 { 44 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 45 | write!(formatter, "MustBe!({}u64)", V) 46 | } 47 | } 48 | 49 | impl Debug for crate::MustBeU128 { 50 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 51 | write!(formatter, "MustBe!({}u128)", V) 52 | } 53 | } 54 | 55 | impl Debug for crate::MustBeI8 { 56 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 57 | write!(formatter, "MustBe!({}i8)", V) 58 | } 59 | } 60 | 61 | impl Debug for crate::MustBeI16 { 62 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 63 | write!(formatter, "MustBe!({}i16)", V) 64 | } 65 | } 66 | 67 | impl Debug for crate::MustBeI32 { 68 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 69 | write!(formatter, "MustBe!({}i32)", V) 70 | } 71 | } 72 | 73 | impl Debug for crate::MustBeI64 { 74 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 75 | write!(formatter, "MustBe!({}i64)", V) 76 | } 77 | } 78 | 79 | impl Debug for crate::MustBeI128 { 80 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 81 | write!(formatter, "MustBe!({}i128)", V) 82 | } 83 | } 84 | 85 | impl Debug for crate::MustBeBool { 86 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 87 | write!(formatter, "MustBe!({})", V) 88 | } 89 | } 90 | 91 | impl Debug for crate::MustBeStr { 92 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 93 | write!(formatter, "MustBe!({:?})", unsafe { 94 | str::from_utf8_unchecked(slice::from_raw_parts( 95 | &V::BYTES as *const V::Type as *const u8, 96 | mem::size_of::(), 97 | )) 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/default.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 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: RetrieveString, 90 | { 91 | fn default() -> Self { 92 | crate::MustBeStr:: 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/deserialize.rs: -------------------------------------------------------------------------------- 1 | use crate::format; 2 | use crate::string::RetrieveString; 3 | use core::fmt::{self, Write as _}; 4 | use core::mem; 5 | use core::slice; 6 | use core::str; 7 | use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor}; 8 | 9 | impl<'de, const V: char> Deserialize<'de> for crate::MustBeChar { 10 | fn deserialize(deserializer: D) -> Result 11 | where 12 | D: Deserializer<'de>, 13 | { 14 | struct MustBeCharVisitor(char); 15 | 16 | impl<'de> Visitor<'de> for MustBeCharVisitor { 17 | type Value = (); 18 | 19 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 20 | write!(formatter, "char {:?}", self.0) 21 | } 22 | 23 | fn visit_char(self, v: char) -> Result 24 | where 25 | E: Error, 26 | { 27 | if v == self.0 { 28 | Ok(()) 29 | } else { 30 | Err(E::invalid_value(Unexpected::Char(v), &self)) 31 | } 32 | } 33 | } 34 | 35 | deserializer 36 | .deserialize_char(MustBeCharVisitor(V)) 37 | .map(|()| crate::MustBeChar) 38 | } 39 | } 40 | 41 | impl<'de, const V: u128> Deserialize<'de> for crate::MustBePosInt { 42 | fn deserialize(deserializer: D) -> Result 43 | where 44 | D: Deserializer<'de>, 45 | { 46 | struct MustBePosIntVisitor(u128); 47 | 48 | impl<'de> Visitor<'de> for MustBePosIntVisitor { 49 | type Value = (); 50 | 51 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 52 | write!(formatter, "integer `{}`", self.0) 53 | } 54 | 55 | fn visit_i64(self, v: i64) -> Result 56 | where 57 | E: Error, 58 | { 59 | if v >= 0 && v as u128 == self.0 { 60 | Ok(()) 61 | } else { 62 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 63 | } 64 | } 65 | 66 | fn visit_i128(self, v: i128) -> Result 67 | where 68 | E: Error, 69 | { 70 | if v >= 0 && v as u128 == self.0 { 71 | Ok(()) 72 | } else { 73 | let mut buf = [0u8; 50]; 74 | let mut writer = format::Buf::new(&mut buf); 75 | write!(writer, "integer `{}`", v).unwrap(); 76 | Err(Error::invalid_value( 77 | Unexpected::Other(writer.as_str()), 78 | &self, 79 | )) 80 | } 81 | } 82 | 83 | fn visit_u64(self, v: u64) -> Result 84 | where 85 | E: Error, 86 | { 87 | if v as u128 == self.0 { 88 | Ok(()) 89 | } else { 90 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 91 | } 92 | } 93 | 94 | fn visit_u128(self, v: u128) -> Result 95 | where 96 | E: Error, 97 | { 98 | if v == self.0 { 99 | Ok(()) 100 | } else { 101 | let mut buf = [0u8; 49]; 102 | let mut writer = format::Buf::new(&mut buf); 103 | write!(writer, "integer `{}`", v).unwrap(); 104 | Err(Error::invalid_value( 105 | Unexpected::Other(writer.as_str()), 106 | &self, 107 | )) 108 | } 109 | } 110 | } 111 | 112 | deserializer 113 | .deserialize_any(MustBePosIntVisitor(V)) 114 | .map(|()| crate::MustBePosInt) 115 | } 116 | } 117 | 118 | impl<'de, const V: i128> Deserialize<'de> for crate::MustBeNegInt { 119 | fn deserialize(deserializer: D) -> Result 120 | where 121 | D: Deserializer<'de>, 122 | { 123 | struct MustBeNegIntVisitor(i128); 124 | 125 | impl<'de> Visitor<'de> for MustBeNegIntVisitor { 126 | type Value = (); 127 | 128 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 129 | write!(formatter, "integer `{}`", self.0) 130 | } 131 | 132 | fn visit_i64(self, v: i64) -> Result 133 | where 134 | E: Error, 135 | { 136 | if v as i128 == self.0 { 137 | Ok(()) 138 | } else { 139 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 140 | } 141 | } 142 | 143 | fn visit_i128(self, v: i128) -> Result 144 | where 145 | E: Error, 146 | { 147 | if v == self.0 { 148 | Ok(()) 149 | } else { 150 | let mut buf = [0u8; 50]; 151 | let mut writer = format::Buf::new(&mut buf); 152 | write!(writer, "integer `{}`", v).unwrap(); 153 | Err(Error::invalid_value( 154 | Unexpected::Other(writer.as_str()), 155 | &self, 156 | )) 157 | } 158 | } 159 | 160 | fn visit_u64(self, v: u64) -> Result 161 | where 162 | E: Error, 163 | { 164 | if v as i128 == self.0 { 165 | Ok(()) 166 | } else { 167 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 168 | } 169 | } 170 | 171 | fn visit_u128(self, v: u128) -> Result 172 | where 173 | E: Error, 174 | { 175 | if self.0 >= 0 && v == self.0 as u128 { 176 | Ok(()) 177 | } else { 178 | let mut buf = [0u8; 49]; 179 | let mut writer = format::Buf::new(&mut buf); 180 | write!(writer, "integer `{}`", v).unwrap(); 181 | Err(Error::invalid_value( 182 | Unexpected::Other(writer.as_str()), 183 | &self, 184 | )) 185 | } 186 | } 187 | } 188 | 189 | deserializer 190 | .deserialize_any(MustBeNegIntVisitor(V)) 191 | .map(|()| crate::MustBeNegInt) 192 | } 193 | } 194 | 195 | impl<'de, const V: u8> Deserialize<'de> for crate::MustBeU8 { 196 | fn deserialize(deserializer: D) -> Result 197 | where 198 | D: Deserializer<'de>, 199 | { 200 | struct MustBeU8Visitor(u8); 201 | 202 | impl<'de> Visitor<'de> for MustBeU8Visitor { 203 | type Value = (); 204 | 205 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 206 | write!(formatter, "integer `{}` as u8", self.0) 207 | } 208 | 209 | fn visit_u8(self, v: u8) -> Result 210 | where 211 | E: Error, 212 | { 213 | if v == self.0 { 214 | Ok(()) 215 | } else { 216 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 217 | } 218 | } 219 | } 220 | 221 | deserializer 222 | .deserialize_any(MustBeU8Visitor(V)) 223 | .map(|()| crate::MustBeU8) 224 | } 225 | } 226 | 227 | impl<'de, const V: u16> Deserialize<'de> for crate::MustBeU16 { 228 | fn deserialize(deserializer: D) -> Result 229 | where 230 | D: Deserializer<'de>, 231 | { 232 | struct MustBeU16Visitor(u16); 233 | 234 | impl<'de> Visitor<'de> for MustBeU16Visitor { 235 | type Value = (); 236 | 237 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 238 | write!(formatter, "integer `{}` as u16", self.0) 239 | } 240 | 241 | fn visit_u16(self, v: u16) -> Result 242 | where 243 | E: Error, 244 | { 245 | if v == self.0 { 246 | Ok(()) 247 | } else { 248 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 249 | } 250 | } 251 | } 252 | 253 | deserializer 254 | .deserialize_any(MustBeU16Visitor(V)) 255 | .map(|()| crate::MustBeU16) 256 | } 257 | } 258 | 259 | impl<'de, const V: u32> Deserialize<'de> for crate::MustBeU32 { 260 | fn deserialize(deserializer: D) -> Result 261 | where 262 | D: Deserializer<'de>, 263 | { 264 | struct MustBeU32Visitor(u32); 265 | 266 | impl<'de> Visitor<'de> for MustBeU32Visitor { 267 | type Value = (); 268 | 269 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 270 | write!(formatter, "integer `{}` as u32", self.0) 271 | } 272 | 273 | fn visit_u32(self, v: u32) -> Result 274 | where 275 | E: Error, 276 | { 277 | if v == self.0 { 278 | Ok(()) 279 | } else { 280 | Err(E::invalid_value(Unexpected::Unsigned(v as u64), &self)) 281 | } 282 | } 283 | } 284 | 285 | deserializer 286 | .deserialize_any(MustBeU32Visitor(V)) 287 | .map(|()| crate::MustBeU32) 288 | } 289 | } 290 | 291 | impl<'de, const V: u64> Deserialize<'de> for crate::MustBeU64 { 292 | fn deserialize(deserializer: D) -> Result 293 | where 294 | D: Deserializer<'de>, 295 | { 296 | struct MustBeU64Visitor(u64); 297 | 298 | impl<'de> Visitor<'de> for MustBeU64Visitor { 299 | type Value = (); 300 | 301 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 302 | write!(formatter, "integer `{}` as u64", self.0) 303 | } 304 | 305 | fn visit_u8(self, v: u8) -> Result 306 | where 307 | E: Error, 308 | { 309 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 310 | } 311 | 312 | fn visit_u16(self, v: u16) -> Result 313 | where 314 | E: Error, 315 | { 316 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 317 | } 318 | 319 | fn visit_u32(self, v: u32) -> Result 320 | where 321 | E: Error, 322 | { 323 | Err(E::invalid_type(Unexpected::Unsigned(v as u64), &self)) 324 | } 325 | 326 | fn visit_u64(self, v: u64) -> Result 327 | where 328 | E: Error, 329 | { 330 | if v == self.0 { 331 | Ok(()) 332 | } else { 333 | Err(E::invalid_value(Unexpected::Unsigned(v), &self)) 334 | } 335 | } 336 | } 337 | 338 | deserializer 339 | .deserialize_any(MustBeU64Visitor(V)) 340 | .map(|()| crate::MustBeU64) 341 | } 342 | } 343 | 344 | impl<'de, const V: u128> Deserialize<'de> for crate::MustBeU128 { 345 | fn deserialize(deserializer: D) -> Result 346 | where 347 | D: Deserializer<'de>, 348 | { 349 | struct MustBeU128Visitor(u128); 350 | 351 | impl<'de> Visitor<'de> for MustBeU128Visitor { 352 | type Value = (); 353 | 354 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 355 | write!(formatter, "integer `{}` as u128", self.0) 356 | } 357 | 358 | fn visit_u128(self, v: u128) -> Result 359 | where 360 | E: Error, 361 | { 362 | if v == self.0 { 363 | Ok(()) 364 | } else { 365 | let mut buf = [0u8; 49]; 366 | let mut writer = format::Buf::new(&mut buf); 367 | write!(writer, "integer `{}`", v).unwrap(); 368 | Err(Error::invalid_value( 369 | Unexpected::Other(writer.as_str()), 370 | &self, 371 | )) 372 | } 373 | } 374 | } 375 | 376 | deserializer 377 | .deserialize_any(MustBeU128Visitor(V)) 378 | .map(|()| crate::MustBeU128) 379 | } 380 | } 381 | 382 | impl<'de, const V: i8> Deserialize<'de> for crate::MustBeI8 { 383 | fn deserialize(deserializer: D) -> Result 384 | where 385 | D: Deserializer<'de>, 386 | { 387 | struct MustBeI8Visitor(i8); 388 | 389 | impl<'de> Visitor<'de> for MustBeI8Visitor { 390 | type Value = (); 391 | 392 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 393 | write!(formatter, "integer `{}` as i8", self.0) 394 | } 395 | 396 | fn visit_i8(self, v: i8) -> Result 397 | where 398 | E: Error, 399 | { 400 | if v == self.0 { 401 | Ok(()) 402 | } else { 403 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 404 | } 405 | } 406 | } 407 | 408 | deserializer 409 | .deserialize_any(MustBeI8Visitor(V)) 410 | .map(|()| crate::MustBeI8) 411 | } 412 | } 413 | 414 | impl<'de, const V: i16> Deserialize<'de> for crate::MustBeI16 { 415 | fn deserialize(deserializer: D) -> Result 416 | where 417 | D: Deserializer<'de>, 418 | { 419 | struct MustBeI16Visitor(i16); 420 | 421 | impl<'de> Visitor<'de> for MustBeI16Visitor { 422 | type Value = (); 423 | 424 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 425 | write!(formatter, "integer `{}` as i16", self.0) 426 | } 427 | 428 | fn visit_i16(self, v: i16) -> Result 429 | where 430 | E: Error, 431 | { 432 | if v == self.0 { 433 | Ok(()) 434 | } else { 435 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 436 | } 437 | } 438 | } 439 | 440 | deserializer 441 | .deserialize_any(MustBeI16Visitor(V)) 442 | .map(|()| crate::MustBeI16) 443 | } 444 | } 445 | 446 | impl<'de, const V: i32> Deserialize<'de> for crate::MustBeI32 { 447 | fn deserialize(deserializer: D) -> Result 448 | where 449 | D: Deserializer<'de>, 450 | { 451 | struct MustBeI32Visitor(i32); 452 | 453 | impl<'de> Visitor<'de> for MustBeI32Visitor { 454 | type Value = (); 455 | 456 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 457 | write!(formatter, "integer `{}` as i32", self.0) 458 | } 459 | 460 | fn visit_i32(self, v: i32) -> Result 461 | where 462 | E: Error, 463 | { 464 | if v == self.0 { 465 | Ok(()) 466 | } else { 467 | Err(E::invalid_value(Unexpected::Signed(v as i64), &self)) 468 | } 469 | } 470 | } 471 | 472 | deserializer 473 | .deserialize_any(MustBeI32Visitor(V)) 474 | .map(|()| crate::MustBeI32) 475 | } 476 | } 477 | 478 | impl<'de, const V: i64> Deserialize<'de> for crate::MustBeI64 { 479 | fn deserialize(deserializer: D) -> Result 480 | where 481 | D: Deserializer<'de>, 482 | { 483 | struct MustBeI64Visitor(i64); 484 | 485 | impl<'de> Visitor<'de> for MustBeI64Visitor { 486 | type Value = (); 487 | 488 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 489 | write!(formatter, "integer `{}` as i64", self.0) 490 | } 491 | 492 | fn visit_i8(self, v: i8) -> Result 493 | where 494 | E: Error, 495 | { 496 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 497 | } 498 | 499 | fn visit_i16(self, v: i16) -> Result 500 | where 501 | E: Error, 502 | { 503 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 504 | } 505 | 506 | fn visit_i32(self, v: i32) -> Result 507 | where 508 | E: Error, 509 | { 510 | Err(E::invalid_type(Unexpected::Signed(v as i64), &self)) 511 | } 512 | 513 | fn visit_i64(self, v: i64) -> Result 514 | where 515 | E: Error, 516 | { 517 | if v == self.0 { 518 | Ok(()) 519 | } else { 520 | Err(E::invalid_value(Unexpected::Signed(v), &self)) 521 | } 522 | } 523 | } 524 | 525 | deserializer 526 | .deserialize_any(MustBeI64Visitor(V)) 527 | .map(|()| crate::MustBeI64) 528 | } 529 | } 530 | 531 | impl<'de, const V: i128> Deserialize<'de> for crate::MustBeI128 { 532 | fn deserialize(deserializer: D) -> Result 533 | where 534 | D: Deserializer<'de>, 535 | { 536 | struct MustBeI128Visitor(i128); 537 | 538 | impl<'de> Visitor<'de> for MustBeI128Visitor { 539 | type Value = (); 540 | 541 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 542 | write!(formatter, "integer `{}` as i128", self.0) 543 | } 544 | 545 | fn visit_i128(self, v: i128) -> Result 546 | where 547 | E: Error, 548 | { 549 | if v == self.0 { 550 | Ok(()) 551 | } else { 552 | let mut buf = [0u8; 50]; 553 | let mut writer = format::Buf::new(&mut buf); 554 | write!(writer, "integer `{}`", v).unwrap(); 555 | Err(Error::invalid_value( 556 | Unexpected::Other(writer.as_str()), 557 | &self, 558 | )) 559 | } 560 | } 561 | } 562 | 563 | deserializer 564 | .deserialize_any(MustBeI128Visitor(V)) 565 | .map(|()| crate::MustBeI128) 566 | } 567 | } 568 | 569 | impl<'de, const V: bool> Deserialize<'de> for crate::MustBeBool { 570 | fn deserialize(deserializer: D) -> Result 571 | where 572 | D: Deserializer<'de>, 573 | { 574 | struct MustBeBoolVisitor(bool); 575 | 576 | impl<'de> Visitor<'de> for MustBeBoolVisitor { 577 | type Value = (); 578 | 579 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 580 | write!(formatter, "boolean `{}`", self.0) 581 | } 582 | 583 | fn visit_bool(self, v: bool) -> Result 584 | where 585 | E: Error, 586 | { 587 | if v == self.0 { 588 | Ok(()) 589 | } else { 590 | Err(E::invalid_value(Unexpected::Bool(v), &self)) 591 | } 592 | } 593 | } 594 | 595 | deserializer 596 | .deserialize_any(MustBeBoolVisitor(V)) 597 | .map(|()| crate::MustBeBool) 598 | } 599 | } 600 | 601 | impl<'de, V: RetrieveString> Deserialize<'de> for crate::MustBeStr { 602 | fn deserialize(deserializer: D) -> Result 603 | where 604 | D: Deserializer<'de>, 605 | { 606 | struct MustBeStrVisitor(&'static str); 607 | 608 | impl<'de> Visitor<'de> for MustBeStrVisitor { 609 | type Value = (); 610 | 611 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 612 | write!(formatter, "string {:?}", self.0) 613 | } 614 | 615 | fn visit_str(self, v: &str) -> Result 616 | where 617 | E: Error, 618 | { 619 | if v == self.0 { 620 | Ok(()) 621 | } else { 622 | Err(E::invalid_value(Unexpected::Str(v), &self)) 623 | } 624 | } 625 | } 626 | 627 | deserializer 628 | .deserialize_any(MustBeStrVisitor(unsafe { 629 | str::from_utf8_unchecked(slice::from_raw_parts( 630 | &V::BYTES as *const V::Type as *const u8, 631 | mem::size_of::(), 632 | )) 633 | })) 634 | .map(|()| crate::MustBeStr) 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /src/eq.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 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/hash.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 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: RetrieveString, 63 | { 64 | fn hash(&self, _: &mut H) {} 65 | } 66 | -------------------------------------------------------------------------------- /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/0.1.14")] 53 | #![allow(non_camel_case_types, non_upper_case_globals)] 54 | #![allow( 55 | clippy::borrow_as_ptr, 56 | clippy::builtin_type_shadow, 57 | clippy::cast_lossless, 58 | clippy::cast_sign_loss, 59 | clippy::elidable_lifetime_names, 60 | clippy::expl_impl_clone_on_copy, 61 | clippy::missing_safety_doc, 62 | clippy::module_name_repetitions, 63 | clippy::needless_lifetimes, 64 | clippy::ptr_as_ptr, 65 | clippy::uninhabited_references, 66 | clippy::uninlined_format_args 67 | )] 68 | 69 | #[doc(hidden)] 70 | pub mod alphabet; 71 | mod debug; 72 | mod default; 73 | mod deserialize; 74 | mod eq; 75 | mod format; 76 | mod hash; 77 | mod ord; 78 | mod partial_eq; 79 | mod partial_ord; 80 | mod serialize; 81 | mod string; 82 | 83 | pub use monostate_impl::MustBe; 84 | 85 | #[derive(Copy, Clone)] 86 | #[doc(hidden)] 87 | pub struct MustBeChar; 88 | 89 | #[derive(Copy, Clone)] 90 | #[doc(hidden)] 91 | pub struct MustBePosInt; 92 | 93 | #[derive(Copy, Clone)] 94 | #[doc(hidden)] 95 | pub struct MustBeNegInt; 96 | 97 | #[derive(Copy, Clone)] 98 | #[doc(hidden)] 99 | pub struct MustBeU8; 100 | 101 | #[derive(Copy, Clone)] 102 | #[doc(hidden)] 103 | pub struct MustBeU16; 104 | 105 | #[derive(Copy, Clone)] 106 | #[doc(hidden)] 107 | pub struct MustBeU32; 108 | 109 | #[derive(Copy, Clone)] 110 | #[doc(hidden)] 111 | pub struct MustBeU64; 112 | 113 | #[derive(Copy, Clone)] 114 | #[doc(hidden)] 115 | pub struct MustBeU128; 116 | 117 | #[derive(Copy, Clone)] 118 | #[doc(hidden)] 119 | pub struct MustBeI8; 120 | 121 | #[derive(Copy, Clone)] 122 | #[doc(hidden)] 123 | pub struct MustBeI16; 124 | 125 | #[derive(Copy, Clone)] 126 | #[doc(hidden)] 127 | pub struct MustBeI32; 128 | 129 | #[derive(Copy, Clone)] 130 | #[doc(hidden)] 131 | pub struct MustBeI64; 132 | 133 | #[derive(Copy, Clone)] 134 | #[doc(hidden)] 135 | pub struct MustBeI128; 136 | 137 | #[derive(Copy, Clone)] 138 | #[doc(hidden)] 139 | pub struct MustBeBool; 140 | 141 | mod void { 142 | use core::marker::PhantomData; 143 | 144 | enum Void {} 145 | 146 | impl Copy for Void {} 147 | 148 | impl Clone for Void { 149 | fn clone(&self) -> Self { 150 | *self 151 | } 152 | } 153 | 154 | pub struct MustBeStr(PhantomData, Void); 155 | 156 | impl Copy for MustBeStr {} 157 | 158 | impl Clone for MustBeStr { 159 | fn clone(&self) -> Self { 160 | *self 161 | } 162 | } 163 | } 164 | 165 | mod value { 166 | #[doc(hidden)] 167 | pub use super::MustBeStr::MustBeStr; 168 | } 169 | 170 | // Equivalent to `pub struct MustBeStr;` but using 171 | // the type encoding described in impl/src/lib.rs to avoid depending on 172 | // #![feature(adt_const_params)] for now. 173 | #[doc(hidden)] 174 | pub enum MustBeStr { 175 | __Phantom(void::MustBeStr), 176 | MustBeStr, 177 | } 178 | 179 | impl Copy for MustBeStr {} 180 | 181 | impl Clone for MustBeStr { 182 | fn clone(&self) -> Self { 183 | *self 184 | } 185 | } 186 | 187 | #[doc(hidden)] 188 | pub use self::value::*; 189 | -------------------------------------------------------------------------------- /src/ord.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 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: RetrieveString, 91 | { 92 | fn cmp(&self, _: &Self) -> Ordering { 93 | Ordering::Equal 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/partial_eq.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 2 | use core::any::TypeId; 3 | 4 | impl PartialEq> for crate::MustBeChar { 5 | fn eq(&self, _: &crate::MustBeChar) -> bool { 6 | V == W 7 | } 8 | } 9 | 10 | impl PartialEq> for crate::MustBePosInt { 11 | fn eq(&self, _: &crate::MustBePosInt) -> bool { 12 | V == W 13 | } 14 | } 15 | 16 | impl PartialEq> for crate::MustBeNegInt { 17 | fn eq(&self, _: &crate::MustBeNegInt) -> bool { 18 | V == W 19 | } 20 | } 21 | 22 | impl PartialEq> for crate::MustBeU8 { 23 | fn eq(&self, _: &crate::MustBeU8) -> bool { 24 | V == W 25 | } 26 | } 27 | 28 | impl PartialEq> for crate::MustBeU16 { 29 | fn eq(&self, _: &crate::MustBeU16) -> bool { 30 | V == W 31 | } 32 | } 33 | 34 | impl PartialEq> for crate::MustBeU32 { 35 | fn eq(&self, _: &crate::MustBeU32) -> bool { 36 | V == W 37 | } 38 | } 39 | 40 | impl PartialEq> for crate::MustBeU64 { 41 | fn eq(&self, _: &crate::MustBeU64) -> bool { 42 | V == W 43 | } 44 | } 45 | 46 | impl PartialEq> for crate::MustBeU128 { 47 | fn eq(&self, _: &crate::MustBeU128) -> bool { 48 | V == W 49 | } 50 | } 51 | 52 | impl PartialEq> for crate::MustBeI8 { 53 | fn eq(&self, _: &crate::MustBeI8) -> bool { 54 | V == W 55 | } 56 | } 57 | 58 | impl PartialEq> for crate::MustBeI16 { 59 | fn eq(&self, _: &crate::MustBeI16) -> bool { 60 | V == W 61 | } 62 | } 63 | 64 | impl PartialEq> for crate::MustBeI32 { 65 | fn eq(&self, _: &crate::MustBeI32) -> bool { 66 | V == W 67 | } 68 | } 69 | 70 | impl PartialEq> for crate::MustBeI64 { 71 | fn eq(&self, _: &crate::MustBeI64) -> bool { 72 | V == W 73 | } 74 | } 75 | 76 | impl PartialEq> for crate::MustBeI128 { 77 | fn eq(&self, _: &crate::MustBeI128) -> bool { 78 | V == W 79 | } 80 | } 81 | 82 | impl PartialEq> for crate::MustBeBool { 83 | fn eq(&self, _: &crate::MustBeBool) -> bool { 84 | V == W 85 | } 86 | } 87 | 88 | impl PartialEq> for crate::MustBeStr 89 | where 90 | V: RetrieveString, 91 | W: RetrieveString, 92 | { 93 | fn eq(&self, _: &crate::MustBeStr) -> bool { 94 | TypeId::of::() == TypeId::of::() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/partial_ord.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 2 | use core::cmp::Ordering; 3 | use core::mem; 4 | use core::slice; 5 | use core::str; 6 | 7 | impl PartialOrd> for crate::MustBeChar { 8 | fn partial_cmp(&self, _: &crate::MustBeChar) -> Option { 9 | Some(V.cmp(&W)) 10 | } 11 | } 12 | 13 | impl PartialOrd> for crate::MustBePosInt { 14 | fn partial_cmp(&self, _: &crate::MustBePosInt) -> Option { 15 | Some(V.cmp(&W)) 16 | } 17 | } 18 | 19 | impl PartialOrd> for crate::MustBeNegInt { 20 | fn partial_cmp(&self, _: &crate::MustBeNegInt) -> Option { 21 | Some(V.cmp(&W)) 22 | } 23 | } 24 | 25 | impl PartialOrd> for crate::MustBeU8 { 26 | fn partial_cmp(&self, _: &crate::MustBeU8) -> Option { 27 | Some(V.cmp(&W)) 28 | } 29 | } 30 | 31 | impl PartialOrd> for crate::MustBeU16 { 32 | fn partial_cmp(&self, _: &crate::MustBeU16) -> Option { 33 | Some(V.cmp(&W)) 34 | } 35 | } 36 | 37 | impl PartialOrd> for crate::MustBeU32 { 38 | fn partial_cmp(&self, _: &crate::MustBeU32) -> Option { 39 | Some(V.cmp(&W)) 40 | } 41 | } 42 | 43 | impl PartialOrd> for crate::MustBeU64 { 44 | fn partial_cmp(&self, _: &crate::MustBeU64) -> Option { 45 | Some(V.cmp(&W)) 46 | } 47 | } 48 | 49 | impl PartialOrd> for crate::MustBeU128 { 50 | fn partial_cmp(&self, _: &crate::MustBeU128) -> Option { 51 | Some(V.cmp(&W)) 52 | } 53 | } 54 | 55 | impl PartialOrd> for crate::MustBeI8 { 56 | fn partial_cmp(&self, _: &crate::MustBeI8) -> Option { 57 | Some(V.cmp(&W)) 58 | } 59 | } 60 | 61 | impl PartialOrd> for crate::MustBeI16 { 62 | fn partial_cmp(&self, _: &crate::MustBeI16) -> Option { 63 | Some(V.cmp(&W)) 64 | } 65 | } 66 | 67 | impl PartialOrd> for crate::MustBeI32 { 68 | fn partial_cmp(&self, _: &crate::MustBeI32) -> Option { 69 | Some(V.cmp(&W)) 70 | } 71 | } 72 | 73 | impl PartialOrd> for crate::MustBeI64 { 74 | fn partial_cmp(&self, _: &crate::MustBeI64) -> Option { 75 | Some(V.cmp(&W)) 76 | } 77 | } 78 | 79 | impl PartialOrd> for crate::MustBeI128 { 80 | fn partial_cmp(&self, _: &crate::MustBeI128) -> Option { 81 | Some(V.cmp(&W)) 82 | } 83 | } 84 | 85 | impl PartialOrd> for crate::MustBeBool { 86 | fn partial_cmp(&self, _: &crate::MustBeBool) -> Option { 87 | Some(V.cmp(&W)) 88 | } 89 | } 90 | 91 | impl PartialOrd> for crate::MustBeStr 92 | where 93 | V: RetrieveString, 94 | W: RetrieveString, 95 | { 96 | fn partial_cmp(&self, _: &crate::MustBeStr) -> Option { 97 | Some(unsafe { 98 | str::from_utf8_unchecked(slice::from_raw_parts( 99 | &V::BYTES as *const V::Type as *const u8, 100 | mem::size_of::(), 101 | )) 102 | .cmp(str::from_utf8_unchecked(slice::from_raw_parts( 103 | &W::BYTES as *const W::Type as *const u8, 104 | mem::size_of::(), 105 | ))) 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/serialize.rs: -------------------------------------------------------------------------------- 1 | use crate::string::RetrieveString; 2 | use core::mem; 3 | use core::slice; 4 | use core::str; 5 | use serde::{Serialize, Serializer}; 6 | 7 | impl Serialize for crate::MustBeChar { 8 | fn serialize(&self, serializer: S) -> Result 9 | where 10 | S: Serializer, 11 | { 12 | serializer.serialize_char(V) 13 | } 14 | } 15 | 16 | impl Serialize for crate::MustBeU8 { 17 | fn serialize(&self, serializer: S) -> Result 18 | where 19 | S: Serializer, 20 | { 21 | serializer.serialize_u8(V) 22 | } 23 | } 24 | 25 | impl Serialize for crate::MustBeU16 { 26 | fn serialize(&self, serializer: S) -> Result 27 | where 28 | S: Serializer, 29 | { 30 | serializer.serialize_u16(V) 31 | } 32 | } 33 | 34 | impl Serialize for crate::MustBeU32 { 35 | fn serialize(&self, serializer: S) -> Result 36 | where 37 | S: Serializer, 38 | { 39 | serializer.serialize_u32(V) 40 | } 41 | } 42 | 43 | impl Serialize for crate::MustBeU64 { 44 | fn serialize(&self, serializer: S) -> Result 45 | where 46 | S: Serializer, 47 | { 48 | serializer.serialize_u64(V) 49 | } 50 | } 51 | 52 | impl Serialize for crate::MustBeU128 { 53 | fn serialize(&self, serializer: S) -> Result 54 | where 55 | S: Serializer, 56 | { 57 | serializer.serialize_u128(V) 58 | } 59 | } 60 | 61 | impl Serialize for crate::MustBeI8 { 62 | fn serialize(&self, serializer: S) -> Result 63 | where 64 | S: Serializer, 65 | { 66 | serializer.serialize_i8(V) 67 | } 68 | } 69 | 70 | impl Serialize for crate::MustBeI16 { 71 | fn serialize(&self, serializer: S) -> Result 72 | where 73 | S: Serializer, 74 | { 75 | serializer.serialize_i16(V) 76 | } 77 | } 78 | 79 | impl Serialize for crate::MustBeI32 { 80 | fn serialize(&self, serializer: S) -> Result 81 | where 82 | S: Serializer, 83 | { 84 | serializer.serialize_i32(V) 85 | } 86 | } 87 | 88 | impl Serialize for crate::MustBeI64 { 89 | fn serialize(&self, serializer: S) -> Result 90 | where 91 | S: Serializer, 92 | { 93 | serializer.serialize_i64(V) 94 | } 95 | } 96 | 97 | impl Serialize for crate::MustBeI128 { 98 | fn serialize(&self, serializer: S) -> Result 99 | where 100 | S: Serializer, 101 | { 102 | serializer.serialize_i128(V) 103 | } 104 | } 105 | 106 | impl Serialize for crate::MustBeBool { 107 | fn serialize(&self, serializer: S) -> Result 108 | where 109 | S: Serializer, 110 | { 111 | serializer.serialize_bool(V) 112 | } 113 | } 114 | 115 | impl Serialize for crate::MustBeStr { 116 | fn serialize(&self, serializer: S) -> Result 117 | where 118 | S: Serializer, 119 | { 120 | serializer.serialize_str(unsafe { 121 | str::from_utf8_unchecked(slice::from_raw_parts( 122 | &V::BYTES as *const V::Type as *const u8, 123 | mem::size_of::(), 124 | )) 125 | }) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | use crate::alphabet; 2 | 3 | const TAG_CONT: u8 = 0b1000_0000; 4 | const TAG_TWO_B: u8 = 0b1100_0000; 5 | const TAG_THREE_B: u8 = 0b1110_0000; 6 | const TAG_FOUR_B: u8 = 0b1111_0000; 7 | 8 | pub unsafe trait RetrieveString { 9 | // SAFETY: Must contain no padding bytes. Must have alignment of 1. 10 | type Type: 'static; 11 | // SAFETY: Contents viewed as bytes must be a valid UTF-8 encoding. 12 | const BYTES: Self::Type; 13 | } 14 | 15 | unsafe impl RetrieveString for alphabet::char { 16 | type Type = u8; 17 | const BYTES: Self::Type = CH as u8; 18 | } 19 | 20 | unsafe impl RetrieveString for alphabet::two::char { 21 | type Type = [u8; 2]; 22 | const BYTES: Self::Type = [ 23 | ((CH as u32 >> 6) & 0x1F) as u8 | TAG_TWO_B, 24 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 25 | ]; 26 | } 27 | 28 | unsafe impl RetrieveString for alphabet::three::char { 29 | type Type = [u8; 3]; 30 | const BYTES: Self::Type = [ 31 | ((CH as u32 >> 12) & 0x0F) as u8 | TAG_THREE_B, 32 | ((CH as u32 >> 6) & 0x3F) as u8 | TAG_CONT, 33 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 34 | ]; 35 | } 36 | 37 | unsafe impl RetrieveString for alphabet::four::char { 38 | type Type = [u8; 4]; 39 | const BYTES: Self::Type = [ 40 | ((CH as u32 >> 18) & 0x07) as u8 | TAG_FOUR_B, 41 | ((CH as u32 >> 12) & 0x3F) as u8 | TAG_CONT, 42 | ((CH as u32 >> 6) & 0x3F) as u8 | TAG_CONT, 43 | (CH as u32 & 0x3F) as u8 | TAG_CONT, 44 | ]; 45 | } 46 | 47 | unsafe impl RetrieveString for () { 48 | type Type = (); 49 | const BYTES: Self::Type = (); 50 | } 51 | 52 | #[repr(C)] 53 | pub struct Concat2(A, B); 54 | 55 | unsafe impl RetrieveString for (A, B) 56 | where 57 | A: RetrieveString, 58 | B: RetrieveString, 59 | { 60 | type Type = Concat2; 61 | const BYTES: Self::Type = Concat2(A::BYTES, B::BYTES); 62 | } 63 | 64 | #[repr(C)] 65 | pub struct Concat3(A, B, C); 66 | 67 | unsafe impl RetrieveString for (A, B, C) 68 | where 69 | A: RetrieveString, 70 | B: RetrieveString, 71 | C: RetrieveString, 72 | { 73 | type Type = Concat3; 74 | const BYTES: Self::Type = Concat3(A::BYTES, B::BYTES, C::BYTES); 75 | } 76 | 77 | #[repr(C)] 78 | pub struct Concat4(A, B, C, D); 79 | 80 | unsafe impl RetrieveString for (A, B, C, D) 81 | where 82 | A: RetrieveString, 83 | B: RetrieveString, 84 | C: RetrieveString, 85 | D: RetrieveString, 86 | { 87 | type Type = Concat4; 88 | const BYTES: Self::Type = Concat4(A::BYTES, B::BYTES, C::BYTES, D::BYTES); 89 | } 90 | 91 | #[repr(C)] 92 | pub struct Concat5(A, B, C, D, E); 93 | 94 | unsafe impl RetrieveString for (A, B, C, D, E) 95 | where 96 | A: RetrieveString, 97 | B: RetrieveString, 98 | C: RetrieveString, 99 | D: RetrieveString, 100 | E: RetrieveString, 101 | { 102 | type Type = Concat5; 103 | const BYTES: Self::Type = Concat5(A::BYTES, B::BYTES, C::BYTES, D::BYTES, E::BYTES); 104 | } 105 | 106 | #[repr(C)] 107 | pub struct Concat6(A, B, C, D, E, F); 108 | 109 | unsafe impl RetrieveString for (A, B, C, D, E, F) 110 | where 111 | A: RetrieveString, 112 | B: RetrieveString, 113 | C: RetrieveString, 114 | D: RetrieveString, 115 | E: RetrieveString, 116 | F: RetrieveString, 117 | { 118 | type Type = Concat6; 119 | const BYTES: Self::Type = Concat6(A::BYTES, B::BYTES, C::BYTES, D::BYTES, E::BYTES, F::BYTES); 120 | } 121 | -------------------------------------------------------------------------------- /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_cmp() { 96 | assert_eq!(MustBe!(4), MustBe!(4)); 97 | assert_ne!(MustBe!(4), MustBe!(5)); 98 | assert!(MustBe!(4) < MustBe!(5)); 99 | } 100 | 101 | #[test] 102 | fn test_long_string() { 103 | let _ = MustBe!("\ 104 | 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. \ 105 | 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. \ 106 | 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.\ 107 | "); 108 | } 109 | 110 | #[test] 111 | fn test_utf8() { 112 | let string = "$£€𐍈"; 113 | let mut chars = string.chars(); 114 | assert_eq!(chars.next().unwrap().len_utf8(), 1); 115 | assert_eq!(chars.next().unwrap().len_utf8(), 2); 116 | assert_eq!(chars.next().unwrap().len_utf8(), 3); 117 | assert_eq!(chars.next().unwrap().len_utf8(), 4); 118 | assert!(chars.next().is_none()); 119 | 120 | let must_be = MustBe!("$£€𐍈"); 121 | assert_eq!( 122 | serde_json::to_string(string).unwrap(), 123 | serde_json::to_string(&must_be).unwrap(), 124 | ); 125 | } 126 | 127 | #[test] 128 | fn test_layout() { 129 | let must_be = MustBe!("s"); 130 | assert_eq!(0, mem::size_of_val(&must_be)); 131 | assert_eq!(1, mem::align_of_val(&must_be)); 132 | 133 | let must_be = MustBe!(1); 134 | assert_eq!(0, mem::size_of_val(&must_be)); 135 | assert_eq!(1, mem::align_of_val(&must_be)); 136 | } 137 | 138 | #[test] 139 | fn test_autotraits() { 140 | fn assert_send(_: impl Send) {} 141 | fn assert_sync(_: impl Sync) {} 142 | 143 | assert_send(MustBe!("")); 144 | assert_sync(MustBe!("")); 145 | assert_send(MustBe!(true)); 146 | assert_sync(MustBe!(true)); 147 | } 148 | --------------------------------------------------------------------------------