├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── macro ├── Cargo.toml └── src │ ├── generate.rs │ └── lib.rs ├── src ├── examples.rs ├── lib.rs ├── parse.rs └── types │ ├── indexing.rs │ └── mod.rs └── tests └── hygiene.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | { 2 | "on": ["push", "pull_request"], 3 | "name": "CI", 4 | "jobs": { 5 | "clippy": { 6 | "name": "Clippy", 7 | "runs-on": "ubuntu-latest", 8 | "steps": [ 9 | { "uses": "actions/checkout@v2" }, 10 | { 11 | "uses": "actions-rs/toolchain@v1", 12 | "with": { 13 | "toolchain": "stable", 14 | "profile": "minimal", 15 | "components": "clippy", 16 | "override": true, 17 | }, 18 | }, 19 | { 20 | "uses": "actions-rs/cargo@v1", 21 | "with": { 22 | "command": "clippy", 23 | "args": "--workspace --all-targets --features types,__examples -- -Dwarnings", 24 | }, 25 | }, 26 | { 27 | "uses": "actions-rs/cargo@v1", 28 | "with": { 29 | "command": "clippy", 30 | "args": "--workspace --all-targets --features types,__examples,arbitrary1,bytemuck1,serde1,zerocopy,std -- -Dwarnings", 31 | }, 32 | }, 33 | ], 34 | }, 35 | "test": { 36 | "name": "Test", 37 | "runs-on": "ubuntu-latest", 38 | "steps": [ 39 | { "uses": "actions/checkout@v2" }, 40 | { 41 | "uses": "actions-rs/toolchain@v1", 42 | "with": { 43 | "profile": "minimal", 44 | "toolchain": "stable", 45 | "override": true, 46 | }, 47 | }, 48 | { 49 | "uses": "actions-rs/cargo@v1", 50 | "with": { 51 | "command": "test", 52 | "args": "--workspace --features types,__examples,arbitrary1,bytemuck1,serde1,zerocopy,std", 53 | }, 54 | }, 55 | ], 56 | }, 57 | "check_nightly": { 58 | "name": "Check Nightly", 59 | "runs-on": "ubuntu-latest", 60 | "env": { "RUSTFLAGS": "-Dwarnings" }, 61 | "steps": [ 62 | { "uses": "actions/checkout@v2" }, 63 | { 64 | "uses": "actions-rs/toolchain@v1", 65 | "with": { 66 | "toolchain": "nightly", 67 | "profile": "minimal", 68 | "override": true, 69 | }, 70 | }, 71 | { 72 | "uses": "actions-rs/cargo@v1", 73 | "with": { 74 | "command": "check", 75 | "args": "--workspace --all-targets --features types,__examples", 76 | }, 77 | }, 78 | ], 79 | }, 80 | "test_nightly": { 81 | "name": "Test Nightly", 82 | "runs-on": "ubuntu-latest", 83 | "steps": [ 84 | { "uses": "actions/checkout@v2" }, 85 | { 86 | "uses": "actions-rs/toolchain@v1", 87 | "with": { 88 | "profile": "minimal", 89 | "toolchain": "nightly", 90 | "override": true, 91 | }, 92 | }, 93 | { 94 | "uses": "actions-rs/cargo@v1", 95 | "with": { 96 | "command": "test", 97 | "args": "--workspace --features types,__examples,arbitrary1,bytemuck1,serde1,zerocopy,std,step_trait", 98 | }, 99 | }, 100 | ], 101 | }, 102 | "fmt": { 103 | "name": "Rustfmt", 104 | "runs-on": "ubuntu-latest", 105 | "steps": [ 106 | { "uses": "actions/checkout@v2" }, 107 | { 108 | "uses": "actions-rs/toolchain@v1", 109 | "with": { 110 | "toolchain": "stable", 111 | "profile": "minimal", 112 | "components": "rustfmt", 113 | "override": true, 114 | }, 115 | }, 116 | { 117 | "uses": "actions-rs/cargo@v1", 118 | "with": { 119 | "command": "fmt", 120 | "args": "--all -- --check", 121 | }, 122 | }, 123 | ], 124 | }, 125 | }, 126 | } 127 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "arbitrary" 7 | version = "1.4.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.4.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 16 | 17 | [[package]] 18 | name = "bounded-integer" 19 | version = "0.5.8" 20 | dependencies = [ 21 | "arbitrary", 22 | "bounded-integer-macro", 23 | "bytemuck", 24 | "num-traits", 25 | "serde", 26 | "zerocopy", 27 | ] 28 | 29 | [[package]] 30 | name = "bounded-integer-macro" 31 | version = "0.5.8" 32 | dependencies = [ 33 | "num-bigint", 34 | "proc-macro2", 35 | "quote", 36 | "syn", 37 | ] 38 | 39 | [[package]] 40 | name = "bytemuck" 41 | version = "1.21.0" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" 44 | 45 | [[package]] 46 | name = "num-bigint" 47 | version = "0.4.6" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 50 | dependencies = [ 51 | "num-integer", 52 | "num-traits", 53 | ] 54 | 55 | [[package]] 56 | name = "num-integer" 57 | version = "0.1.46" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 60 | dependencies = [ 61 | "num-traits", 62 | ] 63 | 64 | [[package]] 65 | name = "num-traits" 66 | version = "0.2.19" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 69 | dependencies = [ 70 | "autocfg", 71 | ] 72 | 73 | [[package]] 74 | name = "proc-macro2" 75 | version = "1.0.93" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 78 | dependencies = [ 79 | "unicode-ident", 80 | ] 81 | 82 | [[package]] 83 | name = "quote" 84 | version = "1.0.38" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 87 | dependencies = [ 88 | "proc-macro2", 89 | ] 90 | 91 | [[package]] 92 | name = "serde" 93 | version = "1.0.217" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 96 | dependencies = [ 97 | "serde_derive", 98 | ] 99 | 100 | [[package]] 101 | name = "serde_derive" 102 | version = "1.0.217" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 105 | dependencies = [ 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "syn" 113 | version = "2.0.96" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 116 | dependencies = [ 117 | "proc-macro2", 118 | "quote", 119 | "unicode-ident", 120 | ] 121 | 122 | [[package]] 123 | name = "unicode-ident" 124 | version = "1.0.14" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 127 | 128 | [[package]] 129 | name = "zerocopy" 130 | version = "0.8.14" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" 133 | dependencies = [ 134 | "zerocopy-derive", 135 | ] 136 | 137 | [[package]] 138 | name = "zerocopy-derive" 139 | version = "0.8.14" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" 142 | dependencies = [ 143 | "proc-macro2", 144 | "quote", 145 | "syn", 146 | ] 147 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bounded-integer" 3 | version = "0.5.8" 4 | description = "Bounded integers" 5 | keywords = ["bounded", "integer", "macro", "refinement"] 6 | license = "ISC" 7 | repository = "https://github.com/Kestrer/bounded-integer" 8 | readme = "README.md" 9 | edition = "2021" 10 | 11 | [dependencies] 12 | bounded-integer-macro = { path = "./macro", version = "=0.5.8", optional = true } 13 | 14 | arbitrary1 = { package = "arbitrary", version = "1.0.2", optional = true } 15 | bytemuck1 = { package = "bytemuck", version = "1.7.2", optional = true } 16 | num-traits02 = { package = "num-traits", version = "0.2.14", default-features = false, features = ["i128"], optional = true } 17 | serde1 = { package = "serde", version = "1.0.124", default-features = false, optional = true } 18 | zerocopy = { version = "0.8.14", features = ["derive"], optional = true } 19 | 20 | [features] 21 | std = ["alloc"] 22 | alloc = [] 23 | 24 | types = [] 25 | 26 | macro = ["bounded-integer-macro"] 27 | __examples = ["macro", "bounded-integer-macro/generate_tests"] 28 | 29 | step_trait = [] 30 | 31 | serde = ["serde1"] 32 | 33 | [package.metadata.docs.rs] 34 | all-features = true 35 | rustdoc-args = ["--cfg", "doc_cfg"] 36 | 37 | [lints.rust] 38 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(doc_cfg)'] } 39 | 40 | [workspace] 41 | members = ["macro"] 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bounded Integer 2 | 3 | [![Crate version][crate-badge]][crate] 4 | [![docs.rs][docsrs-badge]][docsrs] 5 | [![checks][checks-badge]][checks] 6 | 7 | [crate]: https://crates.io/crates/bounded-integer 8 | [crate-badge]: https://img.shields.io/crates/v/bounded-integer.svg 9 | [docsrs]: https://docs.rs/bounded-integer 10 | [docsrs-badge]: https://img.shields.io/badge/docs.rs-bounded--integer-informational 11 | [checks]: https://github.com/Kestrer/bounded-integer/actions?query=workflow%3ACI+branch%3Amaster 12 | [checks-badge]: https://github.com/Kestrer/bounded-integer/workflows/CI/badge.svg 13 | 14 | This crate provides two types of bounded integer for use in Rust. 15 | 16 | ## Macro-generated bounded integers 17 | 18 | The [`bounded_integer!`] macro allows you to define your own bounded integer type, given a 19 | specific range it inhabits. For example: 20 | 21 | ```rust 22 | bounded_integer! { 23 | struct MyInteger { 0..8 } 24 | } 25 | let num = MyInteger::new(5).unwrap(); 26 | assert_eq!(num, 5); 27 | ``` 28 | 29 | This macro supports both `struct`s and `enum`s. See the [`examples`] module for the 30 | documentation of generated types. 31 | 32 | ## Const generics-based bounded integers 33 | 34 | You can also create ad-hoc bounded integers via types in this library that use const generics, 35 | for example: 36 | 37 | ```rust 38 | let num = >::new(5).unwrap(); 39 | assert_eq!(num, 5); 40 | ``` 41 | 42 | These integers are shorter to use as they don't require a type declaration or explicit name, 43 | and they interoperate better with other integers that have different ranges. However due to the 44 | limits of const generics, they do not implement some traits like `Default`. 45 | 46 | ## `no_std` 47 | 48 | All the integers in this crate depend only on libcore and so work in `#![no_std]` environments. 49 | 50 | ## Crate Features 51 | 52 | By default, no crate features are enabled. 53 | - `std`: Interopate with `std` — implies `alloc`. Enables the following things: 54 | - An implementation of [`Error`] for [`ParseError`]. 55 | - `alloc`: Interopate with `alloc`. Enables the following things: 56 | - Support for indexing with the const-generic integers on `Vec` and `VecDeque`. 57 | - `macro`: Enable the [`bounded_integer!`] macro. 58 | - `types`: Enable the bounded integer types that use const generics. 59 | - `arbitrary1`: Implement [`Arbitrary`] for the bounded integers. This is useful when using 60 | bounded integers as fuzzing inputs. 61 | - `bytemuck1`: Implement [`Contiguous`] for all bounded integers, and [`Zeroable`] for 62 | macro-generated bounded integers that support it. 63 | - `num-traits02`: Implement [`Bounded`], [`AsPrimitive`], [`FromPrimitive`], [`NumCast`], 64 | [`ToPrimitive`], [`CheckedAdd`], [`CheckedDiv`], [`CheckedMul`], [`CheckedNeg`], [`CheckedRem`], 65 | [`CheckedSub`], [`MulAdd`], [`SaturatingAdd`], [`SaturatingMul`] and [`SaturatingSub`] for all 66 | const-generic bounded integers. 67 | - `serde1`: Implement [`Serialize`] and [`Deserialize`] for the bounded integers, making sure all 68 | values will never be out of bounds. This has a deprecated alias `serde`. 69 | - `zerocopy`: Implement [`IntoBytes`] for all bounded integers, and [`Unaligned`] for 70 | macro-generated ones. 71 | - `step_trait`: Implement the [`Step`] trait which allows the bounded integers to be easily used 72 | in ranges. This will require you to use nightly and place `#![feature(step_trait)]` in your 73 | crate root if you use the macro. 74 | 75 | [`bounded_integer!`]: https://docs.rs/bounded-integer/*/bounded_integer/macro.bounded_integer.html 76 | [`examples`]: https://docs.rs/bounded-integer/*/bounded_integer/examples/ 77 | [`Arbitrary`]: https://docs.rs/arbitrary/1/arbitrary/trait.Arbitrary.html 78 | [`Contiguous`]: https://docs.rs/bytemuck/1/bytemuck/trait.Contiguous.html 79 | [`Zeroable`]: https://docs.rs/bytemuck/1/bytemuck/trait.Zeroable.html 80 | [`Bounded`]: https://docs.rs/num-traits/0/num_traits/bounds/trait.Bounded.html 81 | [`AsPrimitive`]: https://docs.rs/num-traits/0/num_traits/cast/trait.AsPrimitive.html 82 | [`FromPrimitive`]: https://docs.rs/num-traits/0/num_traits/cast/trait.FromPrimitive.html 83 | [`NumCast`]: https://docs.rs/num-traits/0/num_traits/cast/trait.NumCast.html 84 | [`ToPrimitive`]: https://docs.rs/num-traits/0/num_traits/cast/trait.ToPrimitive.html 85 | [`CheckedAdd`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedAdd.html 86 | [`CheckedDiv`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedDiv.html 87 | [`CheckedMul`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedMul.html 88 | [`CheckedNeg`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedNeg.html 89 | [`CheckedRem`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedRem.html 90 | [`CheckedSub`]: https://docs.rs/num-traits/0/num_traits/ops/checked/trait.CheckedSub.html 91 | [`MulAdd`]: https://docs.rs/num-traits/0/num_traits/ops/mul_add/trait.MulAdd.html 92 | [`SaturatingAdd`]: https://docs.rs/num-traits/0/num_traits/ops/saturating/trait.SaturatingAdd.html 93 | [`SaturatingMul`]: https://docs.rs/num-traits/0/num_traits/ops/saturating/trait.SaturatingMul.html 94 | [`SaturatingSub`]: https://docs.rs/num-traits/0/num_traits/ops/saturating/trait.SaturatingSub.html 95 | [`Serialize`]: https://docs.rs/serde/1/serde/trait.Serialize.html 96 | [`Deserialize`]: https://docs.rs/serde/1/serde/trait.Deserialize.html 97 | [`IntoBytes`]: https://docs.rs/zerocopy/0.8/zerocopy/trait.IntoBytes.html 98 | [`Unaligned`]: https://docs.rs/zerocopy/0.6/zerocopy/trait.Unaligned.html 99 | [`Step`]: https://doc.rust-lang.org/nightly/core/iter/trait.Step.html 100 | [`Error`]: https://doc.rust-lang.org/stable/std/error/trait.Error.html 101 | [`ParseError`]: https://docs.rs/bounded-integer/*/bounded_integer/struct.ParseError.html 102 | -------------------------------------------------------------------------------- /macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bounded-integer-macro" 3 | version = "0.5.8" 4 | description = "Proc macro for `bounded-integer`. Do not use directly." 5 | keywords = ["bounded", "integer", "macro", "refinement"] 6 | license = "ISC" 7 | repository = "https://github.com/Kestrer/bounded-integer" 8 | readme = "../README.md" 9 | edition = "2021" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [features] 15 | # Internal-only feature, not public API 16 | generate_tests = [] 17 | 18 | [dependencies] 19 | proc-macro2 = "1.0.24" 20 | syn = { version = "2.0.0", features = ["proc-macro", "parsing", "printing", "full"], default-features = false } 21 | quote = "1.0.9" 22 | num-bigint = "0.4.6" 23 | -------------------------------------------------------------------------------- /macro/src/generate.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream}; 2 | use quote::{quote, quote_spanned, ToTokens}; 3 | 4 | use num_bigint::BigInt; 5 | 6 | use crate::{BoundedInteger, Kind, ReprSize, ReprSizeFixed, Signed, Unsigned}; 7 | 8 | pub(crate) fn generate(item: &BoundedInteger, tokens: &mut TokenStream) { 9 | generate_item(item, tokens); 10 | generate_impl(item, tokens); 11 | 12 | // TODO: Implement TryFrom and TryInto. This will require adding an error type to the main crate. 13 | generate_ops_traits(item, tokens); 14 | generate_cmp_traits(item, tokens); 15 | generate_as_ref_borrow(item, tokens); 16 | generate_default(item, tokens); 17 | generate_iter_traits(item, tokens); 18 | generate_from_str(item, tokens); 19 | generate_fmt_traits(item, tokens); 20 | generate_to_primitive_traits(item, tokens); 21 | if item.repr.is_usize() { 22 | generate_index_traits(item, tokens); 23 | 24 | if item.std { 25 | generate_index_traits_alloc(item, tokens, &Ident::new("std", Span::call_site())); 26 | } else if item.alloc { 27 | generate_index_traits_alloc(item, tokens, &Ident::new("alloc", Span::call_site())); 28 | } 29 | } 30 | if item.arbitrary1 { 31 | generate_arbitrary1(item, tokens); 32 | } 33 | if item.bytemuck1 { 34 | generate_bytemuck1(item, tokens); 35 | } 36 | if item.serde1 { 37 | generate_serde1(item, tokens); 38 | } 39 | 40 | if cfg!(feature = "generate_tests") { 41 | generate_tests(item, tokens); 42 | } 43 | } 44 | 45 | fn generate_item(item: &BoundedInteger, tokens: &mut TokenStream) { 46 | let repr = &item.repr; 47 | let crate_path = &item.crate_path; 48 | 49 | if item.zerocopy { 50 | let zerocopy = quote!(#crate_path::__private::zerocopy); 51 | 52 | tokens.extend(quote!(#[derive(#zerocopy::IntoBytes)])); 53 | if let ReprSize::Fixed(ReprSizeFixed::Fixed8) = item.repr.size { 54 | tokens.extend(quote!(#[derive(#zerocopy::Unaligned)])); 55 | } 56 | } 57 | 58 | tokens.extend(quote! { 59 | #[derive( 60 | ::core::fmt::Debug, 61 | ::core::hash::Hash, 62 | ::core::clone::Clone, 63 | ::core::marker::Copy, 64 | ::core::cmp::PartialEq, 65 | ::core::cmp::Eq, 66 | ::core::cmp::PartialOrd, 67 | ::core::cmp::Ord 68 | )] 69 | }); 70 | 71 | for attr in &item.attrs { 72 | attr.to_tokens(tokens); 73 | } 74 | 75 | tokens.extend(match &item.kind { 76 | Kind::Enum(_) => quote!(#[repr(#repr)]), 77 | Kind::Struct(_) => quote!(#[repr(transparent)]), 78 | }); 79 | 80 | item.vis.to_tokens(tokens); 81 | 82 | match &item.kind { 83 | Kind::Enum(token) => token.to_tokens(tokens), 84 | Kind::Struct(token) => token.to_tokens(tokens), 85 | } 86 | 87 | item.ident.to_tokens(tokens); 88 | 89 | match &item.kind { 90 | Kind::Struct(_) => { 91 | let span = item.brace_token.span; 92 | tokens.extend(match item.range.contains(&BigInt::ZERO) { 93 | false => quote_spanned!(span=> (::core::num::NonZero<::core::primitive::#repr>);), 94 | true => quote_spanned!(span=> (::core::primitive::#repr);), 95 | }); 96 | } 97 | Kind::Enum(_) => { 98 | let mut inner_tokens = TokenStream::new(); 99 | 100 | let first_variant = enum_variant(item.range.start()); 101 | let start_literal = item.repr.number_literal(item.range.start()); 102 | inner_tokens.extend(quote!(#first_variant = #start_literal)); 103 | 104 | let mut variant = item.range.start() + 1; 105 | while variant <= *item.range.end() { 106 | let name = enum_variant(&variant); 107 | inner_tokens.extend(quote!(, #name)); 108 | variant += 1; 109 | } 110 | 111 | tokens.extend(quote_spanned!(item.brace_token.span=> { #inner_tokens })); 112 | } 113 | } 114 | } 115 | 116 | fn generate_impl(item: &BoundedInteger, tokens: &mut TokenStream) { 117 | let ident = &item.ident; 118 | 119 | let mut content = TokenStream::new(); 120 | generate_min_max_value(item, &mut content); 121 | generate_min_max(item, &mut content); 122 | generate_unchecked_constructors(item, &mut content); 123 | generate_checked_constructors(item, &mut content); 124 | generate_from_str_radix(item, &mut content); 125 | generate_getters(item, &mut content); 126 | generate_inherent_operators(item, &mut content); 127 | generate_checked_operators(item, &mut content); 128 | 129 | tokens.extend(quote! { 130 | impl #ident { 131 | #content 132 | } 133 | }); 134 | } 135 | 136 | fn generate_min_max_value(item: &BoundedInteger, tokens: &mut TokenStream) { 137 | let repr = &item.repr; 138 | let vis = &item.vis; 139 | 140 | let min_value_doc = format!( 141 | "The smallest value that this bounded integer can contain; {}.", 142 | item.range.start() 143 | ); 144 | let max_value_doc = format!( 145 | "The largest value that this bounded integer can contain; {}.", 146 | item.range.end() 147 | ); 148 | 149 | let min_value = repr.number_literal(item.range.start()).into_token_stream(); 150 | let max_value = repr.number_literal(item.range.end()).into_token_stream(); 151 | 152 | tokens.extend(quote! { 153 | #[doc = #min_value_doc] 154 | #vis const MIN_VALUE: ::core::primitive::#repr = #min_value; 155 | #[doc = #max_value_doc] 156 | #vis const MAX_VALUE: ::core::primitive::#repr = #max_value; 157 | }); 158 | } 159 | 160 | fn generate_min_max(item: &BoundedInteger, tokens: &mut TokenStream) { 161 | let vis = &item.vis; 162 | 163 | let min_doc = format!( 164 | "The smallest value of the bounded integer; {}.", 165 | item.range.start() 166 | ); 167 | let max_doc = format!( 168 | "The largest value of the bounded integer; {}.", 169 | item.range.end() 170 | ); 171 | 172 | let (min, max) = match &item.kind { 173 | Kind::Struct(_) => ( 174 | quote!(unsafe { Self::new_unchecked(Self::MIN_VALUE) }), 175 | quote!(unsafe { Self::new_unchecked(Self::MAX_VALUE) }), 176 | ), 177 | Kind::Enum(_) => { 178 | let (min, max) = ( 179 | enum_variant(item.range.start()), 180 | enum_variant(item.range.end()), 181 | ); 182 | 183 | (quote!(Self::#min), quote!(Self::#max)) 184 | } 185 | }; 186 | 187 | tokens.extend(quote! { 188 | #[doc = #min_doc] 189 | #vis const MIN: Self = #min; 190 | #[doc = #max_doc] 191 | #vis const MAX: Self = #max; 192 | }); 193 | } 194 | 195 | fn generate_unchecked_constructors(item: &BoundedInteger, tokens: &mut TokenStream) { 196 | let repr = &item.repr; 197 | let vis = &item.vis; 198 | 199 | let safety_doc = " 200 | # Safety 201 | 202 | The value must not be outside the valid range of values; it must not be less than 203 | [`MIN_VALUE`](Self::MIN_VALUE) or greater than [`MAX_VALUE`](Self::MAX_VALUE).\ 204 | "; 205 | 206 | tokens.extend(quote! { 207 | /// Creates a bounded integer without checking the value. 208 | #[doc = #safety_doc] 209 | #[must_use] 210 | #vis const unsafe fn new_unchecked(n: ::core::primitive::#repr) -> Self { 211 | ::core::debug_assert!(Self::in_range(n)); 212 | unsafe { ::core::mem::transmute::<::core::primitive::#repr, Self>(n) } 213 | } 214 | 215 | /// Creates a shared reference to a bounded integer from a shared reference to a primitive. 216 | #[doc = #safety_doc] 217 | #[must_use] 218 | #vis const unsafe fn new_ref_unchecked(n: &::core::primitive::#repr) -> &Self { 219 | ::core::debug_assert!(Self::in_range(*n)); 220 | unsafe { &*(n as *const ::core::primitive::#repr as *const Self) } 221 | } 222 | 223 | /// Creates a mutable reference to a bounded integer from a mutable reference to a 224 | /// primitive. 225 | #[doc = #safety_doc] 226 | #[must_use] 227 | #vis const unsafe fn new_mut_unchecked(n: &mut ::core::primitive::#repr) -> &mut Self { 228 | ::core::debug_assert!(Self::in_range(*n)); 229 | unsafe { &mut *(n as *mut ::core::primitive::#repr as *mut Self) } 230 | } 231 | }); 232 | } 233 | 234 | fn generate_checked_constructors(item: &BoundedInteger, tokens: &mut TokenStream) { 235 | let repr = &item.repr; 236 | let vis = &item.vis; 237 | 238 | tokens.extend(quote! { 239 | /// Checks whether the given value is in the range of the bounded integer. 240 | #[must_use] 241 | #[inline] 242 | #vis const fn in_range(n: ::core::primitive::#repr) -> ::core::primitive::bool { 243 | n >= Self::MIN_VALUE && n <= Self::MAX_VALUE 244 | } 245 | 246 | /// Creates a bounded integer if the given value is within the range 247 | /// [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 248 | #[must_use] 249 | #[inline] 250 | #vis const fn new(n: ::core::primitive::#repr) -> ::core::option::Option { 251 | if Self::in_range(n) { 252 | ::core::option::Option::Some(unsafe { Self::new_unchecked(n) }) 253 | } else { 254 | ::core::option::Option::None 255 | } 256 | } 257 | 258 | /// Creates a reference to a bounded integer from a reference to a primitive if the 259 | /// given value is within the range [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 260 | #[must_use] 261 | #[inline] 262 | #vis const fn new_ref(n: &::core::primitive::#repr) -> ::core::option::Option<&Self> { 263 | if Self::in_range(*n) { 264 | ::core::option::Option::Some(unsafe { Self::new_ref_unchecked(n) }) 265 | } else { 266 | ::core::option::Option::None 267 | } 268 | } 269 | 270 | /// Creates a mutable reference to a bounded integer from a mutable reference to a 271 | /// primitive if the given value is within the range 272 | /// [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 273 | #[must_use] 274 | #[inline] 275 | #vis const fn new_mut(n: &mut ::core::primitive::#repr) -> ::core::option::Option<&mut Self> { 276 | if Self::in_range(*n) { 277 | ::core::option::Option::Some(unsafe { Self::new_mut_unchecked(n) }) 278 | } else { 279 | ::core::option::Option::None 280 | } 281 | } 282 | 283 | /// Creates a bounded integer by setting the value to [`MIN`](Self::MIN) or 284 | /// [`MAX`](Self::MAX) if it is too low or too high respectively. 285 | #[must_use] 286 | #[inline] 287 | #vis const fn new_saturating(n: ::core::primitive::#repr) -> Self { 288 | if n < Self::MIN_VALUE { 289 | Self::MIN 290 | } else if n > Self::MAX_VALUE { 291 | Self::MAX 292 | } else { 293 | unsafe { Self::new_unchecked(n) } 294 | } 295 | } 296 | }); 297 | } 298 | 299 | fn generate_from_str_radix(item: &BoundedInteger, tokens: &mut TokenStream) { 300 | let repr = &item.repr; 301 | let vis = &item.vis; 302 | let crate_path = &item.crate_path; 303 | 304 | tokens.extend(quote! { 305 | /// Converts a string slice in a given base to the bounded integer. 306 | /// 307 | /// # Panics 308 | /// 309 | /// Panics if `radix` is below 2 or above 36. 310 | #vis fn from_str_radix( 311 | src: &::core::primitive::str, 312 | radix: ::core::primitive::u32 313 | ) -> ::core::result::Result { 314 | let value: ::core::primitive::#repr = #crate_path::__private::FromStrRadix::from_str_radix( 315 | src, 316 | radix 317 | )?; 318 | if value < Self::MIN_VALUE { 319 | ::core::result::Result::Err(#crate_path::__private::error_below_min()) 320 | } else if value > Self::MAX_VALUE { 321 | ::core::result::Result::Err(#crate_path::__private::error_above_max()) 322 | } else { 323 | ::core::result::Result::Ok(unsafe { Self::new_unchecked(value) }) 324 | } 325 | } 326 | }); 327 | } 328 | 329 | fn generate_getters(item: &BoundedInteger, tokens: &mut TokenStream) { 330 | let repr = &item.repr; 331 | let vis = &item.vis; 332 | 333 | tokens.extend(quote! { 334 | /// Returns the value of the bounded integer as a primitive type. 335 | #[must_use] 336 | #[inline] 337 | #vis const fn get(self) -> ::core::primitive::#repr { 338 | unsafe { ::core::mem::transmute::(self) } 339 | } 340 | 341 | /// Returns a shared reference to the value of the bounded integer. 342 | #[must_use] 343 | #[inline] 344 | #vis const fn get_ref(&self) -> &::core::primitive::#repr { 345 | unsafe { &*(self as *const Self as *const ::core::primitive::#repr) } 346 | } 347 | 348 | /// Returns a mutable reference to the value of the bounded integer. 349 | /// 350 | /// # Safety 351 | /// 352 | /// This value must never be set to a value beyond the range of the bounded integer. 353 | #[must_use] 354 | #[inline] 355 | #vis const unsafe fn get_mut(&mut self) -> &mut ::core::primitive::#repr { 356 | unsafe { &mut *(self as *mut Self as *mut ::core::primitive::#repr) } 357 | } 358 | }); 359 | } 360 | 361 | fn generate_inherent_operators(item: &BoundedInteger, tokens: &mut TokenStream) { 362 | let vis = &item.vis; 363 | let repr = &item.repr; 364 | 365 | if item.repr.sign == Signed { 366 | tokens.extend(quote! { 367 | /// Computes the absolute value of `self`, panicking if it is out of range. 368 | #[must_use] 369 | #[inline] 370 | #vis const fn abs(self) -> Self { 371 | self.checked_abs().expect("absolute value should be in range") 372 | } 373 | }); 374 | } 375 | 376 | tokens.extend(quote! { 377 | /// Raises `self` to the power of `exp`, using exponentiation by squaring. Panics if it 378 | /// is out of range. 379 | #[must_use] 380 | #[inline] 381 | #vis const fn pow(self, exp: ::core::primitive::u32) -> Self { 382 | self.checked_pow(exp).expect("value raised to power should be in range") 383 | } 384 | /// Calculates the quotient of Euclidean division of `self` by `rhs`. Panics if `rhs` 385 | /// is 0 or the result is out of range. 386 | #[must_use] 387 | #[inline] 388 | #vis const fn div_euclid(self, rhs: ::core::primitive::#repr) -> Self { 389 | self.checked_div_euclid(rhs).expect("division should be in range") 390 | } 391 | /// Calculates the least nonnegative remainder of `self (mod rhs)`. Panics if `rhs` is 0 392 | /// or the result is out of range. 393 | #[must_use] 394 | #[inline] 395 | #vis const fn rem_euclid(self, rhs: ::core::primitive::#repr) -> Self { 396 | self.checked_rem_euclid(rhs).expect("division with remainer should be in range") 397 | } 398 | }); 399 | } 400 | 401 | fn generate_checked_operators(item: &BoundedInteger, tokens: &mut TokenStream) { 402 | let vis = &item.vis; 403 | 404 | for op in CHECKED_OPERATORS { 405 | if item.repr.sign == Unsigned && op.variants == OpVariants::Signed { 406 | continue; 407 | } 408 | 409 | let rhs = match op.rhs { 410 | Some("Self") => Some({ 411 | let repr = &item.repr; 412 | quote!(::core::primitive::#repr) 413 | }), 414 | Some(name) => Some({ 415 | let ident = Ident::new(name, Span::call_site()); 416 | quote!(::core::primitive::#ident) 417 | }), 418 | None => None, 419 | }; 420 | let rhs_type = rhs.as_ref().map(|ty| quote!(rhs: #ty,)); 421 | let rhs_value = rhs.map(|_| quote!(rhs,)); 422 | 423 | let checked_name = Ident::new(&format!("checked_{}", op.name), Span::call_site()); 424 | let checked_comment = format!("Checked {}.", op.description.trim()); 425 | 426 | tokens.extend(quote! { 427 | #[doc = #checked_comment] 428 | #[must_use] 429 | #[inline] 430 | #vis const fn #checked_name(self, #rhs_type) -> ::core::option::Option { 431 | match self.get().#checked_name(#rhs_value) { 432 | ::core::option::Option::Some(val) => Self::new(val), 433 | ::core::option::Option::None => ::core::option::Option::None, 434 | } 435 | } 436 | }); 437 | 438 | if op.variants == OpVariants::NoSaturating 439 | || item.repr.sign == Unsigned && op.variants == OpVariants::SignedSaturating 440 | { 441 | continue; 442 | } 443 | 444 | let saturating_name = Ident::new(&format!("saturating_{}", op.name), Span::call_site()); 445 | let saturating_comment = format!("Saturating {}.", op.description.trim()); 446 | 447 | tokens.extend(quote! { 448 | #[doc = #saturating_comment] 449 | #[must_use] 450 | #[inline] 451 | #vis const fn #saturating_name(self, #rhs_type) -> Self { 452 | Self::new_saturating(self.get().#saturating_name(#rhs_value)) 453 | } 454 | }); 455 | } 456 | } 457 | 458 | struct CheckedOperator { 459 | name: &'static str, 460 | description: &'static str, 461 | rhs: Option<&'static str>, 462 | variants: OpVariants, 463 | } 464 | 465 | #[derive(Eq, PartialEq, Clone, Copy)] 466 | enum OpVariants { 467 | /// Checked and saturating ops on all integers. 468 | All, 469 | /// Checked operators on unsigned integers, checked and saturating ops on signed integers. 470 | SignedSaturating, 471 | /// Checked operators on all integers. 472 | NoSaturating, 473 | /// Checked and saturating operations on signed integers. 474 | Signed, 475 | } 476 | 477 | macro_rules! tokens_or { 478 | (() ($($else:tt)*)) => { $($else)* }; 479 | (($($tokens:tt)*) ($($else:tt)*)) => { $($tokens)* }; 480 | } 481 | macro_rules! checked_operators { 482 | ($( 483 | #[doc = $description:literal] 484 | fn $name:ident($($rhs:ty)?) 485 | $variants:ident 486 | ,)*) => { 487 | [$( 488 | CheckedOperator { 489 | name: stringify!($name), 490 | description: $description, 491 | rhs: tokens_or!(($(Some(stringify!($rhs)))?) (None)), 492 | variants: OpVariants::$variants, 493 | } 494 | ,)*] 495 | } 496 | } 497 | 498 | const CHECKED_OPERATORS: &[CheckedOperator] = &checked_operators! { 499 | /** integer addition */ fn add (Self) All , 500 | /** integer subtraction */ fn sub (Self) All , 501 | /** integer multiplication */ fn mul (Self) All , 502 | /** integer division */ fn div (Self) NoSaturating , 503 | /** Euclidean division */ fn div_euclid (Self) NoSaturating , 504 | /** integer remainder */ fn rem (Self) NoSaturating , 505 | /** Euclidean remainder */ fn rem_euclid (Self) NoSaturating , 506 | /** negation */ fn neg ( ) SignedSaturating, 507 | /** absolute value */ fn abs ( ) Signed , 508 | /** exponentiation */ fn pow (u32 ) All , 509 | /** shift left */ fn shl (u32 ) NoSaturating , 510 | /** shift right */ fn shr (u32 ) NoSaturating , 511 | }; 512 | 513 | fn generate_ops_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 514 | let repr = &item.repr; 515 | let full_repr = quote!(::core::primitive::#repr); 516 | 517 | for op in OPERATORS { 518 | if item.repr.sign == Unsigned && !op.on_unsigned { 519 | continue; 520 | } 521 | 522 | let description = op.description; 523 | 524 | if op.bin { 525 | // bounded + repr 526 | binop_trait_variations( 527 | op.trait_name, 528 | op.method, 529 | &item.ident, 530 | &full_repr, 531 | |trait_name, method| { 532 | quote! { 533 | Self::new(<#full_repr as ::core::ops::#trait_name>::#method(self.get(), rhs)) 534 | .expect(::core::concat!("result of ", #description, " should be in range")) 535 | } 536 | }, 537 | tokens, 538 | ); 539 | 540 | // repr + bounded 541 | binop_trait_variations( 542 | op.trait_name, 543 | op.method, 544 | &full_repr, 545 | &item.ident, 546 | |trait_name, method| { 547 | quote! { 548 | >::#method(self, rhs.get()) 549 | } 550 | }, 551 | tokens, 552 | ); 553 | 554 | // bounded + bounded 555 | binop_trait_variations( 556 | op.trait_name, 557 | op.method, 558 | &item.ident, 559 | &item.ident, 560 | |trait_name, method| { 561 | quote! { 562 | >::#method(self, rhs.get()) 563 | } 564 | }, 565 | tokens, 566 | ); 567 | } else { 568 | let trait_name = Ident::new(op.trait_name, Span::call_site()); 569 | let method = Ident::new(op.method, Span::call_site()); 570 | 571 | unop_trait_variations( 572 | &trait_name, 573 | &method, 574 | &item.ident, 575 | "e! { 576 | Self::new(<#full_repr as ::core::ops::#trait_name>::#method(self.get())) 577 | .expect(::core::concat!("result of ", #description, " should be in range")) 578 | }, 579 | tokens, 580 | ); 581 | } 582 | } 583 | } 584 | 585 | fn binop_trait_variations( 586 | trait_name_root: &str, 587 | method_root: &str, 588 | lhs: &impl ToTokens, 589 | rhs: &impl ToTokens, 590 | body: impl FnOnce(&Ident, &Ident) -> B, 591 | tokens: &mut TokenStream, 592 | ) { 593 | let trait_name = Ident::new(trait_name_root, Span::call_site()); 594 | let trait_name_assign = Ident::new(&format!("{trait_name_root}Assign"), Span::call_site()); 595 | let method = Ident::new(method_root, Span::call_site()); 596 | let method_assign = Ident::new(&format!("{method_root}_assign"), Span::call_site()); 597 | let body = body(&trait_name, &method); 598 | 599 | tokens.extend(quote! { 600 | impl ::core::ops::#trait_name<#rhs> for #lhs { 601 | type Output = #lhs; 602 | #[inline] 603 | fn #method(self, rhs: #rhs) -> Self::Output { 604 | #body 605 | } 606 | } 607 | impl ::core::ops::#trait_name<#rhs> for &#lhs { 608 | type Output = #lhs; 609 | #[inline] 610 | fn #method(self, rhs: #rhs) -> Self::Output { 611 | <#lhs as ::core::ops::#trait_name<#rhs>>::#method(*self, rhs) 612 | } 613 | } 614 | impl<'b> ::core::ops::#trait_name<&'b #rhs> for #lhs { 615 | type Output = #lhs; 616 | #[inline] 617 | fn #method(self, rhs: &'b #rhs) -> Self::Output { 618 | <#lhs as ::core::ops::#trait_name<#rhs>>::#method(self, *rhs) 619 | } 620 | } 621 | impl<'a> ::core::ops::#trait_name<&'a #rhs> for &#lhs { 622 | type Output = #lhs; 623 | #[inline] 624 | fn #method(self, rhs: &'a #rhs) -> Self::Output { 625 | <#lhs as ::core::ops::#trait_name<#rhs>>::#method(*self, *rhs) 626 | } 627 | } 628 | 629 | impl ::core::ops::#trait_name_assign<#rhs> for #lhs { 630 | #[inline] 631 | fn #method_assign(&mut self, rhs: #rhs) { 632 | *self = >::#method(*self, rhs); 633 | } 634 | } 635 | impl<'a> ::core::ops::#trait_name_assign<&'a #rhs> for #lhs { 636 | #[inline] 637 | fn #method_assign(&mut self, rhs: &'a #rhs) { 638 | *self = >::#method(*self, *rhs); 639 | } 640 | } 641 | }); 642 | } 643 | 644 | fn unop_trait_variations( 645 | trait_name: &impl ToTokens, 646 | method: &impl ToTokens, 647 | lhs: &impl ToTokens, 648 | body: &impl ToTokens, 649 | tokens: &mut TokenStream, 650 | ) { 651 | tokens.extend(quote! { 652 | impl ::core::ops::#trait_name for #lhs { 653 | type Output = #lhs; 654 | #[inline] 655 | fn #method(self) -> Self::Output { 656 | #body 657 | } 658 | } 659 | impl ::core::ops::#trait_name for &#lhs { 660 | type Output = #lhs; 661 | #[inline] 662 | fn #method(self) -> Self::Output { 663 | <#lhs as ::core::ops::#trait_name>::#method(*self) 664 | } 665 | } 666 | }); 667 | } 668 | 669 | #[rustfmt::skip] 670 | const OPERATORS: &[Operator] = &[ 671 | Operator { trait_name: "Add" , method: "add" , description: "add" , bin: true , on_unsigned: true }, 672 | Operator { trait_name: "Sub" , method: "sub" , description: "subtract" , bin: true , on_unsigned: true }, 673 | Operator { trait_name: "Mul" , method: "mul" , description: "multiply" , bin: true , on_unsigned: true }, 674 | Operator { trait_name: "Div" , method: "div" , description: "divide" , bin: true , on_unsigned: true }, 675 | Operator { trait_name: "Rem" , method: "rem" , description: "take remainder", bin: true , on_unsigned: true }, 676 | Operator { trait_name: "Neg" , method: "neg" , description: "negate" , bin: false, on_unsigned: false }, 677 | Operator { trait_name: "Not" , method: "not" , description: "not" , bin: false, on_unsigned: true }, 678 | Operator { trait_name: "BitAnd", method: "bitand", description: "binary and" , bin: true , on_unsigned: true }, 679 | Operator { trait_name: "BitOr" , method: "bitor" , description: "binary or" , bin: true , on_unsigned: true }, 680 | Operator { trait_name: "BitXor", method: "bitxor", description: "binary xor" , bin: true , on_unsigned: true }, 681 | Operator { trait_name: "Shl" , method: "shl" , description: "shift left" , bin: true , on_unsigned: true }, 682 | Operator { trait_name: "Shr" , method: "shr" , description: "shift right" , bin: true , on_unsigned: true }, 683 | ]; 684 | 685 | struct Operator { 686 | trait_name: &'static str, 687 | method: &'static str, 688 | description: &'static str, 689 | bin: bool, 690 | on_unsigned: bool, 691 | } 692 | 693 | fn generate_cmp_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 694 | let ident = &item.ident; 695 | let repr = &item.repr; 696 | 697 | // These are only impls that can't be derived 698 | tokens.extend(quote! { 699 | impl ::core::cmp::PartialEq<::core::primitive::#repr> for #ident { 700 | #[inline] 701 | fn eq(&self, other: &::core::primitive::#repr) -> bool { 702 | self.get() == *other 703 | } 704 | } 705 | impl ::core::cmp::PartialEq<#ident> for ::core::primitive::#repr { 706 | #[inline] 707 | fn eq(&self, other: &#ident) -> bool { 708 | *self == other.get() 709 | } 710 | } 711 | impl ::core::cmp::PartialOrd<::core::primitive::#repr> for #ident { 712 | #[inline] 713 | fn partial_cmp( 714 | &self, 715 | other: &::core::primitive::#repr 716 | ) -> ::core::option::Option<::core::cmp::Ordering> { 717 | ::core::cmp::PartialOrd::partial_cmp(&self.get(), other) 718 | } 719 | } 720 | impl ::core::cmp::PartialOrd<#ident> for ::core::primitive::#repr { 721 | #[inline] 722 | fn partial_cmp( 723 | &self, 724 | other: &#ident 725 | ) -> ::core::option::Option<::core::cmp::Ordering> { 726 | ::core::cmp::PartialOrd::partial_cmp(self, &other.get()) 727 | } 728 | } 729 | }); 730 | } 731 | 732 | fn generate_as_ref_borrow(item: &BoundedInteger, tokens: &mut TokenStream) { 733 | let ident = &item.ident; 734 | let repr = &item.repr; 735 | 736 | tokens.extend(quote! { 737 | impl ::core::convert::AsRef<::core::primitive::#repr> for #ident { 738 | #[inline] 739 | fn as_ref(&self) -> &::core::primitive::#repr { 740 | self.get_ref() 741 | } 742 | } 743 | impl ::core::borrow::Borrow<::core::primitive::#repr> for #ident { 744 | #[inline] 745 | fn borrow(&self) -> &::core::primitive::#repr { 746 | self.get_ref() 747 | } 748 | } 749 | }); 750 | } 751 | 752 | fn generate_default(item: &BoundedInteger, tokens: &mut TokenStream) { 753 | let ident = &item.ident; 754 | 755 | if item.range.contains(&BigInt::ZERO) { 756 | tokens.extend(quote! { 757 | impl ::core::default::Default for #ident { 758 | fn default() -> Self { 759 | unsafe { Self::new_unchecked(0) } 760 | } 761 | } 762 | }); 763 | } 764 | } 765 | 766 | fn generate_iter_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 767 | let ident = &item.ident; 768 | let repr = &item.repr; 769 | 770 | if item.range.contains(&BigInt::ZERO) { 771 | tokens.extend(quote! { 772 | impl ::core::iter::Sum for #ident { 773 | fn sum>(iter: I) -> Self { 774 | ::core::iter::Iterator::fold( 775 | iter, 776 | ::default(), 777 | ::core::ops::Add::add, 778 | ) 779 | } 780 | } 781 | impl<'a> ::core::iter::Sum<&'a Self> for #ident { 782 | fn sum>(iter: I) -> Self { 783 | ::core::iter::Iterator::sum(::core::iter::Iterator::copied(iter)) 784 | } 785 | } 786 | }); 787 | } 788 | 789 | tokens.extend(quote! { 790 | impl ::core::iter::Sum<#ident> for ::core::primitive::#repr { 791 | fn sum>(iter: I) -> Self { 792 | ::core::iter::Iterator::sum(::core::iter::Iterator::map(iter, #ident::get)) 793 | } 794 | } 795 | impl<'a> ::core::iter::Sum<&'a #ident> for ::core::primitive::#repr { 796 | fn sum>(iter: I) -> Self { 797 | ::core::iter::Iterator::sum(::core::iter::Iterator::copied(iter)) 798 | } 799 | } 800 | }); 801 | 802 | if item.range.contains(&BigInt::from(1)) { 803 | tokens.extend(quote! { 804 | impl ::core::iter::Product for #ident { 805 | fn product>(iter: I) -> Self { 806 | ::core::iter::Iterator::fold( 807 | iter, 808 | unsafe { Self::new_unchecked(1) }, 809 | ::core::ops::Mul::mul, 810 | ) 811 | } 812 | } 813 | impl<'a> ::core::iter::Product<&'a Self> for #ident { 814 | fn product>(iter: I) -> Self { 815 | ::core::iter::Iterator::product(::core::iter::Iterator::copied(iter)) 816 | } 817 | } 818 | }); 819 | } 820 | 821 | tokens.extend(quote! { 822 | impl ::core::iter::Product<#ident> for ::core::primitive::#repr { 823 | fn product>(iter: I) -> Self { 824 | ::core::iter::Iterator::product(::core::iter::Iterator::map(iter, #ident::get)) 825 | } 826 | } 827 | impl<'a> ::core::iter::Product<&'a #ident> for ::core::primitive::#repr { 828 | fn product>(iter: I) -> Self { 829 | ::core::iter::Iterator::product(::core::iter::Iterator::copied(iter)) 830 | } 831 | } 832 | }); 833 | 834 | if item.step_trait { 835 | tokens.extend(quote! { 836 | impl ::core::iter::Step for #ident { 837 | #[inline] 838 | fn steps_between(start: &Self, end: &Self) -> (::core::primitive::usize, ::core::option::Option<::core::primitive::usize>) { 839 | ::core::iter::Step::steps_between(&start.get(), &end.get()) 840 | } 841 | #[inline] 842 | fn forward_checked(start: Self, count: ::core::primitive::usize) -> ::core::option::Option { 843 | ::core::iter::Step::forward_checked(start.get(), count).and_then(Self::new) 844 | } 845 | #[inline] 846 | fn backward_checked(start: Self, count: ::core::primitive::usize) -> ::core::option::Option { 847 | ::core::iter::Step::backward_checked(start.get(), count).and_then(Self::new) 848 | } 849 | } 850 | }); 851 | } 852 | } 853 | 854 | fn generate_from_str(item: &BoundedInteger, tokens: &mut TokenStream) { 855 | let ident = &item.ident; 856 | let crate_path = &item.crate_path; 857 | 858 | tokens.extend(quote! { 859 | impl ::core::str::FromStr for #ident { 860 | type Err = #crate_path::ParseError; 861 | fn from_str(s: &::core::primitive::str) -> ::core::result::Result { 862 | Self::from_str_radix(s, 10) 863 | } 864 | } 865 | }); 866 | } 867 | 868 | fn generate_fmt_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 869 | let ident = &item.ident; 870 | let repr = &item.repr; 871 | 872 | for &fmt_trait in &[ 873 | "Binary", "Display", "LowerExp", "LowerHex", "Octal", "UpperExp", "UpperHex", 874 | ] { 875 | let fmt_trait = Ident::new(fmt_trait, Span::call_site()); 876 | 877 | tokens.extend(quote! { 878 | impl ::core::fmt::#fmt_trait for #ident { 879 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 880 | <::core::primitive::#repr as ::core::fmt::#fmt_trait>::fmt(&self.get(), f) 881 | } 882 | } 883 | }); 884 | } 885 | } 886 | 887 | fn generate_to_primitive_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 888 | let ident = &item.ident; 889 | 890 | for repr in item.repr.larger_reprs() { 891 | tokens.extend(quote! { 892 | impl ::core::convert::From<#ident> for ::core::primitive::#repr { 893 | #[inline] 894 | fn from(bounded: #ident) -> Self { 895 | ::core::convert::From::from(bounded.get()) 896 | } 897 | } 898 | }); 899 | } 900 | } 901 | 902 | fn generate_index_traits(item: &BoundedInteger, tokens: &mut TokenStream) { 903 | let ident = &item.ident; 904 | 905 | tokens.extend(quote! { 906 | impl ::core::ops::Index<#ident> for [T] { 907 | type Output = T; 908 | 909 | #[inline] 910 | fn index(&self, index: #ident) -> &Self::Output { 911 | &self[index.get()] 912 | } 913 | } 914 | 915 | impl for [T] { 916 | #[inline] 917 | fn index_mut(&mut self, index: #ident) -> &mut Self::Output { 918 | &mut self[index.get()] 919 | } 920 | } 921 | }); 922 | } 923 | 924 | fn generate_index_traits_alloc( 925 | item: &BoundedInteger, 926 | tokens: &mut TokenStream, 927 | alloc_or_std: &Ident, 928 | ) { 929 | let ident = &item.ident; 930 | 931 | tokens.extend(quote! { 932 | impl 933 | for ::#alloc_or_std::vec::Vec 934 | { 935 | type Output = T; 936 | 937 | #[inline] 938 | fn index(&self, index: #ident) -> &Self::Output { 939 | &self[index.get()] 940 | } 941 | } 942 | impl 943 | for ::#alloc_or_std::collections::VecDeque 944 | { 945 | type Output = T; 946 | 947 | #[inline] 948 | fn index(&self, index: #ident) -> &Self::Output { 949 | &self[index.get()] 950 | } 951 | } 952 | 953 | impl 954 | for ::#alloc_or_std::vec::Vec 955 | { 956 | #[inline] 957 | fn index_mut(&mut self, index: #ident) -> &mut Self::Output { 958 | &mut self[index.get()] 959 | } 960 | } 961 | impl 962 | for ::#alloc_or_std::collections::VecDeque 963 | { 964 | #[inline] 965 | fn index_mut(&mut self, index: #ident) -> &mut Self::Output { 966 | &mut self[index.get()] 967 | } 968 | } 969 | }); 970 | } 971 | 972 | fn generate_arbitrary1(item: &BoundedInteger, tokens: &mut TokenStream) { 973 | let ident = &item.ident; 974 | let repr = &item.repr; 975 | let crate_path = &item.crate_path; 976 | let arbitrary = quote!(#crate_path::__private::arbitrary1); 977 | 978 | tokens.extend(quote! { 979 | impl<'a> #arbitrary::Arbitrary<'a> for #ident { 980 | fn arbitrary(u: &mut #arbitrary::Unstructured<'a>) -> #arbitrary::Result { 981 | Self::new(u.arbitrary()?).ok_or(#arbitrary::Error::IncorrectFormat) 982 | } 983 | 984 | #[inline] 985 | fn size_hint( 986 | depth: ::core::primitive::usize, 987 | ) -> (::core::primitive::usize, ::core::option::Option<::core::primitive::usize>) { 988 | <#repr as #arbitrary::Arbitrary<'a>>::size_hint(depth) 989 | } 990 | } 991 | }); 992 | } 993 | 994 | fn generate_bytemuck1(item: &BoundedInteger, tokens: &mut TokenStream) { 995 | let ident = &item.ident; 996 | let repr = &item.repr; 997 | let crate_path = &item.crate_path; 998 | let bytemuck = quote!(#crate_path::__private::bytemuck1); 999 | 1000 | tokens.extend(quote! { 1001 | unsafe impl #bytemuck::Contiguous for #ident { 1002 | type Int = ::core::primitive::#repr; 1003 | const MAX_VALUE: ::core::primitive::#repr = Self::MAX_VALUE; 1004 | const MIN_VALUE: ::core::primitive::#repr = Self::MIN_VALUE; 1005 | } 1006 | }); 1007 | 1008 | if item.range.contains(&BigInt::ZERO) { 1009 | tokens.extend(quote! { 1010 | unsafe impl #bytemuck::Zeroable for #ident {} 1011 | }); 1012 | } 1013 | } 1014 | 1015 | fn generate_serde1(item: &BoundedInteger, tokens: &mut TokenStream) { 1016 | let ident = &item.ident; 1017 | let repr = &item.repr; 1018 | let crate_path = &item.crate_path; 1019 | let serde = quote!(#crate_path::__private::serde1); 1020 | 1021 | tokens.extend(quote! { 1022 | impl #serde::Serialize for #ident { 1023 | fn serialize(&self, serializer: S) -> ::core::result::Result< 1024 | ::Ok, 1025 | ::Error, 1026 | > 1027 | where 1028 | S: #serde::Serializer, 1029 | { 1030 | <::core::primitive::#repr as #serde::Serialize>::serialize(&self.get(), serializer) 1031 | } 1032 | } 1033 | }); 1034 | 1035 | tokens.extend(quote! { 1036 | impl<'de> #serde::Deserialize<'de> for #ident { 1037 | fn deserialize(deserializer: D) -> ::core::result::Result< 1038 | Self, 1039 | >::Error, 1040 | > 1041 | where 1042 | D: #serde::Deserializer<'de>, 1043 | { 1044 | let value = <::core::primitive::#repr as #serde::Deserialize<'de>>::deserialize(deserializer)?; 1045 | Self::new(value) 1046 | .ok_or_else(|| { 1047 | <>::Error as #serde::de::Error>::custom( 1048 | ::core::format_args!( 1049 | "integer out of range, expected it to be between {} and {}", 1050 | Self::MIN_VALUE, 1051 | Self::MAX_VALUE, 1052 | ) 1053 | ) 1054 | }) 1055 | } 1056 | } 1057 | }); 1058 | } 1059 | 1060 | fn generate_tests(item: &BoundedInteger, tokens: &mut TokenStream) { 1061 | let mut tests = TokenStream::new(); 1062 | 1063 | generate_test_layout(item, &mut tests); 1064 | generate_test_range(item, &mut tests); 1065 | generate_test_arithmetic(item, &mut tests); 1066 | 1067 | tokens.extend(quote! { 1068 | mod tests { 1069 | use super::*; 1070 | use ::core::mem::size_of; 1071 | use ::core::option::Option::{self, Some, None}; 1072 | use ::core::primitive::*; 1073 | use ::core::{assert, assert_eq}; 1074 | #tests 1075 | } 1076 | }); 1077 | } 1078 | 1079 | fn generate_test_layout(item: &BoundedInteger, tokens: &mut TokenStream) { 1080 | let ident = &item.ident; 1081 | let repr = &item.repr; 1082 | 1083 | tokens.extend(quote! { 1084 | const _: [(); size_of::<#repr>()] = [(); size_of::<#ident>()]; 1085 | }); 1086 | 1087 | if !item.range.contains(&BigInt::ZERO) { 1088 | tokens.extend(quote! { 1089 | const _: [(); size_of::<#repr>()] = [(); size_of::>()]; 1090 | }); 1091 | } 1092 | } 1093 | 1094 | fn generate_test_range(item: &BoundedInteger, tokens: &mut TokenStream) { 1095 | let ident = &item.ident; 1096 | let repr = &item.repr; 1097 | 1098 | let min = item.repr.number_literal(item.range.start()); 1099 | let max = item.repr.number_literal(item.range.end()); 1100 | 1101 | let above_min = item.repr.number_literal(item.range.start() + 1); 1102 | let below_max = item.repr.number_literal(item.range.end() - 1); 1103 | 1104 | let opt_literal = |num| { 1105 | if let Ok(lit) = item.repr.try_number_literal(num) { 1106 | quote!(Some(#lit)) 1107 | } else { 1108 | quote!(None) 1109 | } 1110 | }; 1111 | let below_range = opt_literal(item.range.start() - 1); 1112 | let above_range = opt_literal(item.range.end() + 1); 1113 | 1114 | tokens.extend(quote! { 1115 | #[test] 1116 | fn range() { 1117 | assert_eq!(#ident::MIN_VALUE, #min); 1118 | assert_eq!(#ident::MAX_VALUE, #max); 1119 | assert_eq!(#ident::MIN.get(), #min); 1120 | assert_eq!(#ident::MAX.get(), #max); 1121 | 1122 | if let Some(below_range) = #below_range { 1123 | assert!(!#ident::in_range(below_range)); 1124 | } else { 1125 | assert_eq!(#ident::MIN_VALUE, #repr::MIN); 1126 | } 1127 | assert!(#ident::in_range(#min)); 1128 | assert!(#ident::in_range(#above_min)); 1129 | assert!(#ident::in_range(#below_max)); 1130 | assert!(#ident::in_range(#max)); 1131 | if let Some(above_range) = #above_range { 1132 | assert!(!#ident::in_range(above_range)); 1133 | } else { 1134 | assert_eq!(#ident::MAX_VALUE, #repr::MAX); 1135 | } 1136 | } 1137 | 1138 | #[test] 1139 | fn saturating() { 1140 | assert_eq!(#ident::new_saturating(#repr::MIN), #ident::MIN_VALUE); 1141 | if let Some(below_range) = #below_range { 1142 | assert_eq!(#ident::new_saturating(below_range), #ident::MIN_VALUE); 1143 | } 1144 | assert_eq!(#ident::new_saturating(#min), #ident::MIN_VALUE); 1145 | 1146 | assert_eq!(#ident::new_saturating(#above_min).get(), #above_min); 1147 | assert_eq!(#ident::new_saturating(#below_max).get(), #below_max); 1148 | 1149 | assert_eq!(#ident::new_saturating(#max), #ident::MAX_VALUE); 1150 | if let Some(above_range) = #above_range { 1151 | assert_eq!(#ident::new_saturating(above_range), #ident::MAX_VALUE); 1152 | } 1153 | assert_eq!(#ident::new_saturating(#repr::MAX), #ident::MAX_VALUE); 1154 | } 1155 | }); 1156 | } 1157 | 1158 | fn generate_test_arithmetic(item: &BoundedInteger, tokens: &mut TokenStream) { 1159 | let ident = &item.ident; 1160 | let repr = &item.repr; 1161 | 1162 | let mut body = TokenStream::new(); 1163 | 1164 | for &op in &['+', '-', '*', '/', '%'] { 1165 | let op = Punct::new(op, Spacing::Joint); 1166 | body.extend(quote! { 1167 | let _: #ident = #ident::MIN #op 0; 1168 | let _: #ident = &#ident::MIN #op 0; 1169 | let _: #ident = #ident::MIN #op &0; 1170 | let _: #ident = &#ident::MIN #op &0; 1171 | let _: #repr = 0 #op #ident::MIN; 1172 | let _: #repr = 0 #op &#ident::MIN; 1173 | let _: #repr = &0 #op #ident::MIN; 1174 | let _: #repr = &0 #op &#ident::MIN; 1175 | let _: #ident = #ident::MIN #op #ident::MIN; 1176 | let _: #ident = &#ident::MIN #op #ident::MIN; 1177 | let _: #ident = #ident::MIN #op &#ident::MIN; 1178 | let _: #ident = &#ident::MIN #op &#ident::MIN; 1179 | *&mut #ident::MIN #op= 0; 1180 | *&mut #ident::MIN #op= &0; 1181 | *&mut #ident::MIN #op= #ident::MIN; 1182 | *&mut #ident::MIN #op= &#ident::MIN; 1183 | *&mut 0 #op= #ident::MIN; 1184 | *&mut 0 #op= &#ident::MIN; 1185 | }); 1186 | } 1187 | 1188 | if item.repr.sign == Signed { 1189 | body.extend(quote! { 1190 | let _: #ident = #ident::MIN.abs(); 1191 | let _: Option<#ident> = #ident::MIN.checked_abs(); 1192 | 1193 | let _: #ident = -#ident::MIN; 1194 | let _: #ident = -&#ident::MIN; 1195 | let _: #ident = #ident::MIN.saturating_neg(); 1196 | }); 1197 | } 1198 | body.extend(quote! { 1199 | let _: Option<#ident> = #ident::MIN.checked_neg(); 1200 | }); 1201 | 1202 | let infallibles = [ 1203 | "pow", 1204 | "div_euclid", 1205 | "rem_euclid", 1206 | "saturating_add", 1207 | "saturating_sub", 1208 | "saturating_mul", 1209 | "saturating_pow", 1210 | ]; 1211 | let fallibles = [ 1212 | "add", 1213 | "sub", 1214 | "mul", 1215 | "div", 1216 | "div_euclid", 1217 | "rem", 1218 | "rem_euclid", 1219 | "pow", 1220 | ]; 1221 | for method in &infallibles { 1222 | let method = Ident::new(method, Span::call_site()); 1223 | body.extend(quote! { 1224 | let _: #ident = #ident::MIN.#method(0); 1225 | }); 1226 | } 1227 | for method in &fallibles { 1228 | let method = Ident::new(&format!("checked_{method}"), Span::call_site()); 1229 | body.extend(quote! { 1230 | let _: Option<#ident> = #ident::MIN.#method(0); 1231 | }); 1232 | } 1233 | 1234 | tokens.extend(quote! { 1235 | #[test] 1236 | fn arithmetic() { 1237 | // Don't run the tests, as they might panic. We just need to make sure these methods 1238 | // exist. 1239 | if false { #body } 1240 | } 1241 | }); 1242 | } 1243 | 1244 | fn enum_variant(i: &BigInt) -> Ident { 1245 | Ident::new( 1246 | &match i.sign() { 1247 | num_bigint::Sign::Minus => format!("N{}", i.magnitude()), 1248 | num_bigint::Sign::NoSign => "Z".to_owned(), 1249 | num_bigint::Sign::Plus => format!("P{i}"), 1250 | }, 1251 | Span::call_site(), 1252 | ) 1253 | } 1254 | 1255 | #[cfg(test)] 1256 | mod tests { 1257 | use super::*; 1258 | use syn::parse2; 1259 | 1260 | #[allow(clippy::needless_pass_by_value)] 1261 | fn assert_result( 1262 | f: impl FnOnce(&BoundedInteger, &mut TokenStream), 1263 | input: TokenStream, 1264 | expected: TokenStream, 1265 | ) { 1266 | let input = quote!([::path] false false false false false false false #input); 1267 | let item = match parse2::(input.clone()) { 1268 | Ok(item) => item, 1269 | Err(e) => panic!("Failed to parse '{input}': {e}"), 1270 | }; 1271 | let mut result = TokenStream::new(); 1272 | f(&item, &mut result); 1273 | assert_eq!(result.to_string(), expected.to_string()); 1274 | drop((input, expected)); 1275 | } 1276 | 1277 | #[test] 1278 | fn test_tokens() { 1279 | let derives = quote! { 1280 | #[derive( 1281 | ::core::fmt::Debug, 1282 | ::core::hash::Hash, 1283 | ::core::clone::Clone, 1284 | ::core::marker::Copy, 1285 | ::core::cmp::PartialEq, 1286 | ::core::cmp::Eq, 1287 | ::core::cmp::PartialOrd, 1288 | ::core::cmp::Ord 1289 | )] 1290 | }; 1291 | 1292 | assert_result( 1293 | generate_item, 1294 | quote! { 1295 | #[repr(isize)] 1296 | pub(crate) enum Nibble { -8..6+2 } 1297 | }, 1298 | quote! { 1299 | #derives 1300 | #[repr(isize)] 1301 | pub(crate) enum Nibble { 1302 | N8 = -8isize, N7, N6, N5, N4, N3, N2, N1, Z, P1, P2, P3, P4, P5, P6, P7 1303 | } 1304 | }, 1305 | ); 1306 | 1307 | assert_result( 1308 | generate_item, 1309 | quote! { 1310 | #[repr(u16)] 1311 | enum Nibble { 3..=7 } 1312 | }, 1313 | quote! { 1314 | #derives 1315 | #[repr(u16)] 1316 | enum Nibble { 1317 | P3 = 3u16, P4, P5, P6, P7 1318 | } 1319 | }, 1320 | ); 1321 | 1322 | assert_result( 1323 | generate_item, 1324 | quote! { 1325 | #[repr(i8)] 1326 | pub struct S { -3..2 } 1327 | }, 1328 | quote! { 1329 | #derives 1330 | #[repr(transparent)] 1331 | pub struct S(::core::primitive::i8); 1332 | }, 1333 | ); 1334 | 1335 | assert_result( 1336 | generate_item, 1337 | quote! { 1338 | #[repr(i16)] 1339 | pub struct S { -3..-1 } 1340 | }, 1341 | quote! { 1342 | #derives 1343 | #[repr(transparent)] 1344 | pub struct S(::core::num::NonZero<::core::primitive::i16>); 1345 | }, 1346 | ); 1347 | } 1348 | } 1349 | -------------------------------------------------------------------------------- /macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A macro for generating bounded integer structs and enums. 2 | //! 3 | //! This crate is unstable and must not be used directly. 4 | #![warn(clippy::pedantic, rust_2018_idioms, unused_qualifications)] 5 | #![allow(clippy::single_match_else, clippy::match_bool)] 6 | #![allow(unused)] 7 | 8 | use std::borrow::Borrow; 9 | use std::cmp; 10 | use std::convert::TryInto; 11 | use std::fmt::{self, Display, Formatter}; 12 | use std::ops::RangeInclusive; 13 | 14 | use proc_macro2::{Group, Ident, Literal, Span, TokenStream}; 15 | use quote::{quote, ToTokens, TokenStreamExt as _}; 16 | use syn::parse::{self, Parse, ParseStream}; 17 | use syn::{braced, parse_macro_input, token::Brace, Token}; 18 | use syn::{Attribute, Error, Expr, PathArguments, PathSegment, Visibility}; 19 | use syn::{BinOp, ExprBinary, ExprRange, ExprUnary, RangeLimits, UnOp}; 20 | use syn::{ExprGroup, ExprParen}; 21 | use syn::{ExprLit, Lit, LitBool}; 22 | 23 | use num_bigint::{BigInt, Sign, TryFromBigIntError}; 24 | 25 | mod generate; 26 | 27 | #[proc_macro] 28 | #[doc(hidden)] 29 | pub fn bounded_integer(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 30 | let mut item = parse_macro_input!(input as BoundedInteger); 31 | 32 | // Hide in a module to prevent access to private parts. 33 | let module_name = Ident::new( 34 | &format!("__bounded_integer_private_{}", item.ident), 35 | item.ident.span(), 36 | ); 37 | let ident = &item.ident; 38 | let original_visibility = item.vis; 39 | 40 | let import = quote!(#original_visibility use #module_name::#ident); 41 | 42 | item.vis = raise_one_level(original_visibility); 43 | let mut result = TokenStream::new(); 44 | generate::generate(&item, &mut result); 45 | 46 | quote!( 47 | #[allow(non_snake_case)] 48 | mod #module_name { 49 | #result 50 | } 51 | #import; 52 | ) 53 | .into() 54 | } 55 | 56 | #[allow(clippy::struct_excessive_bools)] 57 | struct BoundedInteger { 58 | // $crate 59 | crate_path: TokenStream, 60 | 61 | // Optional features 62 | alloc: bool, 63 | arbitrary1: bool, 64 | bytemuck1: bool, 65 | serde1: bool, 66 | std: bool, 67 | zerocopy: bool, 68 | step_trait: bool, 69 | 70 | // The item itself 71 | attrs: Vec, 72 | repr: Repr, 73 | vis: Visibility, 74 | kind: Kind, 75 | ident: Ident, 76 | brace_token: Brace, 77 | range: RangeInclusive, 78 | } 79 | 80 | impl Parse for BoundedInteger { 81 | fn parse(input: ParseStream<'_>) -> parse::Result { 82 | let crate_path = input.parse::()?.stream(); 83 | 84 | let alloc = input.parse::()?.value; 85 | let arbitrary1 = input.parse::()?.value; 86 | let bytemuck1 = input.parse::()?.value; 87 | let serde1 = input.parse::()?.value; 88 | let std = input.parse::()?.value; 89 | let zerocopy = input.parse::()?.value; 90 | let step_trait = input.parse::()?.value; 91 | 92 | let mut attrs = input.call(Attribute::parse_outer)?; 93 | 94 | let repr_pos = attrs.iter().position(|attr| attr.path().is_ident("repr")); 95 | let repr = repr_pos 96 | .map(|pos| attrs.remove(pos).parse_args::()) 97 | .transpose()?; 98 | 99 | let vis: Visibility = input.parse()?; 100 | 101 | let kind: Kind = input.parse()?; 102 | 103 | let ident: Ident = input.parse()?; 104 | 105 | let range_tokens; 106 | let brace_token = braced!(range_tokens in input); 107 | let range: ExprRange = range_tokens.parse()?; 108 | 109 | let Some((start_expr, end_expr)) = range.start.as_deref().zip(range.end.as_deref()) else { 110 | return Err(Error::new_spanned(range, "Range must be closed")); 111 | }; 112 | let start = eval_expr(start_expr)?; 113 | let end = eval_expr(end_expr)?; 114 | let end = if let RangeLimits::HalfOpen(_) = range.limits { 115 | end - 1 116 | } else { 117 | end 118 | }; 119 | if start >= end { 120 | return Err(Error::new_spanned( 121 | range, 122 | "The start of the range must be before the end", 123 | )); 124 | } 125 | 126 | let repr = match repr { 127 | Some(explicit_repr) => { 128 | if explicit_repr.sign == Unsigned && start.sign() == Sign::Minus { 129 | return Err(Error::new_spanned( 130 | start_expr, 131 | "An unsigned integer cannot hold a negative value", 132 | )); 133 | } 134 | 135 | if explicit_repr.minimum().is_some_and(|min| start < min) { 136 | return Err(Error::new_spanned( 137 | start_expr, 138 | format_args!( 139 | "Bound {start} is below the minimum value for the underlying type", 140 | ), 141 | )); 142 | } 143 | if explicit_repr.maximum().is_some_and(|max| end > max) { 144 | return Err(Error::new_spanned( 145 | end_expr, 146 | format_args!( 147 | "Bound {end} is above the maximum value for the underlying type", 148 | ), 149 | )); 150 | } 151 | 152 | explicit_repr 153 | } 154 | None => Repr::smallest_repr(&start, &end).ok_or_else(|| { 155 | Error::new_spanned(range, "Range is too wide to fit in any integer primitive") 156 | })?, 157 | }; 158 | 159 | Ok(Self { 160 | crate_path, 161 | alloc, 162 | arbitrary1, 163 | bytemuck1, 164 | serde1, 165 | std, 166 | zerocopy, 167 | step_trait, 168 | attrs, 169 | repr, 170 | vis, 171 | kind, 172 | ident, 173 | brace_token, 174 | range: start..=end, 175 | }) 176 | } 177 | } 178 | 179 | enum Kind { 180 | Struct(Token![struct]), 181 | Enum(Token![enum]), 182 | } 183 | 184 | impl Parse for Kind { 185 | fn parse(input: ParseStream<'_>) -> parse::Result { 186 | Ok(if input.peek(Token![struct]) { 187 | Self::Struct(input.parse()?) 188 | } else { 189 | Self::Enum(input.parse()?) 190 | }) 191 | } 192 | } 193 | 194 | #[derive(Clone, Copy, PartialEq, Eq)] 195 | enum ReprSign { 196 | Signed, 197 | Unsigned, 198 | } 199 | use ReprSign::{Signed, Unsigned}; 200 | 201 | struct Repr { 202 | sign: ReprSign, 203 | size: ReprSize, 204 | name: Ident, 205 | } 206 | 207 | impl Repr { 208 | fn new(sign: ReprSign, size: ReprSize) -> Self { 209 | let prefix = match sign { 210 | Signed => 'i', 211 | Unsigned => 'u', 212 | }; 213 | Self { 214 | sign, 215 | size, 216 | name: Ident::new(&format!("{prefix}{size}"), Span::call_site()), 217 | } 218 | } 219 | 220 | fn smallest_repr(min: &BigInt, max: &BigInt) -> Option { 221 | // NOTE: Never infer nonzero types, even if we can. 222 | Some(if min.sign() == Sign::Minus { 223 | Self::new( 224 | Signed, 225 | ReprSize::Fixed(cmp::max( 226 | ReprSizeFixed::from_bits((min + 1_u8).bits() + 1)?, 227 | ReprSizeFixed::from_bits(max.bits() + 1)?, 228 | )), 229 | ) 230 | } else { 231 | Self::new( 232 | Unsigned, 233 | ReprSize::Fixed(ReprSizeFixed::from_bits(max.bits())?), 234 | ) 235 | }) 236 | } 237 | 238 | fn minimum(&self) -> Option { 239 | Some(match (self.sign, self.size) { 240 | (Unsigned, ReprSize::Fixed(_)) => BigInt::from(0u8), 241 | (Signed, ReprSize::Fixed(size)) => -(BigInt::from(1u8) << (size.to_bits() - 1)), 242 | (_, ReprSize::Pointer) => return None, 243 | }) 244 | } 245 | 246 | fn maximum(&self) -> Option { 247 | Some(match (self.sign, self.size) { 248 | (Unsigned, ReprSize::Fixed(size)) => (BigInt::from(1u8) << size.to_bits()) - 1, 249 | (Signed, ReprSize::Fixed(size)) => (BigInt::from(1u8) << (size.to_bits() - 1)) - 1, 250 | (_, ReprSize::Pointer) => return None, 251 | }) 252 | } 253 | 254 | fn try_number_literal( 255 | &self, 256 | value: impl Borrow, 257 | ) -> Result> { 258 | macro_rules! match_repr { 259 | ($($sign:ident $size:ident $(($fixed:ident))? => $f:ident,)*) => { 260 | match (self.sign, self.size) { 261 | $(($sign, ReprSize::$size $((ReprSizeFixed::$fixed))?) => { 262 | Ok(Literal::$f(value.borrow().try_into()?)) 263 | })* 264 | } 265 | } 266 | } 267 | 268 | match_repr! { 269 | Unsigned Fixed(Fixed8) => u8_suffixed, 270 | Unsigned Fixed(Fixed16) => u16_suffixed, 271 | Unsigned Fixed(Fixed32) => u32_suffixed, 272 | Unsigned Fixed(Fixed64) => u64_suffixed, 273 | Unsigned Fixed(Fixed128) => u128_suffixed, 274 | Unsigned Pointer => usize_suffixed, 275 | Signed Fixed(Fixed8) => i8_suffixed, 276 | Signed Fixed(Fixed16) => i16_suffixed, 277 | Signed Fixed(Fixed32) => i32_suffixed, 278 | Signed Fixed(Fixed64) => i64_suffixed, 279 | Signed Fixed(Fixed128) => i128_suffixed, 280 | Signed Pointer => isize_suffixed, 281 | } 282 | } 283 | 284 | fn number_literal(&self, value: impl Borrow) -> Literal { 285 | self.try_number_literal(value).unwrap() 286 | } 287 | 288 | fn larger_reprs(&self) -> impl Iterator { 289 | match self.sign { 290 | Signed => Either::A(self.size.larger_reprs().map(|size| Self::new(Signed, size))), 291 | Unsigned => Either::B( 292 | self.size 293 | .larger_reprs() 294 | .map(|size| Self::new(Unsigned, size)) 295 | .chain( 296 | self.size 297 | .larger_reprs() 298 | .skip(1) 299 | .map(|size| Self::new(Signed, size)), 300 | ), 301 | ), 302 | } 303 | } 304 | 305 | fn is_usize(&self) -> bool { 306 | matches!((self.sign, self.size), (Unsigned, ReprSize::Pointer)) 307 | } 308 | } 309 | 310 | impl Parse for Repr { 311 | fn parse(input: ParseStream<'_>) -> parse::Result { 312 | let name = input.parse::()?; 313 | let span = name.span(); 314 | let s = name.to_string(); 315 | 316 | let (size, sign) = if let Some(size) = s.strip_prefix('i') { 317 | (size, Signed) 318 | } else if let Some(size) = s.strip_prefix('u') { 319 | (size, Unsigned) 320 | } else { 321 | return Err(Error::new(span, "Repr must a primitive integer type")); 322 | }; 323 | 324 | let size = match size { 325 | "8" => ReprSize::Fixed(ReprSizeFixed::Fixed8), 326 | "16" => ReprSize::Fixed(ReprSizeFixed::Fixed16), 327 | "32" => ReprSize::Fixed(ReprSizeFixed::Fixed32), 328 | "64" => ReprSize::Fixed(ReprSizeFixed::Fixed64), 329 | "128" => ReprSize::Fixed(ReprSizeFixed::Fixed128), 330 | "size" => ReprSize::Pointer, 331 | unknown => { 332 | return Err(Error::new( 333 | span, 334 | format_args!( 335 | "Unknown integer size {unknown}, must be one of 8, 16, 32, 64, 128 or size", 336 | ), 337 | )); 338 | } 339 | }; 340 | 341 | Ok(Self { sign, size, name }) 342 | } 343 | } 344 | 345 | impl ToTokens for Repr { 346 | fn to_tokens(&self, tokens: &mut TokenStream) { 347 | tokens.append(self.name.clone()); 348 | } 349 | } 350 | 351 | #[derive(Clone, Copy)] 352 | enum ReprSize { 353 | Fixed(ReprSizeFixed), 354 | 355 | /// `usize`/`isize` 356 | Pointer, 357 | } 358 | 359 | impl ReprSize { 360 | fn larger_reprs(self) -> impl Iterator { 361 | match self { 362 | Self::Fixed(fixed) => Either::A(fixed.larger_reprs().map(Self::Fixed)), 363 | Self::Pointer => Either::B(std::iter::once(Self::Pointer)), 364 | } 365 | } 366 | } 367 | 368 | impl Display for ReprSize { 369 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 370 | match self { 371 | Self::Fixed(fixed) => fixed.fmt(f), 372 | Self::Pointer => f.write_str("size"), 373 | } 374 | } 375 | } 376 | 377 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] 378 | enum ReprSizeFixed { 379 | Fixed8, 380 | Fixed16, 381 | Fixed32, 382 | Fixed64, 383 | Fixed128, 384 | } 385 | 386 | impl ReprSizeFixed { 387 | fn to_bits(self) -> u64 { 388 | match self { 389 | ReprSizeFixed::Fixed8 => 8, 390 | ReprSizeFixed::Fixed16 => 16, 391 | ReprSizeFixed::Fixed32 => 32, 392 | ReprSizeFixed::Fixed64 => 64, 393 | ReprSizeFixed::Fixed128 => 128, 394 | } 395 | } 396 | 397 | fn from_bits(bits: u64) -> Option { 398 | Some(match bits { 399 | 0..=8 => Self::Fixed8, 400 | 9..=16 => Self::Fixed16, 401 | 17..=32 => Self::Fixed32, 402 | 33..=64 => Self::Fixed64, 403 | 65..=128 => Self::Fixed128, 404 | 129..=u64::MAX => return None, 405 | }) 406 | } 407 | 408 | fn larger_reprs(self) -> impl Iterator { 409 | const REPRS: [ReprSizeFixed; 5] = [ 410 | ReprSizeFixed::Fixed8, 411 | ReprSizeFixed::Fixed16, 412 | ReprSizeFixed::Fixed32, 413 | ReprSizeFixed::Fixed64, 414 | ReprSizeFixed::Fixed128, 415 | ]; 416 | let index = match self { 417 | Self::Fixed8 => 0, 418 | Self::Fixed16 => 1, 419 | Self::Fixed32 => 2, 420 | Self::Fixed64 => 3, 421 | Self::Fixed128 => 4, 422 | }; 423 | REPRS[index..].iter().copied() 424 | } 425 | } 426 | 427 | impl Display for ReprSizeFixed { 428 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 429 | f.write_str(match self { 430 | Self::Fixed8 => "8", 431 | Self::Fixed16 => "16", 432 | Self::Fixed32 => "32", 433 | Self::Fixed64 => "64", 434 | Self::Fixed128 => "128", 435 | }) 436 | } 437 | } 438 | 439 | fn eval_expr(expr: &Expr) -> syn::Result { 440 | Ok(match expr { 441 | Expr::Lit(ExprLit { lit, .. }) => match lit { 442 | Lit::Byte(byte) => byte.value().into(), 443 | Lit::Int(int) => int.base10_parse()?, 444 | _ => { 445 | return Err(Error::new_spanned(lit, "literal must be integer")); 446 | } 447 | }, 448 | Expr::Unary(ExprUnary { op, expr, .. }) => { 449 | let expr = eval_expr(expr)?; 450 | match op { 451 | UnOp::Not(_) => !expr, 452 | UnOp::Neg(_) => -expr, 453 | _ => return Err(Error::new_spanned(op, "unary operator must be ! or -")), 454 | } 455 | } 456 | Expr::Binary(ExprBinary { 457 | left, op, right, .. 458 | }) => { 459 | let left = eval_expr(left)?; 460 | let right = eval_expr(right)?; 461 | match op { 462 | BinOp::Add(_) => left + right, 463 | BinOp::Sub(_) => left - right, 464 | BinOp::Mul(_) => left * right, 465 | BinOp::Div(_) => left 466 | .checked_div(&right) 467 | .ok_or_else(|| Error::new_spanned(op, "Attempted to divide by zero"))?, 468 | BinOp::Rem(_) => left % right, 469 | BinOp::BitXor(_) => left ^ right, 470 | BinOp::BitAnd(_) => left & right, 471 | BinOp::BitOr(_) => left | right, 472 | _ => { 473 | return Err(Error::new_spanned( 474 | op, 475 | "operator not supported in this context", 476 | )); 477 | } 478 | } 479 | } 480 | Expr::Group(ExprGroup { expr, .. }) | Expr::Paren(ExprParen { expr, .. }) => { 481 | eval_expr(expr)? 482 | } 483 | _ => return Err(Error::new_spanned(expr, "expected simple expression")), 484 | }) 485 | } 486 | 487 | /// Raise a visibility one level. 488 | /// 489 | /// ```text 490 | /// no visibility -> pub(super) 491 | /// pub(self) -> pub(super) 492 | /// pub(in self) -> pub(in super) 493 | /// pub(in self::some::path) -> pub(in super::some::path) 494 | /// pub(super) -> pub(in super::super) 495 | /// pub(in super) -> pub(in super::super) 496 | /// pub(in super::some::path) -> pub(in super::super::some::path) 497 | /// ``` 498 | fn raise_one_level(vis: Visibility) -> Visibility { 499 | match vis { 500 | Visibility::Inherited => syn::parse2(quote!(pub(super))).unwrap(), 501 | Visibility::Restricted(mut restricted) 502 | if restricted.path.segments.first().unwrap().ident == "self" => 503 | { 504 | let first = &mut restricted.path.segments.first_mut().unwrap().ident; 505 | *first = Ident::new("super", first.span()); 506 | Visibility::Restricted(restricted) 507 | } 508 | Visibility::Restricted(mut restricted) 509 | if restricted.path.segments.first().unwrap().ident == "super" => 510 | { 511 | restricted 512 | .in_token 513 | .get_or_insert_with(::default); 514 | let first = PathSegment { 515 | ident: restricted.path.segments.first().unwrap().ident.clone(), 516 | arguments: PathArguments::None, 517 | }; 518 | restricted.path.segments.insert(0, first); 519 | Visibility::Restricted(restricted) 520 | } 521 | absolute_visibility => absolute_visibility, 522 | } 523 | } 524 | 525 | #[test] 526 | fn test_raise_one_level() { 527 | #[track_caller] 528 | fn assert_output(input: TokenStream, output: TokenStream) { 529 | let tokens = raise_one_level(syn::parse2(input).unwrap()).into_token_stream(); 530 | assert_eq!(tokens.to_string(), output.to_string()); 531 | drop(output); 532 | } 533 | 534 | assert_output(TokenStream::new(), quote!(pub(super))); 535 | assert_output(quote!(pub(self)), quote!(pub(super))); 536 | assert_output(quote!(pub(in self)), quote!(pub(in super))); 537 | assert_output( 538 | quote!(pub(in self::some::path)), 539 | quote!(pub(in super::some::path)), 540 | ); 541 | assert_output(quote!(pub(super)), quote!(pub(in super::super))); 542 | assert_output(quote!(pub(in super)), quote!(pub(in super::super))); 543 | assert_output( 544 | quote!(pub(in super::some::path)), 545 | quote!(pub(in super::super::some::path)), 546 | ); 547 | 548 | assert_output(quote!(pub), quote!(pub)); 549 | assert_output(quote!(pub(crate)), quote!(pub(crate))); 550 | assert_output(quote!(pub(in crate)), quote!(pub(in crate))); 551 | assert_output( 552 | quote!(pub(in crate::some::path)), 553 | quote!(pub(in crate::some::path)), 554 | ); 555 | } 556 | 557 | enum Either { 558 | A(A), 559 | B(B), 560 | } 561 | impl, B: Iterator> Iterator for Either { 562 | type Item = T; 563 | fn next(&mut self) -> Option { 564 | match self { 565 | Self::A(a) => a.next(), 566 | Self::B(b) => b.next(), 567 | } 568 | } 569 | } 570 | -------------------------------------------------------------------------------- /src/examples.rs: -------------------------------------------------------------------------------- 1 | //! Examples of bounded integers generated by the [`bounded_integer!`] macro. 2 | //! 3 | //! This module is not present when using the crate. 4 | 5 | use crate::bounded_integer; 6 | 7 | bounded_integer! { 8 | /// A bounded struct, implemented as a protected newtype. 9 | /// 10 | /// This was generated from: 11 | /// ``` 12 | #[cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 13 | /// # use bounded_integer::bounded_integer; 14 | /// bounded_integer! { 15 | /// pub struct BoundedStruct { -8..8 } 16 | /// } 17 | /// ``` 18 | pub struct BoundedStruct { -8..8 } 19 | } 20 | 21 | bounded_integer! { 22 | /// A bounded enum. 23 | /// 24 | /// This was generated from: 25 | /// ``` 26 | #[cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 27 | /// # use bounded_integer::bounded_integer; 28 | /// bounded_integer! { 29 | /// pub enum BoundedEnum { -8..8 } 30 | /// } 31 | /// ``` 32 | pub enum BoundedEnum { -8..8 } 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | macro_rules! test_range { 40 | ($fn:ident, $bounded:ident) => { 41 | #[test] 42 | fn $fn() { 43 | assert_eq!($bounded::MIN_VALUE, -8); 44 | assert_eq!($bounded::MAX_VALUE, 7); 45 | assert_eq!($bounded::MIN.get(), -8); 46 | assert_eq!($bounded::MAX.get(), 7); 47 | 48 | assert!($bounded::in_range(0)); 49 | assert!($bounded::in_range(-8)); 50 | assert!(!$bounded::in_range(-9)); 51 | assert!($bounded::in_range(7)); 52 | assert!(!$bounded::in_range(8)); 53 | 54 | assert_eq!($bounded::default().get(), 0); 55 | } 56 | }; 57 | } 58 | 59 | macro_rules! test_saturating { 60 | ($fn:ident, $bounded:ident) => { 61 | #[test] 62 | fn $fn() { 63 | assert_eq!($bounded::new_saturating(0).get(), 0); 64 | assert_eq!($bounded::new_saturating(-8).get(), -8); 65 | assert_eq!($bounded::new_saturating(-9).get(), -8); 66 | assert_eq!($bounded::new_saturating(i8::MIN).get(), -8); 67 | assert_eq!($bounded::new_saturating(7).get(), 7); 68 | assert_eq!($bounded::new_saturating(8).get(), 7); 69 | assert_eq!($bounded::new_saturating(i8::MAX).get(), 7); 70 | } 71 | }; 72 | } 73 | 74 | macro_rules! test_arithmetic { 75 | ($fn:ident, $bounded:ident) => { 76 | #[test] 77 | fn $fn() { 78 | assert_eq!($bounded::new(7).unwrap().abs().get(), 7); 79 | assert_eq!($bounded::new(-7).unwrap().abs().get(), 7); 80 | assert_eq!($bounded::new(-2).unwrap().pow(3).get(), -8); 81 | assert_eq!($bounded::new(-5).unwrap().div_euclid(3).get(), -2); 82 | assert_eq!($bounded::new(-5).unwrap().rem_euclid(3).get(), 1); 83 | assert_eq!(($bounded::new(-5).unwrap() + 2).get(), -3); 84 | assert_eq!(($bounded::new(3).unwrap() - 7).get(), -4); 85 | assert_eq!(($bounded::new(-2).unwrap() * 3).get(), -6); 86 | assert_eq!(($bounded::new(7).unwrap() / 3).get(), 2); 87 | assert_eq!(($bounded::new(7).unwrap() % 3).get(), 1); 88 | assert_eq!(-2 + $bounded::new(-8).unwrap(), -10); 89 | } 90 | }; 91 | } 92 | 93 | macro_rules! test_iter { 94 | ($fn:ident, $bounded:ident) => { 95 | #[test] 96 | fn $fn() { 97 | fn b(&n: &i8) -> $bounded { 98 | $bounded::new(n).unwrap() 99 | } 100 | 101 | assert_eq!([3, 2, 1].iter().map(b).sum::<$bounded>().get(), 6); 102 | assert_eq!([-8, 3, 7, 5, -2].iter().map(b).sum::<$bounded>().get(), 5); 103 | assert_eq!([7, 6, 4].iter().map(b).sum::(), 17); 104 | assert_eq!([-8, 3, 7, 5, -2].iter().map(b).sum::(), 5); 105 | 106 | assert_eq!([1, 3, 2, 1].iter().map(b).product::<$bounded>().get(), 6); 107 | assert_eq!([1, 3, 2, 1, 0].iter().map(b).product::<$bounded>().get(), 0); 108 | assert_eq!([-2, -3, -1].iter().map(b).product::<$bounded>().get(), -6); 109 | assert_eq!([3, 3].iter().map(b).product::(), 9); 110 | } 111 | }; 112 | } 113 | 114 | macro_rules! test_parse { 115 | ($fn:ident, $bounded:ident) => { 116 | #[test] 117 | fn $fn() { 118 | use crate::ParseErrorKind::*; 119 | 120 | assert_eq!("0".parse::<$bounded>().unwrap().get(), 0); 121 | assert_eq!("-0".parse::<$bounded>().unwrap().get(), 0); 122 | assert_eq!("+0".parse::<$bounded>().unwrap().get(), 0); 123 | assert_eq!("3".parse::<$bounded>().unwrap().get(), 3); 124 | assert_eq!("-8".parse::<$bounded>().unwrap().get(), -8); 125 | assert_eq!("+7".parse::<$bounded>().unwrap().get(), 7); 126 | assert_eq!($bounded::from_str_radix("0110", 2).unwrap().get(), 6); 127 | assert_eq!($bounded::from_str_radix("-0111", 2).unwrap().get(), -7); 128 | assert_eq!($bounded::from_str_radix("12", 4).unwrap().get(), 6); 129 | assert_eq!($bounded::from_str_radix("+2", 36).unwrap().get(), 2); 130 | 131 | assert_eq!("".parse::<$bounded>().unwrap_err().kind(), NoDigits); 132 | assert_eq!("+".parse::<$bounded>().unwrap_err().kind(), NoDigits); 133 | assert_eq!("-".parse::<$bounded>().unwrap_err().kind(), NoDigits); 134 | assert_eq!("-9".parse::<$bounded>().unwrap_err().kind(), BelowMin); 135 | assert_eq!("8".parse::<$bounded>().unwrap_err().kind(), AboveMax); 136 | assert_eq!( 137 | $bounded::from_str_radix("11", 7).unwrap_err().kind(), 138 | AboveMax 139 | ); 140 | assert_eq!("45483".parse::<$bounded>().unwrap_err().kind(), AboveMax); 141 | assert_eq!("-01934".parse::<$bounded>().unwrap_err().kind(), BelowMin); 142 | 143 | assert_eq!("++0".parse::<$bounded>().unwrap_err().kind(), InvalidDigit); 144 | assert_eq!("--0".parse::<$bounded>().unwrap_err().kind(), InvalidDigit); 145 | assert_eq!("O".parse::<$bounded>().unwrap_err().kind(), InvalidDigit); 146 | assert_eq!("C".parse::<$bounded>().unwrap_err().kind(), InvalidDigit); 147 | assert_eq!( 148 | $bounded::from_str_radix("3", 2).unwrap_err().kind(), 149 | InvalidDigit 150 | ); 151 | } 152 | }; 153 | } 154 | 155 | test_range!(test_struct_range, BoundedStruct); 156 | test_saturating!(test_struct_saturating, BoundedStruct); 157 | test_arithmetic!(test_struct_arithmetic, BoundedStruct); 158 | test_iter!(test_struct_iter, BoundedStruct); 159 | test_parse!(test_struct_parse, BoundedStruct); 160 | 161 | test_range!(test_enum_range, BoundedEnum); 162 | test_saturating!(test_enum_saturating, BoundedEnum); 163 | test_arithmetic!(test_enum_arithmetic, BoundedEnum); 164 | test_iter!(test_enum_iter, BoundedEnum); 165 | test_parse!(test_enum_parse, BoundedEnum); 166 | 167 | #[allow(unused_imports)] 168 | mod all_below_zero { 169 | use super::bounded_integer; 170 | bounded_integer! { 171 | struct Struct { -400..=-203 } 172 | } 173 | bounded_integer! { 174 | enum Enum { -500..=-483 } 175 | } 176 | } 177 | 178 | mod correct_reprs { 179 | use super::bounded_integer; 180 | bounded_integer! { 181 | struct ByteStruct { 0..256 } 182 | } 183 | const _: u8 = ByteStruct::MIN_VALUE; 184 | bounded_integer! { 185 | enum ByteEnum { 0..256 } 186 | } 187 | const _: u8 = ByteEnum::MIN_VALUE; 188 | 189 | bounded_integer! { 190 | struct SignedByteStruct { -128..128 } 191 | } 192 | const _: i8 = SignedByteStruct::MIN_VALUE; 193 | bounded_integer! { 194 | struct SignedByteEnum { -128..128 } 195 | } 196 | const _: i8 = SignedByteEnum::MIN_VALUE; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides two types of bounded integer. 2 | //! 3 | //! # Macro-generated bounded integers 4 | //! 5 | //! The [`bounded_integer!`] macro allows you to define your own bounded integer type, given a 6 | //! specific range it inhabits. For example: 7 | //! 8 | //! ```rust 9 | #![cfg_attr(not(feature = "macro"), doc = "# #[cfg(any())] {")] 10 | #![cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 11 | //! # use bounded_integer::bounded_integer; 12 | //! bounded_integer! { 13 | //! struct MyInteger { 0..8 } 14 | //! } 15 | //! let num = MyInteger::new(5).unwrap(); 16 | //! assert_eq!(num, 5); 17 | #![cfg_attr(not(feature = "macro"), doc = "# }")] 18 | //! ``` 19 | //! 20 | //! This macro supports both `struct`s and `enum`s. See the [`examples`] module for the 21 | //! documentation of generated types. 22 | //! 23 | //! # Const generics-based bounded integers 24 | //! 25 | //! You can also create ad-hoc bounded integers via types in this library that use const generics, 26 | //! for example: 27 | //! 28 | //! ```rust 29 | #![cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 30 | #![cfg_attr(not(feature = "types"), doc = "# #[cfg(any())] {")] 31 | //! # use bounded_integer::BoundedU8; 32 | //! let num = >::new(5).unwrap(); 33 | //! assert_eq!(num, 5); 34 | #![cfg_attr(not(feature = "types"), doc = "# }")] 35 | //! ``` 36 | //! 37 | //! These integers are shorter to use as they don't require a type declaration or explicit name, 38 | //! and they interoperate better with other integers that have different ranges. However due to the 39 | //! limits of const generics, they do not implement some traits like `Default`. 40 | //! 41 | //! # `no_std` 42 | //! 43 | //! All the integers in this crate depend only on libcore and so work in `#![no_std]` environments. 44 | //! 45 | //! # Crate Features 46 | //! 47 | //! By default, no crate features are enabled. 48 | //! - `std`: Interopate with `std` — implies `alloc`. Enables the following things: 49 | //! - An implementation of [`Error`] for [`ParseError`]. 50 | //! - Support for indexing with the const-generic integers on `VecDeque`. 51 | //! - `alloc`: Interopate with `alloc`. Enables the following things: 52 | //! - Support for indexing with the const-generic integers on `Vec`. 53 | //! - `macro`: Enable the [`bounded_integer!`] macro. 54 | //! - `types`: Enable the bounded integer types that use const generics. 55 | //! - `arbitrary1`: Implement [`Arbitrary`] for the bounded integers. This is useful when using 56 | //! bounded integers as fuzzing inputs. 57 | //! - `bytemuck1`: Implement [`Contiguous`] for all bounded integers, and [`Zeroable`] for 58 | //! macro-generated bounded integers that support it. 59 | //! - `num-traits02`: Implement [`Bounded`], [`AsPrimitive`], [`FromPrimitive`], [`NumCast`], 60 | //! [`ToPrimitive`], [`CheckedAdd`], [`CheckedDiv`], [`CheckedMul`], [`CheckedNeg`], 61 | //! [`CheckedRem`], [`CheckedSub`], [`MulAdd`], [`SaturatingAdd`], [`SaturatingMul`] and 62 | //! [`SaturatingSub`] for all const-generic bounded integers. 63 | //! - `serde1`: Implement [`Serialize`] and [`Deserialize`] for the bounded integers, making sure all 64 | //! values will never be out of bounds. This has a deprecated alias `serde`. 65 | //! - `zerocopy`: Implement [`IntoBytes`] for all bounded integers, 66 | //! and [`Unaligned`] for suitable macro-generated ones. 67 | //! - `step_trait`: Implement the [`Step`] trait which allows the bounded integers to be easily used 68 | //! in ranges. This will require you to use nightly and place `#![feature(step_trait)]` in your 69 | //! crate root if you use the macro. 70 | //! 71 | //! [`bounded_integer!`]: https://docs.rs/bounded-integer/*/bounded_integer/macro.bounded_integer.html 72 | //! [`examples`]: https://docs.rs/bounded-integer/*/bounded_integer/examples/ 73 | //! [`Arbitrary`]: https://docs.rs/arbitrary/1/arbitrary/trait.Arbitrary.html 74 | //! [`Contiguous`]: https://docs.rs/bytemuck/1/bytemuck/trait.Contiguous.html 75 | //! [`Zeroable`]: https://docs.rs/bytemuck/1/bytemuck/trait.Zeroable.html 76 | //! [`Bounded`]: https://docs.rs/num-traits/0.2/num_traits/bounds/trait.Bounded.html 77 | //! [`AsPrimitive`]: https://docs.rs/num-traits/0.2/num_traits/cast/trait.AsPrimitive.html 78 | //! [`FromPrimitive`]: https://docs.rs/num-traits/0.2/num_traits/cast/trait.FromPrimitive.html 79 | //! [`NumCast`]: https://docs.rs/num-traits/0.2/num_traits/cast/trait.NumCast.html 80 | //! [`ToPrimitive`]: https://docs.rs/num-traits/0.2/num_traits/cast/trait.ToPrimitive.html 81 | //! [`CheckedAdd`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedAdd.html 82 | //! [`CheckedDiv`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedDiv.html 83 | //! [`CheckedMul`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedMul.html 84 | //! [`CheckedNeg`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedNeg.html 85 | //! [`CheckedRem`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedRem.html 86 | //! [`CheckedSub`]: https://docs.rs/num-traits/0.2/num_traits/ops/checked/trait.CheckedSub.html 87 | //! [`MulAdd`]: https://docs.rs/num-traits/0.2/num_traits/ops/mul_add/trait.MulAdd.html 88 | //! [`SaturatingAdd`]: https://docs.rs/num-traits/0.2/num_traits/ops/saturating/trait.SaturatingAdd.html 89 | //! [`SaturatingMul`]: https://docs.rs/num-traits/0.2/num_traits/ops/saturating/trait.SaturatingMul.html 90 | //! [`SaturatingSub`]: https://docs.rs/num-traits/0.2/num_traits/ops/saturating/trait.SaturatingSub.html 91 | //! [`Serialize`]: https://docs.rs/serde/1/serde/trait.Serialize.html 92 | //! [`Deserialize`]: https://docs.rs/serde/1/serde/trait.Deserialize.html 93 | //! [`IntoBytes`]: https://docs.rs/zerocopy/0.8/zerocopy/trait.IntoBytes.html 94 | //! [`Unaligned`]: https://docs.rs/zerocopy/0.8/zerocopy/trait.Unaligned.html 95 | //! [`Step`]: https://doc.rust-lang.org/nightly/core/iter/trait.Step.html 96 | //! [`Error`]: https://doc.rust-lang.org/stable/std/error/trait.Error.html 97 | //! [`ParseError`]: https://docs.rs/bounded-integer/*/bounded_integer/struct.ParseError.html 98 | #![cfg_attr(feature = "step_trait", feature(step_trait))] 99 | #![cfg_attr(doc_cfg, feature(doc_cfg))] 100 | #![allow(clippy::single_component_path_imports)] // https://github.com/rust-lang/rust-clippy/issues/7106 101 | #![no_std] 102 | 103 | #[cfg(feature = "std")] 104 | extern crate std; 105 | 106 | #[cfg(feature = "alloc")] 107 | extern crate alloc; 108 | 109 | #[cfg(feature = "types")] 110 | mod types; 111 | #[cfg(feature = "types")] 112 | pub use types::*; 113 | 114 | mod parse; 115 | pub use parse::{ParseError, ParseErrorKind}; 116 | 117 | #[doc(hidden)] 118 | #[cfg(feature = "macro")] 119 | pub mod __private { 120 | #[cfg(feature = "arbitrary1")] 121 | pub use ::arbitrary1; 122 | 123 | #[cfg(feature = "bytemuck1")] 124 | pub use ::bytemuck1; 125 | 126 | #[cfg(feature = "serde1")] 127 | pub use ::serde1; 128 | 129 | #[cfg(feature = "zerocopy")] 130 | pub use ::zerocopy; 131 | 132 | pub use bounded_integer_macro::bounded_integer as proc_macro; 133 | 134 | pub use crate::parse::{error_above_max, error_below_min, FromStrRadix}; 135 | } 136 | 137 | #[cfg(feature = "__examples")] 138 | pub mod examples; 139 | 140 | /// Generate a bounded integer type. 141 | /// 142 | /// It takes in single struct or enum, with the content being a bounded range expression, whose 143 | /// upper bound can be inclusive (`x..=y`) or exclusive (`x..y`). The attributes and visibility 144 | /// (e.g. `pub`) of the type are forwarded directly to the output type. 145 | /// 146 | /// If the type is a struct and the bounded integer's range does not include zero, 147 | /// the struct will have a niche at zero, 148 | /// allowing for `Option` to be the same size as `BoundedInteger` itself. 149 | /// 150 | /// See the [`examples`] module for examples of what this macro generates. 151 | /// 152 | /// # Examples 153 | /// 154 | /// With a struct: 155 | /// ``` 156 | #[cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 157 | /// # mod force_item_scope { 158 | /// # use bounded_integer::bounded_integer; 159 | /// bounded_integer! { 160 | /// pub struct S { -3..2 } 161 | /// } 162 | /// # } 163 | /// ``` 164 | /// The generated item should look like this (i8 is chosen as it is the smallest repr): 165 | /// ``` 166 | /// #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 167 | /// #[repr(transparent)] 168 | /// pub struct S(i8); 169 | /// ``` 170 | /// And the methods will ensure that `-3 <= S.0 < 2`. 171 | /// 172 | /// With an enum: 173 | /// ``` 174 | #[cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 175 | /// # mod force_item_scope { 176 | /// # use bounded_integer::bounded_integer; 177 | /// bounded_integer! { 178 | /// pub enum S { 5..=7 } 179 | /// } 180 | /// # } 181 | /// ``` 182 | /// The generated item should look like this (u8 is chosen as it is the smallest repr): 183 | /// ``` 184 | /// #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 185 | /// #[repr(u8)] 186 | /// pub enum S { 187 | /// P5 = 5, P6, P7 188 | /// } 189 | /// ``` 190 | /// 191 | /// # Custom repr 192 | /// 193 | /// The item can have a `repr` attribute to specify how it will be represented in memory, which can 194 | /// be a `u*` or `i*` type. In this example we override the `repr` to be a `u16`, when it would 195 | /// have normally been a `u8`. 196 | /// 197 | /// ``` 198 | #[cfg_attr(feature = "step_trait", doc = "# #![feature(step_trait)]")] 199 | /// # mod force_item_scope { 200 | /// # use bounded_integer::bounded_integer; 201 | /// bounded_integer! { 202 | /// #[repr(u16)] 203 | /// pub struct S { 2..5 } 204 | /// } 205 | /// # } 206 | /// ``` 207 | /// The generated item should look like this: 208 | /// ``` 209 | /// #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 210 | /// #[repr(transparent)] 211 | /// pub struct S(u16); 212 | /// ``` 213 | /// 214 | /// # Limitations 215 | /// 216 | /// - Both bounds of ranges must be closed and a simple const expression involving only literals and 217 | /// the following operators: 218 | /// - Negation (`-x`) 219 | /// - Addition (`x+y`), subtraction (`x-y`), multiplication (`x*y`), division (`x/y`) and 220 | /// remainder (`x%y`). 221 | /// - Bitwise not (`!x`), XOR (`x^y`), AND (`x&y`) and OR (`x|y`). 222 | #[cfg(feature = "macro")] 223 | #[cfg_attr(doc_cfg, doc(cfg(feature = "macro")))] 224 | #[macro_export] 225 | macro_rules! bounded_integer { 226 | ($($tt:tt)*) => { $crate::__bounded_integer_inner! { $($tt)* } }; 227 | } 228 | 229 | // `bounded_integer!` needs to generate different output depending on what feature flags are 230 | // enabled in this crate. We can't propagate feature flags from this crate directly to 231 | // `bounded-integer-macro` because it is an optional dependency, so we instead dynamically pass 232 | // options into the macro depending on which feature flags are enabled here. 233 | 234 | #[cfg(feature = "macro")] 235 | block! { 236 | let alloc: ident = cfg_bool!(feature = "alloc"); 237 | let arbitrary1: ident = cfg_bool!(feature = "arbitrary1"); 238 | let bytemuck1: ident = cfg_bool!(feature = "bytemuck1"); 239 | let serde1: ident = cfg_bool!(feature = "serde1"); 240 | let std: ident = cfg_bool!(feature = "std"); 241 | let zerocopy: ident = cfg_bool!(feature = "zerocopy"); 242 | let step_trait: ident = cfg_bool!(feature = "step_trait"); 243 | let d: tt = dollar!(); 244 | 245 | #[doc(hidden)] 246 | #[macro_export] 247 | macro_rules! __bounded_integer_inner2 { 248 | ($d($d tt:tt)*) => { 249 | $crate::__private::proc_macro! { 250 | [$crate] $alloc $arbitrary1 $bytemuck1 $serde1 $std $zerocopy $step_trait $d($d tt)* 251 | } 252 | }; 253 | } 254 | 255 | // Workaround for `macro_expanded_macro_exports_accessed_by_absolute_paths` 256 | #[doc(hidden)] 257 | pub use __bounded_integer_inner2 as __bounded_integer_inner; 258 | } 259 | 260 | #[cfg(feature = "macro")] 261 | macro_rules! cfg_bool { 262 | ($meta:meta) => { 263 | #[cfg($meta)] 264 | ret! { true } 265 | #[cfg(not($meta))] 266 | ret! { false } 267 | }; 268 | } 269 | #[cfg(feature = "macro")] 270 | use cfg_bool; 271 | 272 | #[cfg(feature = "macro")] 273 | macro_rules! dollar { 274 | () => { ret! { $ } }; 275 | } 276 | #[cfg(feature = "macro")] 277 | use dollar; 278 | 279 | #[cfg(feature = "macro")] 280 | macro_rules! block { 281 | { let $ident:ident: $ty:ident = $macro:ident!($($macro_args:tt)*); $($rest:tt)* } => { 282 | macro_rules! ret { 283 | ($d:tt) => { 284 | macro_rules! ret { ($d $ident: $ty) => { block! { $($rest)* } } } 285 | $macro! { $($macro_args)* } 286 | } 287 | } 288 | dollar! {} 289 | }; 290 | { $($rest:tt)* } => { $($rest)* }; 291 | } 292 | #[cfg(feature = "macro")] 293 | use block; 294 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display, Formatter}; 2 | #[cfg(feature = "std")] 3 | use std::error::Error; 4 | 5 | #[cfg_attr(not(any(feature = "types", feature = "macro")), expect(unused))] 6 | pub trait FromStrRadix: Sized { 7 | fn from_str_radix(s: &str, radix: u32) -> Result; 8 | } 9 | 10 | macro_rules! from_str_radix_impl { 11 | ($($ty:ident)*) => { $( 12 | impl FromStrRadix for $ty { 13 | fn from_str_radix(src: &str, radix: u32) -> Result { 14 | assert!( 15 | (2..=36).contains(&radix), 16 | "from_str_radix: radix must lie in the range `[2, 36]` - found {}", 17 | radix, 18 | ); 19 | 20 | let src = src.as_bytes(); 21 | 22 | let (positive, digits) = match *src { 23 | [b'+', ref digits @ ..] => (true, digits), 24 | [b'-', ref digits @ ..] => (false, digits), 25 | ref digits => (true, digits), 26 | }; 27 | 28 | if digits.is_empty() { 29 | return Err(ParseError { 30 | kind: ParseErrorKind::NoDigits, 31 | }); 32 | } 33 | 34 | let overflow_kind = if positive { 35 | ParseErrorKind::AboveMax 36 | } else { 37 | ParseErrorKind::BelowMin 38 | }; 39 | 40 | let mut result: Self = 0; 41 | 42 | for &digit in digits { 43 | let digit_value = 44 | char::from(digit) 45 | .to_digit(radix) 46 | .ok_or_else(|| ParseError { 47 | kind: ParseErrorKind::InvalidDigit, 48 | })?; 49 | 50 | result = result 51 | .checked_mul(radix as Self) 52 | .ok_or_else(|| ParseError { 53 | kind: overflow_kind, 54 | })?; 55 | 56 | result = if positive { 57 | result.checked_add(digit_value as Self) 58 | } else { 59 | result.checked_sub(digit_value as Self) 60 | } 61 | .ok_or_else(|| ParseError { 62 | kind: overflow_kind, 63 | })?; 64 | } 65 | 66 | Ok(result) 67 | } 68 | } 69 | )* } 70 | } 71 | from_str_radix_impl! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } 72 | 73 | /// An error which can be returned when parsing a bounded integer. 74 | /// 75 | /// This is the error type of all bounded integers' `from_str_radix()` functions (such as 76 | /// [`BoundedI8::from_str_radix`](crate::BoundedI8::from_str_radix)) as well as their 77 | /// [`FromStr`](std::str::FromStr) implementations. 78 | #[derive(Debug, Clone)] 79 | pub struct ParseError { 80 | kind: ParseErrorKind, 81 | } 82 | 83 | impl ParseError { 84 | /// Gives the cause of the error. 85 | #[must_use] 86 | pub fn kind(&self) -> ParseErrorKind { 87 | self.kind 88 | } 89 | } 90 | 91 | impl Display for ParseError { 92 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 93 | match self.kind() { 94 | ParseErrorKind::NoDigits => f.write_str("no digits found"), 95 | ParseErrorKind::InvalidDigit => f.write_str("invalid digit found in string"), 96 | ParseErrorKind::AboveMax => f.write_str("number too high to fit in target range"), 97 | ParseErrorKind::BelowMin => f.write_str("number too low to fit in target range"), 98 | } 99 | } 100 | } 101 | 102 | #[cfg(feature = "std")] 103 | #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))] 104 | impl Error for ParseError {} 105 | 106 | /// The cause of the failure to parse the integer. 107 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 108 | #[non_exhaustive] 109 | pub enum ParseErrorKind { 110 | /// No digits were found in the input string. 111 | /// 112 | /// This happens when the input is an empty string, or when it only contains a `+` or `-`. 113 | #[non_exhaustive] 114 | NoDigits, 115 | /// An invalid digit was found in the input. 116 | #[non_exhaustive] 117 | InvalidDigit, 118 | /// The integer is too high to fit in the bounded integer's range. 119 | #[non_exhaustive] 120 | AboveMax, 121 | /// The integer is too low to fit in the bounded integer's range. 122 | #[non_exhaustive] 123 | BelowMin, 124 | } 125 | 126 | #[cfg_attr(not(any(feature = "types", feature = "macro")), expect(unused))] 127 | pub fn error_below_min() -> ParseError { 128 | ParseError { 129 | kind: ParseErrorKind::BelowMin, 130 | } 131 | } 132 | #[cfg_attr(not(any(feature = "types", feature = "macro")), expect(unused))] 133 | pub fn error_above_max() -> ParseError { 134 | ParseError { 135 | kind: ParseErrorKind::AboveMax, 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/types/indexing.rs: -------------------------------------------------------------------------------- 1 | //! Indexing operations on [T; N], Vec and VecDeque for BoundedUsize 2 | 3 | use super::BoundedUsize; 4 | use core::ops::Index; 5 | use core::ops::IndexMut; 6 | 7 | impl Index> for [T] { 8 | type Output = T; 9 | 10 | #[inline] 11 | fn index(&self, index: BoundedUsize) -> &Self::Output { 12 | &self[index.get()] 13 | } 14 | } 15 | 16 | impl IndexMut> for [T] { 17 | #[inline] 18 | fn index_mut(&mut self, index: BoundedUsize) -> &mut Self::Output { 19 | &mut self[index.get()] 20 | } 21 | } 22 | 23 | #[cfg(feature = "alloc")] 24 | impl Index> for alloc::vec::Vec { 25 | type Output = T; 26 | 27 | #[inline] 28 | fn index(&self, index: BoundedUsize) -> &Self::Output { 29 | &self[index.get()] 30 | } 31 | } 32 | 33 | #[cfg(feature = "alloc")] 34 | impl IndexMut> 35 | for alloc::vec::Vec 36 | { 37 | #[inline] 38 | fn index_mut(&mut self, index: BoundedUsize) -> &mut Self::Output { 39 | &mut self[index.get()] 40 | } 41 | } 42 | 43 | #[cfg(feature = "alloc")] 44 | impl Index> 45 | for alloc::collections::VecDeque 46 | { 47 | type Output = T; 48 | 49 | #[inline] 50 | fn index(&self, index: BoundedUsize) -> &Self::Output { 51 | &self[index.get()] 52 | } 53 | } 54 | 55 | #[cfg(feature = "alloc")] 56 | impl IndexMut> 57 | for alloc::collections::VecDeque 58 | { 59 | #[inline] 60 | fn index_mut(&mut self, index: BoundedUsize) -> &mut Self::Output { 61 | &mut self[index.get()] 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use crate::types::BoundedUsize; 68 | 69 | #[test] 70 | fn indexing() { 71 | let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 72 | 73 | for i in 0..arr.len() { 74 | let b_u = BoundedUsize::<0, 30>::new(i).unwrap(); 75 | assert_eq!(arr[b_u], i); 76 | } 77 | } 78 | 79 | #[test] 80 | #[cfg(feature = "alloc")] 81 | fn indexing_alloc() { 82 | let vec = (0..20).collect::>(); 83 | let deq = vec 84 | .clone() 85 | .into_iter() 86 | .rev() 87 | .collect::>(); 88 | 89 | for i in 0..vec.len() { 90 | let b_u = BoundedUsize::<0, 30>::new(i).unwrap(); 91 | 92 | assert_eq!(vec[b_u], i); 93 | assert_eq!(deq[b_u], 19 - i); 94 | } 95 | } 96 | 97 | #[test] 98 | fn indexing_mut() { 99 | let mut arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 100 | 101 | for i in 0..arr.len() { 102 | let b_u = BoundedUsize::<0, 30>::new(i).unwrap(); 103 | 104 | arr[b_u] += 5; 105 | 106 | assert_eq!(arr[b_u], i + 5); 107 | } 108 | } 109 | 110 | #[test] 111 | #[cfg(feature = "alloc")] 112 | fn indexing_mut_alloc() { 113 | let mut vec = (0..20).collect::>(); 114 | let mut deq = vec 115 | .clone() 116 | .into_iter() 117 | .rev() 118 | .collect::>(); 119 | 120 | for i in 0..vec.len() { 121 | let b_u = BoundedUsize::<0, 30>::new(i).unwrap(); 122 | 123 | vec[b_u] += 5; 124 | deq[b_u] += 10; 125 | 126 | assert_eq!(vec[b_u], i + 5); 127 | assert_eq!(deq[b_u], (19 - i) + 10); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/types/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! bin_op_variations { 2 | ([$($generics:tt)*] $lhs:ty, $rhs:ty, $op:ident::$method:ident/$op_assign:ident::$method_assign:ident) => { 3 | impl<$($generics)*> $op<$rhs> for &$lhs { 4 | type Output = $lhs; 5 | #[inline] 6 | fn $method(self, rhs: $rhs) -> Self::Output { 7 | <$lhs as $op<$rhs>>::$method(*self, rhs) 8 | } 9 | } 10 | impl<$($generics)*> $op<&$rhs> for $lhs { 11 | type Output = $lhs; 12 | #[inline] 13 | fn $method(self, rhs: &$rhs) -> Self::Output { 14 | <$lhs as $op<$rhs>>::$method(self, *rhs) 15 | } 16 | } 17 | impl<$($generics)*> $op<&$rhs> for &$lhs { 18 | type Output = $lhs; 19 | #[inline] 20 | fn $method(self, rhs: &$rhs) -> Self::Output { 21 | <$lhs as $op<$rhs>>::$method(*self, *rhs) 22 | } 23 | } 24 | 25 | impl<$($generics)*> $op_assign<$rhs> for $lhs { 26 | #[inline] 27 | fn $method_assign(&mut self, rhs: $rhs) { 28 | *self = >::$method(*self, rhs); 29 | } 30 | } 31 | impl<$($generics)*> $op_assign<&$rhs> for $lhs { 32 | #[inline] 33 | fn $method_assign(&mut self, rhs: &$rhs) { 34 | *self = >::$method(*self, *rhs); 35 | } 36 | } 37 | } 38 | } 39 | 40 | macro_rules! impl_bin_op { 41 | ($op:ident::$method:ident/$op_assign:ident::$method_assign:ident, $desc:literal) => { 42 | use core::ops::{$op, $op_assign}; 43 | 44 | impl $op for Bounded { 45 | type Output = Self; 46 | #[inline] 47 | fn $method(self, rhs: Inner) -> Self::Output { 48 | Self::new(self.get().$method(rhs)) 49 | .expect(concat!("Attempted to ", $desc, " out of range")) 50 | } 51 | } 52 | bin_op_variations!( 53 | [const MIN: Inner, const MAX: Inner] 54 | Bounded, Inner, $op::$method/$op_assign::$method_assign 55 | ); 56 | 57 | impl $op> for Inner { 58 | type Output = Self; 59 | #[inline] 60 | fn $method(self, rhs: Bounded) -> Self::Output { 61 | self.$method(rhs.get()) 62 | } 63 | } 64 | bin_op_variations! { 65 | [const MIN: Inner, const MAX: Inner] 66 | Inner, Bounded, $op::$method/$op_assign::$method_assign 67 | } 68 | 69 | impl 70 | $op> for Bounded 71 | { 72 | type Output = Self; 73 | #[inline] 74 | fn $method(self, rhs: Bounded) -> Self::Output { 75 | Self::new(self.get().$method(rhs)) 76 | .expect(concat!("Attempted to ", $desc, " out of range")) 77 | } 78 | } 79 | bin_op_variations! { 80 | [const L_MIN: Inner, const L_MAX: Inner, const R_MIN: Inner, const R_MAX: Inner] 81 | Bounded, Bounded, $op::$method/$op_assign::$method_assign 82 | } 83 | }; 84 | } 85 | 86 | macro_rules! impl_shift_bin_op { 87 | (u32, $op:ident::$method:ident/$op_assign:ident::$method_assign:ident, $desc:literal) => { 88 | impl_bin_op!($op::$method/$op_assign::$method_assign, $desc); 89 | }; 90 | ($inner:ident, $op:ident::$method:ident/$op_assign:ident::$method_assign:ident, $desc:literal) => { 91 | impl_bin_op!($op::$method/$op_assign::$method_assign, $desc); 92 | 93 | // Implementation used by checked shift operations 94 | impl $op for Bounded { 95 | type Output = Self; 96 | #[inline] 97 | fn $method(self, rhs: u32) -> Self::Output { 98 | Self::new(self.get().$method(rhs)) 99 | .expect(concat!("Attempted to ", $desc, " out of range")) 100 | } 101 | } 102 | bin_op_variations!( 103 | [const MIN: Inner, const MAX: Inner] 104 | Bounded, u32, $op::$method/$op_assign::$method_assign 105 | ); 106 | }; 107 | } 108 | 109 | #[cfg(test)] 110 | macro_rules! test_arithmetic { 111 | (ops($($op:tt $op_assign:tt)*) infallibles($($infallible:ident)*) fallibles($($fallible:ident)*)) => { 112 | $( #[allow(const_item_mutation)] { 113 | let _: Bounded = Bounded::MIN $op 0; 114 | let _: Bounded = &Bounded::MIN $op 0; 115 | let _: Bounded = Bounded::MIN $op &0; 116 | let _: Bounded = &Bounded::MIN $op &0; 117 | let _: Inner = 0 $op Bounded::MIN; 118 | let _: Inner = 0 $op &Bounded::MIN; 119 | let _: Inner = &0 $op Bounded::MIN; 120 | let _: Inner = &0 $op &Bounded::MIN; 121 | let _: Bounded = Bounded::MIN $op Bounded::MIN; 122 | let _: Bounded = &Bounded::MIN $op Bounded::MIN; 123 | let _: Bounded = Bounded::MIN $op &Bounded::MIN; 124 | let _: Bounded = &Bounded::MIN $op &Bounded::MIN; 125 | *&mut Bounded::MIN $op_assign 0; 126 | *&mut Bounded::MIN $op_assign &0; 127 | *&mut Bounded::MIN $op_assign Bounded::MIN; 128 | *&mut Bounded::MIN $op_assign &Bounded::MIN; 129 | *&mut 0 $op_assign Bounded::MIN; 130 | *&mut 0 $op_assign &Bounded::MIN; 131 | } )* 132 | $(let _: Bounded = Bounded::MIN.$infallible(0);)* 133 | $(let _: Option = Bounded::MIN.$fallible(0);)* 134 | let _: Option = Bounded::MIN.checked_neg(); 135 | }; 136 | (signed $($tt:tt)*) => { 137 | test_arithmetic!($($tt)*); 138 | 139 | let _: Bounded = Bounded::MIN.abs(); 140 | let _: Option = Bounded::MIN.checked_abs(); 141 | 142 | let _: Bounded = -Bounded::MIN; 143 | let _: Bounded = -&Bounded::MIN; 144 | let _: Bounded = Bounded::MIN.saturating_neg(); 145 | }; 146 | } 147 | 148 | macro_rules! impl_fmt_traits { 149 | ($($trait:ident),*) => { $( 150 | impl fmt::$trait for Bounded { 151 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 152 | fmt::$trait::fmt(&self.get(), f) 153 | } 154 | } 155 | )* } 156 | } 157 | 158 | macro_rules! define_bounded_integers { 159 | ($( 160 | $name:ident $inner:ident $(signed $([$signed:ident])?)? -> $($into:ident)*, 161 | )*) => { $( mod $inner { 162 | use core::borrow::Borrow; 163 | use core::cmp; 164 | use core::fmt; 165 | use core::iter; 166 | use core::str::FromStr; 167 | 168 | use crate::parse::{ParseError, FromStrRadix}; 169 | 170 | type Inner = core::primitive::$inner; 171 | 172 | #[doc = "An"] 173 | #[doc = concat!("[`", stringify!($inner), "`]")] 174 | #[doc = "constrained to be in the range `MIN..=MAX`."] 175 | #[cfg_attr(doc_cfg, doc(cfg(feature = "types")))] 176 | #[repr(transparent)] 177 | #[derive(Debug, Hash, Clone, Copy, Eq, Ord)] 178 | #[cfg_attr(feature = "zerocopy", derive(zerocopy::IntoBytes))] 179 | pub struct Bounded(Inner); 180 | 181 | impl Bounded { 182 | /// The smallest value this bounded integer can contain. 183 | pub const MIN_VALUE: Inner = MIN; 184 | /// The largest value that this bounded integer can contain. 185 | pub const MAX_VALUE: Inner = MAX; 186 | 187 | /// The smallest value of the bounded integer. 188 | pub const MIN: Self = Self(MIN); 189 | /// The largest value of the bounded integer. 190 | pub const MAX: Self = Self(MAX); 191 | 192 | /// Creates a bounded integer without checking the value. 193 | /// 194 | /// # Safety 195 | /// 196 | /// The value must not be outside the valid range of values; it must not be less than 197 | /// [`MIN_VALUE`](Self::MIN_VALUE) or greater than [`MAX_VALUE`](Self::MAX_VALUE). 198 | #[must_use] 199 | pub const unsafe fn new_unchecked(n: Inner) -> Self { 200 | // Doesn't work in `const fn`: 201 | // debug_assert!(Self::in_range(n)); 202 | Self(n) 203 | } 204 | 205 | /// Creates a shared reference to a bounded integer from a shared reference to a 206 | /// primitive. 207 | /// 208 | /// # Safety 209 | /// 210 | /// The value must not be outside the valid range of values; it must not be less than 211 | /// [`MIN_VALUE`](Self::MIN_VALUE) or greater than [`MAX_VALUE`](Self::MAX_VALUE). 212 | #[must_use] 213 | pub unsafe fn new_ref_unchecked(n: &Inner) -> &Self { 214 | debug_assert!(Self::in_range(*n)); 215 | &*<*const _>::cast(n) 216 | } 217 | 218 | /// Creates a mutable reference to a bounded integer from a mutable reference to a 219 | /// primitive. 220 | /// 221 | /// # Safety 222 | /// 223 | /// The value must not be outside the valid range of values; it must not be less than 224 | /// [`MIN_VALUE`](Self::MIN_VALUE) or greater than [`MAX_VALUE`](Self::MAX_VALUE). 225 | #[must_use] 226 | pub unsafe fn new_mut_unchecked(n: &mut Inner) -> &mut Self { 227 | debug_assert!(Self::in_range(*n)); 228 | &mut *<*mut _>::cast(n) 229 | } 230 | 231 | /// Checks whether the given value is in the range of the bounded integer. 232 | #[must_use] 233 | #[inline] 234 | pub const fn in_range(n: Inner) -> bool { 235 | n >= Self::MIN_VALUE && n <= Self::MAX_VALUE 236 | } 237 | 238 | /// Creates a bounded integer if the given value is within the range 239 | /// [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 240 | #[must_use] 241 | #[inline] 242 | pub const fn new(n: Inner) -> Option { 243 | if Self::in_range(n) { 244 | Some(Self(n)) 245 | } else { 246 | None 247 | } 248 | } 249 | 250 | /// Creates a reference to a bounded integer from a reference to a primitive if the 251 | /// given value is within the range [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 252 | #[must_use] 253 | #[inline] 254 | pub fn new_ref(n: &Inner) -> Option<&Self> { 255 | Self::in_range(*n).then(|| { 256 | // SAFETY: We just asserted that the value is in range. 257 | unsafe { Self::new_ref_unchecked(n) } 258 | }) 259 | } 260 | 261 | /// Creates a mutable reference to a bounded integer from a mutable reference to a 262 | /// primitive if the given value is within the range 263 | /// [[`MIN`](Self::MIN), [`MAX`](Self::MAX)]. 264 | #[must_use] 265 | #[inline] 266 | pub fn new_mut(n: &mut Inner) -> Option<&mut Self> { 267 | Self::in_range(*n).then(move || { 268 | // SAFETY: We just asserted that the value is in range. 269 | unsafe { Self::new_mut_unchecked(n) } 270 | }) 271 | } 272 | 273 | /// Creates a bounded integer by setting the value to [`MIN`](Self::MIN) or 274 | /// [`MAX`](Self::MAX) if it is too low or too high respectively. 275 | #[must_use] 276 | #[inline] 277 | pub const fn new_saturating(n: Inner) -> Self { 278 | if n < Self::MIN_VALUE { 279 | Self::MIN 280 | } else if n > Self::MAX_VALUE { 281 | Self::MAX 282 | } else { 283 | Self(n) 284 | } 285 | } 286 | 287 | /// Converts a string slice in a given base to the bounded integer. 288 | /// 289 | /// # Panics 290 | /// 291 | /// Panics if `radix` is below 2 or above 36. 292 | pub fn from_str_radix(src: &str, radix: u32) -> Result { 293 | let value = ::from_str_radix(src, radix)?; 294 | if value < Self::MIN_VALUE { 295 | Err(crate::parse::error_below_min()) 296 | } else if value > Self::MAX_VALUE { 297 | Err(crate::parse::error_above_max()) 298 | } else { 299 | Ok(unsafe { Self::new_unchecked(value) }) 300 | } 301 | } 302 | 303 | /// Returns the value of the bounded integer as a primitive type. 304 | #[must_use] 305 | #[inline] 306 | pub const fn get(self) -> Inner { 307 | self.0 308 | } 309 | 310 | /// Returns a shared reference to the value of the bounded integer. 311 | #[must_use] 312 | #[inline] 313 | pub const fn get_ref(&self) -> &Inner { 314 | &self.0 315 | } 316 | 317 | /// Returns a mutable reference to the value of the bounded integer. 318 | /// 319 | /// # Safety 320 | /// 321 | /// This value must never be set to a value beyond the range of the bounded integer. 322 | #[must_use] 323 | #[inline] 324 | pub unsafe fn get_mut(&mut self) -> &mut Inner { 325 | &mut *<*mut _>::cast(self) 326 | } 327 | 328 | $($(if $signed)? 329 | /// Computes the absolute value of `self`, panicking if it is out of range. 330 | #[must_use] 331 | #[inline] 332 | pub fn abs(self) -> Self { 333 | Self::new(self.get().abs()).expect("Absolute value out of range") 334 | } 335 | )* 336 | 337 | /// Raises `self` to the power of `exp`, using exponentiation by squaring. Panics if it 338 | /// is out of range. 339 | #[must_use] 340 | #[inline] 341 | pub fn pow(self, exp: u32) -> Self { 342 | Self::new(self.get().pow(exp)).expect("Value raised to power out of range") 343 | } 344 | 345 | /// Calculates the quotient of Euclidean division of `self` by `rhs`. Panics if `rhs` 346 | /// is 0 or the result is out of range. 347 | #[must_use] 348 | #[inline] 349 | pub fn div_euclid(self, rhs: Inner) -> Self { 350 | Self::new(self.get().div_euclid(rhs)).expect("Attempted to divide out of range") 351 | } 352 | 353 | /// Calculates the least nonnegative remainder of `self (mod rhs)`. Panics if `rhs` is 0 354 | /// or the result is out of range. 355 | #[must_use] 356 | #[inline] 357 | pub fn rem_euclid(self, rhs: Inner) -> Self { 358 | Self::new(self.get().rem_euclid(rhs)) 359 | .expect("Attempted to divide with remainder out of range") 360 | } 361 | 362 | /// Checked integer addition. 363 | #[must_use] 364 | #[inline] 365 | pub const fn checked_add(self, rhs: Inner) -> Option { 366 | match self.get().checked_add(rhs) { 367 | Some(val) => Self::new(val), 368 | None => None, 369 | } 370 | } 371 | 372 | /// Saturating integer addition. 373 | #[must_use] 374 | #[inline] 375 | pub const fn saturating_add(self, rhs: Inner) -> Self { 376 | Self::new_saturating(self.get().saturating_add(rhs)) 377 | } 378 | 379 | /// Checked integer subtraction. 380 | #[must_use] 381 | #[inline] 382 | pub const fn checked_sub(self, rhs: Inner) -> Option { 383 | match self.get().checked_sub(rhs) { 384 | Some(val) => Self::new(val), 385 | None => None, 386 | } 387 | } 388 | 389 | /// Saturating integer subtraction. 390 | #[must_use] 391 | #[inline] 392 | pub const fn saturating_sub(self, rhs: Inner) -> Self { 393 | Self::new_saturating(self.get().saturating_sub(rhs)) 394 | } 395 | 396 | /// Checked integer multiplication. 397 | #[must_use] 398 | #[inline] 399 | pub const fn checked_mul(self, rhs: Inner) -> Option { 400 | match self.get().checked_mul(rhs) { 401 | Some(val) => Self::new(val), 402 | None => None, 403 | } 404 | } 405 | 406 | /// Saturating integer multiplication. 407 | #[must_use] 408 | #[inline] 409 | pub const fn saturating_mul(self, rhs: Inner) -> Self { 410 | Self::new_saturating(self.get().saturating_mul(rhs)) 411 | } 412 | 413 | /// Checked integer division. 414 | #[must_use] 415 | #[inline] 416 | pub const fn checked_div(self, rhs: Inner) -> Option { 417 | match self.get().checked_div(rhs) { 418 | Some(val) => Self::new(val), 419 | None => None, 420 | } 421 | } 422 | 423 | /// Checked Euclidean division. 424 | #[must_use] 425 | #[inline] 426 | pub const fn checked_div_euclid(self, rhs: Inner) -> Option { 427 | match self.get().checked_div_euclid(rhs) { 428 | Some(val) => Self::new(val), 429 | None => None, 430 | } 431 | } 432 | 433 | /// Checked integer remainder. 434 | #[must_use] 435 | #[inline] 436 | pub const fn checked_rem(self, rhs: Inner) -> Option { 437 | match self.get().checked_rem(rhs) { 438 | Some(val) => Self::new(val), 439 | None => None, 440 | } 441 | } 442 | 443 | /// Checked Euclidean remainder. 444 | #[must_use] 445 | #[inline] 446 | pub const fn checked_rem_euclid(self, rhs: Inner) -> Option { 447 | match self.get().checked_rem_euclid(rhs) { 448 | Some(val) => Self::new(val), 449 | None => None, 450 | } 451 | } 452 | 453 | /// Checked negation. 454 | #[must_use] 455 | #[inline] 456 | pub const fn checked_neg(self) -> Option { 457 | match self.get().checked_neg() { 458 | Some(val) => Self::new(val), 459 | None => None, 460 | } 461 | } 462 | 463 | $($(if $signed)? 464 | /// Saturating negation. 465 | #[must_use] 466 | #[inline] 467 | pub const fn saturating_neg(self) -> Self { 468 | Self::new_saturating(self.get().saturating_neg()) 469 | } 470 | 471 | /// Checked absolute value. 472 | #[must_use] 473 | #[inline] 474 | pub const fn checked_abs(self) -> Option { 475 | match self.get().checked_abs() { 476 | Some(val) => Self::new(val), 477 | None => None, 478 | } 479 | } 480 | 481 | /// Saturating absolute value. 482 | #[must_use] 483 | #[inline] 484 | pub const fn saturating_abs(self) -> Self { 485 | Self::new_saturating(self.get().saturating_abs()) 486 | } 487 | )* 488 | 489 | /// Checked exponentiation. 490 | #[must_use] 491 | #[inline] 492 | pub const fn checked_pow(self, rhs: u32) -> Option { 493 | match self.get().checked_pow(rhs) { 494 | Some(val) => Self::new(val), 495 | None => None, 496 | } 497 | } 498 | 499 | /// Saturating exponentiation. 500 | #[must_use] 501 | #[inline] 502 | pub const fn saturating_pow(self, rhs: u32) -> Self { 503 | Self::new_saturating(self.get().saturating_pow(rhs)) 504 | } 505 | 506 | /// Checked shift left. 507 | #[must_use] 508 | #[inline] 509 | pub const fn checked_shl(self, rhs: u32) -> Option { 510 | match self.get().checked_shl(rhs) { 511 | Some(val) => Self::new(val), 512 | None => None, 513 | } 514 | } 515 | 516 | /// Checked shift right. 517 | #[must_use] 518 | #[inline] 519 | pub const fn checked_shr(self, rhs: u32) -> Option { 520 | match self.get().checked_shr(rhs) { 521 | Some(val) => Self::new(val), 522 | None => None, 523 | } 524 | } 525 | } 526 | 527 | // === Operators === 528 | 529 | impl_bin_op!(Add::add/AddAssign::add_assign, "add"); 530 | impl_bin_op!(Sub::sub/SubAssign::sub_assign, "subtract"); 531 | impl_bin_op!(Mul::mul/MulAssign::mul_assign, "multiply"); 532 | impl_bin_op!(Div::div/DivAssign::div_assign, "divide"); 533 | impl_bin_op!(Rem::rem/RemAssign::rem_assign, "take remainder"); 534 | impl_bin_op!(BitAnd::bitand/BitAndAssign::bitand_assign, "binary and"); 535 | impl_bin_op!(BitOr::bitor/BitOrAssign::bitor_assign, "binary or"); 536 | impl_bin_op!(BitXor::bitxor/BitXorAssign::bitxor_assign, "binary xor"); 537 | impl_shift_bin_op!($inner, Shl::shl/ShlAssign::shl_assign, "shift left"); 538 | impl_shift_bin_op!($inner, Shr::shr/ShrAssign::shr_assign, "shift right"); 539 | 540 | $($(if $signed)? 541 | use core::ops::Neg; 542 | 543 | impl Neg for Bounded { 544 | type Output = Self; 545 | #[inline] 546 | fn neg(self) -> Self::Output { 547 | Self::new(-self.get()) 548 | .expect("Attempted to negate out of range") 549 | } 550 | } 551 | impl Neg for &Bounded { 552 | type Output = Bounded; 553 | #[inline] 554 | fn neg(self) -> Self::Output { 555 | -*self 556 | } 557 | } 558 | )? 559 | 560 | use core::ops::Not; 561 | 562 | impl Not for Bounded { 563 | type Output = Self; 564 | #[inline] 565 | fn not(self) -> Self::Output { 566 | Self::new(!self.get()) 567 | .expect("Attempted to negate out of range") 568 | } 569 | } 570 | impl Not for &Bounded { 571 | type Output = Bounded; 572 | #[inline] 573 | fn not(self) -> Self::Output { 574 | !*self 575 | } 576 | } 577 | 578 | // === Comparisons === 579 | 580 | impl PartialEq for Bounded { 581 | #[inline] 582 | fn eq(&self, other: &Inner) -> bool { 583 | self.get() == *other 584 | } 585 | } 586 | impl PartialEq> for Inner { 587 | #[inline] 588 | fn eq(&self, other: &Bounded) -> bool { 589 | *self == other.get() 590 | } 591 | } 592 | impl 593 | PartialEq> for Bounded 594 | { 595 | #[inline] 596 | fn eq(&self, other: &Bounded) -> bool { 597 | self.get() == other.get() 598 | } 599 | } 600 | 601 | impl PartialOrd for Bounded { 602 | #[inline] 603 | fn partial_cmp(&self, other: &Inner) -> Option { 604 | self.get().partial_cmp(other) 605 | } 606 | } 607 | impl PartialOrd> for Inner { 608 | #[inline] 609 | fn partial_cmp(&self, other: &Bounded) -> Option { 610 | self.partial_cmp(&other.get()) 611 | } 612 | } 613 | impl 614 | PartialOrd> for Bounded 615 | { 616 | #[inline] 617 | fn partial_cmp(&self, other: &Bounded) -> Option { 618 | self.get().partial_cmp(&other.get()) 619 | } 620 | } 621 | 622 | // === AsRef, Borrow === 623 | 624 | impl AsRef for Bounded { 625 | #[inline] 626 | fn as_ref(&self) -> &Inner { 627 | self.get_ref() 628 | } 629 | } 630 | impl Borrow for Bounded { 631 | #[inline] 632 | fn borrow(&self) -> &Inner { 633 | self.get_ref() 634 | } 635 | } 636 | 637 | // === Iterator traits === 638 | 639 | // Sum bounded to bounded 640 | impl iter::Sum for Bounded { 641 | fn sum>(iter: I) -> Self { 642 | iter.reduce(Add::add) 643 | .unwrap_or_else(|| Self::new(0).expect("Attempted to sum to zero")) 644 | } 645 | } 646 | impl<'a, const MIN: Inner, const MAX: Inner> iter::Sum<&'a Self> for Bounded { 647 | fn sum>(iter: I) -> Self { 648 | iter.copied().sum() 649 | } 650 | } 651 | 652 | // Sum bounded to primitive 653 | impl iter::Sum> for Inner { 654 | fn sum>>(iter: I) -> Self { 655 | iter.map(Bounded::get).sum() 656 | } 657 | } 658 | impl<'a, const MIN: Inner, const MAX: Inner> iter::Sum<&'a Bounded> for Inner { 659 | fn sum>>(iter: I) -> Self { 660 | iter.copied().sum() 661 | } 662 | } 663 | 664 | // Take product of bounded to bounded 665 | impl iter::Product for Bounded { 666 | fn product>(iter: I) -> Self { 667 | iter.reduce(Mul::mul) 668 | .unwrap_or_else(|| Self::new(1).expect("Attempted to take product to one")) 669 | } 670 | } 671 | impl<'a, const MIN: Inner, const MAX: Inner> iter::Product<&'a Self> for Bounded { 672 | fn product>(iter: I) -> Self { 673 | iter.copied().product() 674 | } 675 | } 676 | 677 | // Take product of bounded to primitive 678 | impl iter::Product> for Inner { 679 | fn product>>(iter: I) -> Self { 680 | iter.map(Bounded::get).product() 681 | } 682 | } 683 | impl<'a, const MIN: Inner, const MAX: Inner> iter::Product<&'a Bounded> for Inner { 684 | fn product>>(iter: I) -> Self { 685 | iter.copied().product() 686 | } 687 | } 688 | 689 | #[cfg(feature = "step_trait")] 690 | #[cfg_attr(doc_cfg, doc(cfg(feature = "step_trait")))] 691 | impl iter::Step for Bounded { 692 | #[inline] 693 | fn steps_between(start: &Self, end: &Self) -> (usize, Option) { 694 | iter::Step::steps_between(&start.get(), &end.get()) 695 | } 696 | #[inline] 697 | fn forward_checked(start: Self, count: usize) -> Option { 698 | iter::Step::forward_checked(start.get(), count).and_then(Self::new) 699 | } 700 | #[inline] 701 | fn backward_checked(start: Self, count: usize) -> Option { 702 | iter::Step::backward_checked(start.get(), count).and_then(Self::new) 703 | } 704 | } 705 | 706 | // === Parsing === 707 | 708 | impl FromStr for Bounded { 709 | type Err = ParseError; 710 | 711 | fn from_str(s: &str) -> Result { 712 | Self::from_str_radix(s, 10) 713 | } 714 | } 715 | 716 | // === Formatting === 717 | 718 | impl_fmt_traits!(Binary, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex); 719 | 720 | // === Arbitrary === 721 | 722 | #[cfg(feature = "arbitrary1")] 723 | use arbitrary1::{Arbitrary, Unstructured}; 724 | 725 | #[cfg(feature = "arbitrary1")] 726 | #[cfg_attr(doc_cfg, doc(cfg(feature = "arbitrary1")))] 727 | impl<'a, const MIN: Inner, const MAX: Inner> Arbitrary<'a> for Bounded { 728 | fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary1::Result { 729 | Self::new(u.arbitrary()?).ok_or(arbitrary1::Error::IncorrectFormat) 730 | } 731 | 732 | #[inline] 733 | fn size_hint(depth: usize) -> (usize, Option) { 734 | >::size_hint(depth) 735 | } 736 | } 737 | 738 | // === Bytemuck === 739 | 740 | #[cfg(feature = "bytemuck1")] 741 | #[cfg_attr(doc_cfg, doc(cfg(feature = "bytemuck1")))] 742 | unsafe impl bytemuck1::Contiguous for Bounded { 743 | type Int = Inner; 744 | const MAX_VALUE: Inner = MAX; 745 | const MIN_VALUE: Inner = MIN; 746 | } 747 | 748 | // === Num === 749 | 750 | #[cfg(feature = "num-traits02")] 751 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 752 | impl num_traits02::Bounded for Bounded { 753 | fn min_value() -> Self { 754 | Self::MIN 755 | } 756 | 757 | fn max_value() -> Self { 758 | Self::MAX 759 | } 760 | } 761 | 762 | #[cfg(feature = "num-traits02")] 763 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 764 | impl num_traits02::AsPrimitive 765 | for Bounded 766 | where 767 | Inner: num_traits02::AsPrimitive, 768 | T: 'static + Copy, 769 | { 770 | fn as_(self) -> T { 771 | self.get().as_() 772 | } 773 | } 774 | 775 | #[cfg(feature = "num-traits02")] 776 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 777 | impl num_traits02::FromPrimitive for Bounded 778 | where 779 | Inner: num_traits02::FromPrimitive, 780 | { 781 | fn from_i64(n: i64) -> Option { 782 | Inner::from_i64(n) 783 | .map(Self::new) 784 | .flatten() 785 | } 786 | 787 | fn from_u64(n: u64) -> Option { 788 | Inner::from_u64(n) 789 | .map(Self::new) 790 | .flatten() 791 | } 792 | 793 | fn from_isize(n: isize) -> Option { 794 | Inner::from_isize(n) 795 | .map(Self::new) 796 | .flatten() 797 | } 798 | 799 | fn from_i8(n: i8) -> Option { 800 | Inner::from_i8(n) 801 | .map(Self::new) 802 | .flatten() 803 | } 804 | 805 | fn from_i16(n: i16) -> Option { 806 | Inner::from_i16(n) 807 | .map(Self::new) 808 | .flatten() 809 | } 810 | 811 | fn from_i32(n: i32) -> Option { 812 | Inner::from_i32(n) 813 | .map(Self::new) 814 | .flatten() 815 | } 816 | 817 | fn from_i128(n: i128) -> Option { 818 | Inner::from_i128(n) 819 | .map(Self::new) 820 | .flatten() 821 | } 822 | 823 | fn from_usize(n: usize) -> Option { 824 | Inner::from_usize(n) 825 | .map(Self::new) 826 | .flatten() 827 | } 828 | 829 | fn from_u8(n: u8) -> Option { 830 | Inner::from_u8(n) 831 | .map(Self::new) 832 | .flatten() 833 | } 834 | 835 | fn from_u16(n: u16) -> Option { 836 | Inner::from_u16(n) 837 | .map(Self::new) 838 | .flatten() 839 | } 840 | 841 | fn from_u32(n: u32) -> Option { 842 | Inner::from_u32(n) 843 | .map(Self::new) 844 | .flatten() 845 | } 846 | 847 | fn from_u128(n: u128) -> Option { 848 | Inner::from_u128(n) 849 | .map(Self::new) 850 | .flatten() 851 | } 852 | 853 | fn from_f32(n: f32) -> Option { 854 | Inner::from_f32(n) 855 | .map(Self::new) 856 | .flatten() 857 | } 858 | 859 | fn from_f64(n: f64) -> Option { 860 | Inner::from_f64(n) 861 | .map(Self::new) 862 | .flatten() 863 | } 864 | } 865 | 866 | #[cfg(feature = "num-traits02")] 867 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 868 | impl num_traits02::NumCast for Bounded 869 | where 870 | Inner: num_traits02::NumCast, 871 | { 872 | fn from(n: T) -> Option { 873 | ::from(n).map(Self::new).flatten() 874 | } 875 | } 876 | 877 | #[cfg(feature = "num-traits02")] 878 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 879 | impl num_traits02::ToPrimitive for Bounded 880 | where 881 | Inner: num_traits02::ToPrimitive, 882 | { 883 | fn to_i64(&self) -> Option { 884 | self.get().to_i64() 885 | } 886 | 887 | fn to_u64(&self) -> Option { 888 | self.get().to_u64() 889 | } 890 | 891 | fn to_isize(&self) -> Option { 892 | self.get().to_isize() 893 | } 894 | 895 | fn to_i8(&self) -> Option { 896 | self.get().to_i8() 897 | } 898 | 899 | fn to_i16(&self) -> Option { 900 | self.get().to_i16() 901 | } 902 | 903 | fn to_i32(&self) -> Option { 904 | self.get().to_i32() 905 | } 906 | 907 | fn to_i128(&self) -> Option { 908 | self.get().to_i128() 909 | } 910 | 911 | fn to_usize(&self) -> Option { 912 | self.get().to_usize() 913 | } 914 | 915 | fn to_u8(&self) -> Option { 916 | self.get().to_u8() 917 | } 918 | 919 | fn to_u16(&self) -> Option { 920 | self.get().to_u16() 921 | } 922 | 923 | fn to_u32(&self) -> Option { 924 | self.get().to_u32() 925 | } 926 | 927 | fn to_u128(&self) -> Option { 928 | self.get().to_u128() 929 | } 930 | 931 | fn to_f32(&self) -> Option { 932 | self.get().to_f32() 933 | } 934 | 935 | fn to_f64(&self) -> Option { 936 | self.get().to_f64() 937 | } 938 | } 939 | 940 | #[cfg(feature = "num-traits02")] 941 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 942 | impl num_traits02::CheckedAdd for Bounded { 943 | fn checked_add(&self, v: &Self) -> Option { 944 | Self::checked_add(*self, v.get()) 945 | } 946 | } 947 | 948 | #[cfg(feature = "num-traits02")] 949 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 950 | impl num_traits02::CheckedDiv for Bounded { 951 | fn checked_div(&self, v: &Self) -> Option { 952 | Self::checked_div(*self, v.get()) 953 | } 954 | } 955 | 956 | #[cfg(feature = "num-traits02")] 957 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 958 | impl num_traits02::CheckedMul for Bounded { 959 | fn checked_mul(&self, v: &Self) -> Option { 960 | Self::checked_mul(*self, v.get()) 961 | } 962 | } 963 | 964 | #[cfg(feature = "num-traits02")] 965 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 966 | impl num_traits02::CheckedNeg for Bounded { 967 | fn checked_neg(&self) -> Option { 968 | Self::checked_neg(*self) 969 | } 970 | } 971 | 972 | #[cfg(feature = "num-traits02")] 973 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 974 | impl num_traits02::CheckedRem for Bounded { 975 | fn checked_rem(&self, v: &Self) -> Option { 976 | Self::checked_rem(*self, v.get()) 977 | } 978 | } 979 | 980 | #[cfg(feature = "num-traits02")] 981 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 982 | impl num_traits02::CheckedShl for Bounded { 983 | fn checked_shl(&self, v: u32) -> Option { 984 | Self::checked_shl(*self, v) 985 | } 986 | } 987 | 988 | #[cfg(feature = "num-traits02")] 989 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 990 | impl num_traits02::CheckedShr for Bounded { 991 | fn checked_shr(&self, v: u32) -> Option { 992 | Self::checked_shr(*self, v) 993 | } 994 | } 995 | 996 | #[cfg(feature = "num-traits02")] 997 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 998 | impl num_traits02::CheckedSub for Bounded { 999 | fn checked_sub(&self, v: &Self) -> Option { 1000 | Self::checked_sub(*self, v.get()) 1001 | } 1002 | } 1003 | 1004 | #[cfg(feature = "num-traits02")] 1005 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 1006 | impl num_traits02::MulAdd 1007 | for Bounded 1008 | where 1009 | Inner: num_traits02::MulAdd, 1010 | { 1011 | type Output = Inner; 1012 | 1013 | fn mul_add(self, a: A, b: B) -> Self::Output { 1014 | self.get().mul_add(a, b) 1015 | } 1016 | } 1017 | 1018 | #[cfg(feature = "num-traits02")] 1019 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 1020 | impl num_traits02::SaturatingAdd for Bounded { 1021 | fn saturating_add(&self, v: &Self) -> Self { 1022 | Self::saturating_add(*self, v.get()) 1023 | } 1024 | } 1025 | 1026 | #[cfg(feature = "num-traits02")] 1027 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 1028 | impl num_traits02::SaturatingMul for Bounded { 1029 | fn saturating_mul(&self, v: &Self) -> Self { 1030 | Self::saturating_mul(*self, v.get()) 1031 | } 1032 | } 1033 | 1034 | #[cfg(feature = "num-traits02")] 1035 | #[cfg_attr(doc_cfg, doc(cfg(feature = "num-traits02")))] 1036 | impl num_traits02::SaturatingSub for Bounded { 1037 | fn saturating_sub(&self, v: &Self) -> Self { 1038 | Self::saturating_sub(*self, v.get()) 1039 | } 1040 | } 1041 | 1042 | // === Serde === 1043 | 1044 | #[cfg(feature = "serde1")] 1045 | use serde1::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; 1046 | 1047 | #[cfg(feature = "serde1")] 1048 | #[cfg_attr(doc_cfg, doc(cfg(feature = "serde1")))] 1049 | impl Serialize for Bounded { 1050 | fn serialize(&self, serializer: S) -> Result { 1051 | self.get().serialize(serializer) 1052 | } 1053 | } 1054 | 1055 | #[cfg(feature = "serde1")] 1056 | #[cfg_attr(doc_cfg, doc(cfg(feature = "serde1")))] 1057 | impl<'de, const MIN: Inner, const MAX: Inner> Deserialize<'de> for Bounded { 1058 | fn deserialize>(deserializer: D) -> Result { 1059 | Self::new(Inner::deserialize(deserializer)?) 1060 | .ok_or_else(|| { 1061 | D::Error::custom(format_args!( 1062 | "integer out of range, expected it to be between {} and {}", 1063 | Self::MIN_VALUE, 1064 | Self::MAX_VALUE, 1065 | )) 1066 | }) 1067 | } 1068 | } 1069 | 1070 | // === Conversions === 1071 | 1072 | $(impl From> for $into { 1073 | fn from(bounded: Bounded) -> Self { 1074 | Self::from(bounded.get()) 1075 | } 1076 | })* 1077 | 1078 | // === Tests === 1079 | 1080 | #[cfg(test)] 1081 | mod tests { 1082 | use super::Inner; 1083 | 1084 | #[cfg(feature = "std")] 1085 | use std::format; 1086 | 1087 | #[test] 1088 | fn range() { 1089 | type Bounded = super::Bounded<3, 10>; 1090 | assert_eq!(Bounded::MIN_VALUE, 3); 1091 | assert_eq!(Bounded::MAX_VALUE, 10); 1092 | assert_eq!(Bounded::MIN.get(), Bounded::MIN_VALUE); 1093 | assert_eq!(Bounded::MAX.get(), Bounded::MAX_VALUE); 1094 | 1095 | assert!(Bounded::in_range(3)); 1096 | assert!(!Bounded::in_range(2)); 1097 | assert!(Bounded::in_range(10)); 1098 | assert!(!Bounded::in_range(11)); 1099 | } 1100 | 1101 | #[test] 1102 | fn saturating() { 1103 | type Bounded = super::Bounded<3, 10>; 1104 | assert_eq!(Bounded::new_saturating(Inner::MIN), Bounded::MIN); 1105 | assert_eq!(Bounded::new_saturating(Inner::MAX), Bounded::MAX); 1106 | assert_eq!(Bounded::new_saturating(11).get(), 10); 1107 | assert_eq!(Bounded::new_saturating(10).get(), 10); 1108 | assert_eq!(Bounded::new_saturating(3).get(), 3); 1109 | assert_eq!(Bounded::new_saturating(2).get(), 3); 1110 | } 1111 | 1112 | #[test] 1113 | fn arithmetic() { 1114 | if false { 1115 | type Bounded = super::Bounded<0, 15>; 1116 | test_arithmetic! { 1117 | $($(if $signed)? signed)? 1118 | ops(+ += - -= * *= / /= % %=) 1119 | infallibles( 1120 | pow 1121 | div_euclid 1122 | rem_euclid 1123 | saturating_add 1124 | saturating_sub 1125 | saturating_mul 1126 | saturating_pow 1127 | ) 1128 | fallibles( 1129 | checked_add 1130 | checked_sub 1131 | checked_mul 1132 | checked_div 1133 | checked_div_euclid 1134 | checked_rem 1135 | checked_rem_euclid 1136 | checked_pow 1137 | checked_shl 1138 | checked_shr 1139 | ) 1140 | } 1141 | } 1142 | } 1143 | 1144 | #[test] 1145 | fn iter() { 1146 | type Bounded = super::Bounded<{ 0 $($(if $signed)? - 8)? }, 8>; 1147 | 1148 | fn b(&n: &Inner) -> Bounded { 1149 | Bounded::new(n).unwrap() 1150 | } 1151 | 1152 | assert_eq!([3, 2, 1].iter().map(b).sum::().get(), 6); 1153 | $($(if $signed)? assert_eq!([-8, 3, 7, 5, -2].iter().map(b).sum::().get(), 5);)? 1154 | assert_eq!([7, 6, 4].iter().map(b).sum::(), 17); 1155 | $($(if $signed)? assert_eq!([-8, 3, 7, 5, -2].iter().map(b).sum::(), 5);)? 1156 | 1157 | assert_eq!([1, 3, 2, 1].iter().map(b).product::().get(), 6); 1158 | assert_eq!([1, 3, 2, 1, 0].iter().map(b).product::().get(), 0); 1159 | $($(if $signed)? assert_eq!([-2, -3, -1].iter().map(b).product::().get(), -6);)? 1160 | assert_eq!([3, 3].iter().map(b).product::(), 9); 1161 | } 1162 | 1163 | #[test] 1164 | fn parse() { 1165 | use crate::ParseErrorKind::*; 1166 | 1167 | type Bounded = super::Bounded<3, 11>; 1168 | 1169 | assert_eq!("3".parse::().unwrap().get(), 3); 1170 | assert_eq!("10".parse::().unwrap().get(), 10); 1171 | assert_eq!("+11".parse::().unwrap().get(), 11); 1172 | assert_eq!(Bounded::from_str_radix("1010", 2).unwrap().get(), 10); 1173 | assert_eq!(Bounded::from_str_radix("B", 0xC).unwrap().get(), 11); 1174 | assert_eq!(Bounded::from_str_radix("11", 7).unwrap().get(), 8); 1175 | assert_eq!(Bounded::from_str_radix("7", 36).unwrap().get(), 7); 1176 | 1177 | assert_eq!("".parse::().unwrap_err().kind(), NoDigits); 1178 | assert_eq!("+".parse::().unwrap_err().kind(), NoDigits); 1179 | assert_eq!("-".parse::().unwrap_err().kind(), NoDigits); 1180 | assert_eq!("2".parse::().unwrap_err().kind(), BelowMin); 1181 | assert_eq!("12".parse::().unwrap_err().kind(), AboveMax); 1182 | assert_eq!("-5".parse::().unwrap_err().kind(), BelowMin); 1183 | #[cfg(feature = "std")] 1184 | assert_eq!( 1185 | format!("{}00", Inner::MAX).parse::().unwrap_err().kind(), 1186 | AboveMax 1187 | ); 1188 | #[cfg(feature = "std")] 1189 | assert_eq!( 1190 | format!("{}00", Inner::MIN).parse::().unwrap_err().kind(), 1191 | BelowMin 1192 | ); 1193 | 1194 | assert_eq!("++0".parse::().unwrap_err().kind(), InvalidDigit); 1195 | assert_eq!("--0".parse::().unwrap_err().kind(), InvalidDigit); 1196 | assert_eq!("O".parse::().unwrap_err().kind(), InvalidDigit); 1197 | assert_eq!("C".parse::().unwrap_err().kind(), InvalidDigit); 1198 | assert_eq!(Bounded::from_str_radix("3", 2).unwrap_err().kind(), InvalidDigit); 1199 | } 1200 | 1201 | #[test] 1202 | #[cfg(feature = "num-traits02")] 1203 | fn num() { 1204 | use num_traits02::{ 1205 | Bounded, AsPrimitive, FromPrimitive, NumCast, ToPrimitive, CheckedAdd, 1206 | CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, CheckedShl, CheckedShr 1207 | }; 1208 | 1209 | type B = super::Bounded<2, 8>; 1210 | type BNeg = super::Bounded<{0 $($(if $signed)? - 4)?}, 8>; 1211 | 1212 | fn b(n: Inner) -> B { 1213 | B::new(n).unwrap() 1214 | } 1215 | 1216 | fn bneg(n: Inner) -> BNeg { 1217 | BNeg::new(n).unwrap() 1218 | } 1219 | 1220 | assert_eq!(B::min_value(), 2); 1221 | assert_eq!(B::max_value(), 8); 1222 | 1223 | assert_eq!(BNeg::min_value(), 0 $($(if $signed)? - 4)?); 1224 | assert_eq!(BNeg::max_value(), 8); 1225 | 1226 | assert_eq!(>::as_(b(4)), 4u8); 1227 | assert_eq!(>::as_(b(4)), 4u16); 1228 | assert_eq!(>::as_(b(4)), 4u32); 1229 | assert_eq!(>::as_(b(4)), 4u64); 1230 | assert_eq!(>::as_(b(4)), 4u128); 1231 | assert_eq!(>::as_(b(4)), 4usize); 1232 | assert_eq!(>::as_(b(4)), 4i8); 1233 | assert_eq!(>::as_(b(4)), 4i16); 1234 | assert_eq!(>::as_(b(4)), 4i32); 1235 | assert_eq!(>::as_(b(4)), 4i64); 1236 | assert_eq!(>::as_(b(4)), 4i128); 1237 | assert_eq!(>::as_(b(4)), 4isize); 1238 | assert_eq!(>::as_(b(4)), 4f32); 1239 | assert_eq!(>::as_(b(4)), 4f64); 1240 | 1241 | assert_eq!(B::from_u8(4u8), Some(b(4))); 1242 | assert_eq!(B::from_u16(4u16), Some(b(4))); 1243 | assert_eq!(B::from_u32(4u32), Some(b(4))); 1244 | assert_eq!(B::from_u64(4u64), Some(b(4))); 1245 | assert_eq!(B::from_u128(4u128), Some(b(4))); 1246 | assert_eq!(B::from_usize(4usize), Some(b(4))); 1247 | assert_eq!(B::from_i8(4i8), Some(b(4))); 1248 | assert_eq!(B::from_i16(4i16), Some(b(4))); 1249 | assert_eq!(B::from_i32(4i32), Some(b(4))); 1250 | assert_eq!(B::from_i64(4i64), Some(b(4))); 1251 | assert_eq!(B::from_i128(4i128), Some(b(4))); 1252 | assert_eq!(B::from_isize(4isize), Some(b(4))); 1253 | assert_eq!(B::from_f32(4f32), Some(b(4))); 1254 | assert_eq!(B::from_f64(4f64), Some(b(4))); 1255 | 1256 | assert_eq!(B::from_u8(16u8), None); 1257 | assert_eq!(B::from_u16(16u16), None); 1258 | assert_eq!(B::from_u32(16u32), None); 1259 | assert_eq!(B::from_u64(16u64), None); 1260 | assert_eq!(B::from_u128(16u128), None); 1261 | assert_eq!(B::from_usize(16usize), None); 1262 | assert_eq!(B::from_i8(16i8), None); 1263 | assert_eq!(B::from_i16(16i16), None); 1264 | assert_eq!(B::from_i32(16i32), None); 1265 | assert_eq!(B::from_i64(16i64), None); 1266 | assert_eq!(B::from_i128(16i128), None); 1267 | assert_eq!(B::from_isize(16isize), None); 1268 | assert_eq!(B::from_f32(16f32), None); 1269 | assert_eq!(B::from_f64(16f64), None); 1270 | 1271 | assert_eq!(::from(4u8), Some(b(4))); 1272 | assert_eq!(::from(4u16), Some(b(4))); 1273 | assert_eq!(::from(4u32), Some(b(4))); 1274 | assert_eq!(::from(4u64), Some(b(4))); 1275 | assert_eq!(::from(4u128), Some(b(4))); 1276 | assert_eq!(::from(4usize), Some(b(4))); 1277 | assert_eq!(::from(4i8), Some(b(4))); 1278 | assert_eq!(::from(4i16), Some(b(4))); 1279 | assert_eq!(::from(4i32), Some(b(4))); 1280 | assert_eq!(::from(4i64), Some(b(4))); 1281 | assert_eq!(::from(4i128), Some(b(4))); 1282 | assert_eq!(::from(4isize), Some(b(4))); 1283 | assert_eq!(::from(4f32), Some(b(4))); 1284 | assert_eq!(::from(4f64), Some(b(4))); 1285 | 1286 | assert_eq!(::from(16u8), None); 1287 | assert_eq!(::from(16u16), None); 1288 | assert_eq!(::from(16u32), None); 1289 | assert_eq!(::from(16u64), None); 1290 | assert_eq!(::from(16u128), None); 1291 | assert_eq!(::from(16usize), None); 1292 | assert_eq!(::from(16i8), None); 1293 | assert_eq!(::from(16i16), None); 1294 | assert_eq!(::from(16i32), None); 1295 | assert_eq!(::from(16i64), None); 1296 | assert_eq!(::from(16i128), None); 1297 | assert_eq!(::from(16isize), None); 1298 | assert_eq!(::from(16f32), None); 1299 | assert_eq!(::from(16f64), None); 1300 | 1301 | assert_eq!(b(4).to_u8(), Some(4u8)); 1302 | assert_eq!(b(4).to_u16(), Some(4u16)); 1303 | assert_eq!(b(4).to_u32(), Some(4u32)); 1304 | assert_eq!(b(4).to_u64(), Some(4u64)); 1305 | assert_eq!(b(4).to_u128(), Some(4u128)); 1306 | assert_eq!(b(4).to_usize(), Some(4usize)); 1307 | assert_eq!(b(4).to_i8(), Some(4i8)); 1308 | assert_eq!(b(4).to_i16(), Some(4i16)); 1309 | assert_eq!(b(4).to_i32(), Some(4i32)); 1310 | assert_eq!(b(4).to_i64(), Some(4i64)); 1311 | assert_eq!(b(4).to_i128(), Some(4i128)); 1312 | assert_eq!(b(4).to_isize(), Some(4isize)); 1313 | assert_eq!(b(4).to_f32(), Some(4f32)); 1314 | assert_eq!(b(4).to_f64(), Some(4f64)); 1315 | 1316 | assert_eq!(::checked_add(&b(4), &b(4)), Some(b(8))); 1317 | assert_eq!(::checked_add(&b(4), &b(8)), None); 1318 | 1319 | assert_eq!(::checked_div(&b(8), &b(2)), Some(b(4))); 1320 | assert_eq!(::checked_div(&b(4), &b(4)), None); 1321 | 1322 | assert_eq!(::checked_mul(&b(2), &b(2)), Some(b(4))); 1323 | assert_eq!(::checked_mul(&b(2), &b(8)), None); 1324 | 1325 | $($(if $signed)? { 1326 | assert_eq!(::checked_neg(&bneg(2)), Some(bneg(-2))); 1327 | })? 1328 | 1329 | assert_eq!(::checked_neg(&bneg(8)), None); 1330 | 1331 | assert_eq!(::checked_rem(&b(8), &b(6)), Some(b(2))); 1332 | assert_eq!(::checked_rem(&b(8), &b(7)), None); 1333 | 1334 | assert_eq!(::checked_sub(&b(4), &b(2)), Some(b(2))); 1335 | assert_eq!(::checked_sub(&b(4), &b(4)), None); 1336 | 1337 | assert_eq!(::checked_shl(&b(4), 1u32), Some(b(8))); 1338 | assert_eq!(::checked_shl(&b(4), 2u32), None); 1339 | 1340 | assert_eq!(::checked_shr(&b(4), 1u32), Some(b(2))); 1341 | assert_eq!(::checked_shr(&b(4), 2u32), None); 1342 | } 1343 | } 1344 | } pub use self::$inner::Bounded as $name; )* } 1345 | } 1346 | 1347 | define_bounded_integers! { 1348 | BoundedU8 u8 -> u8 u16 u32 u64 u128 usize i16 i32 i64 i128 isize, 1349 | BoundedU16 u16 -> u16 u32 u64 u128 usize i32 i64 i128, 1350 | BoundedU32 u32 -> u32 u64 u128 i64 i128, 1351 | BoundedU64 u64 -> u64 u128 i128, 1352 | BoundedU128 u128 -> u128, 1353 | BoundedUsize usize -> usize, 1354 | BoundedI8 i8 signed -> i8 i16 i32 i64 i128 isize, 1355 | BoundedI16 i16 signed -> i16 i32 i64 i128 isize, 1356 | BoundedI32 i32 signed -> i32 i64 i128, 1357 | BoundedI64 i64 signed -> i64 i128, 1358 | BoundedI128 i128 signed -> i128, 1359 | BoundedIsize isize signed -> isize, 1360 | } 1361 | 1362 | mod indexing; 1363 | -------------------------------------------------------------------------------- /tests/hygiene.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | #![no_std] 3 | #![cfg_attr(feature = "step_trait", feature(step_trait))] 4 | #![cfg(feature = "macro")] 5 | #![forbid(clippy::pedantic)] 6 | 7 | #[allow(dead_code, non_camel_case_types)] 8 | mod primitives { 9 | struct u8 {} 10 | struct u16 {} 11 | struct u32 {} 12 | struct u64 {} 13 | struct u128 {} 14 | struct usize {} 15 | struct i8 {} 16 | struct i16 {} 17 | struct i32 {} 18 | struct i64 {} 19 | struct i128 {} 20 | struct isize {} 21 | } 22 | 23 | ::bounded_integer::bounded_integer! { 24 | #[repr(isize)] 25 | pub struct StructSigned { -3..2 } 26 | } 27 | ::bounded_integer::bounded_integer! { 28 | #[repr(u16)] 29 | pub struct StructUnsigned { 36..65535 } 30 | } 31 | ::bounded_integer::bounded_integer! { 32 | #[repr(i64)] 33 | pub enum EnumSigned { -4..6 } 34 | } 35 | ::bounded_integer::bounded_integer! { 36 | #[repr(u8)] 37 | pub enum EnumUnsigned { 253..255 } 38 | } 39 | --------------------------------------------------------------------------------