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;