bounded_integer/
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 = <BoundedU8<0, 7>>::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")]
104extern crate std;
105
106#[cfg(feature = "alloc")]
107extern crate alloc;
108
109#[cfg(feature = "types")]
110mod types;
111#[cfg(feature = "types")]
112pub use types::*;
113
114mod parse;
115pub use parse::{ParseError, ParseErrorKind};
116
117#[doc(hidden)]
118#[cfg(feature = "macro")]
119pub 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")]
138pub 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<BoundedInteger>` 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]
225macro_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")]
235block! {
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")]
261macro_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")]
270use cfg_bool;
271
272#[cfg(feature = "macro")]
273macro_rules! dollar {
274    () => { ret! { $ } };
275}
276#[cfg(feature = "macro")]
277use dollar;
278
279#[cfg(feature = "macro")]
280macro_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")]
293use block;