linearize/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(clippy::missing_safety_doc)]
3
4//! A crate for enumerable types.
5//!
6//! This crate provides the [Linearize] trait which defines a bijection between a type
7//! and an interval of the natural numbers.
8//!
9//! Given such a bijection, many useful things become possible. For example, this crate
10//! defines the types [StaticMap] and [StaticCopyMap] which provide high-performance,
11//! non-allocating mappings from linearizable types to arbitrary values.
12//!
13//! ```rust
14//! # use linearize::StaticMap;
15//! # use linearize_derive::Linearize;
16//! #
17//! #[derive(Linearize)]
18//! enum ColorFormat {
19//! R,
20//! Rgb {
21//! alpha: bool,
22//! },
23//! }
24//!
25//! let mut channels = StaticMap::default();
26//! channels[ColorFormat::R] = 1;
27//! channels[ColorFormat::Rgb { alpha: false }] = 3;
28//! channels[ColorFormat::Rgb { alpha: true }] = 4;
29//!
30//! assert_eq!(channels[ColorFormat::Rgb { alpha: false }], 3);
31//! ```
32//!
33//! These maps can be constructed conveniently with the [static_map] macro:
34//!
35//! ```rust
36//! # use linearize::{static_map, StaticMap};
37//! # use linearize_derive::Linearize;
38//! #
39//! # #[derive(Linearize)]
40//! # enum ColorFormat {
41//! # R,
42//! # Rgb {
43//! # alpha: bool,
44//! # },
45//! # }
46//! #
47//! let channels = static_map! {
48//! ColorFormat::R => 1,
49//! ColorFormat::Rgb { alpha } => 3 + alpha as u32,
50//! };
51//!
52//! assert_eq!(channels[ColorFormat::Rgb { alpha: false }], 3);
53//! ```
54//!
55//! # Features
56//!
57//! The following features are enabled by default:
58//!
59//! - `std`
60//!
61//! This crate provides the following features:
62//!
63//! - `alloc`: Adds a dependency on the `alloc` crate. This implements additional traits
64//! for the map types.
65//! - `std`: Adds a dependency on the `std` crate.
66//! - `derive`: Provides the [Linearize](linearize_derive::Linearize) derive macro.
67//! - `serde-1`: Implements `Serialize` and `Deserialize` from serde 1.x for the map types.
68//! - `arbitrary-1`: Implements `Arbitrary` from arbitrary 1.x for the map types.
69//! - `bytemuck-1`: Implements `NoUninit`, `Zeroable`, and `AnyBitPattern` from bytemuck 1.x for the map types.
70//! - `rand-0_8`: Implements various distributions from rand 0.8.x for the map types.
71//! - `rand-0_9`: Implements various distributions from rand 0.9.x for the map types.
72
73#[cfg(feature = "alloc")]
74extern crate alloc;
75
76mod copy_map;
77mod foreign;
78mod impls;
79mod linearized;
80mod r#macro;
81mod map;
82mod storage;
83mod variants;
84
85use crate::{
86 sealed::Sealed,
87 storage::{CopyStorage, Storage},
88 variants::Variants,
89};
90#[cfg(feature = "serde-1")]
91pub use foreign::serde_1;
92#[cfg(feature = "derive")]
93pub use linearize_derive::Linearize;
94#[doc(hidden)]
95pub use r#macro::Builder;
96pub use {copy_map::StaticCopyMap, linearized::Linearized, map::StaticMap};
97
98/// Types whose values can be enumerated.
99///
100/// Types that implement this trait define a bijection between themselves and an interval
101/// of the natural numbers.
102///
103/// # Safety
104///
105/// - [`Self::Storage<T>`] must be `[T; Self::LENGTH]`.
106/// - [`Self::CopyStorage<T>`] must be `[T; Self::LENGTH]`.
107/// - [`Self::linearize`] must be a bijection to `[0, Self::LENGTH)`.
108/// - [`Self::from_linear_unchecked`] must be its inverse.
109///
110/// Note that the bijection implies that a roundtrip through
111/// `linearize | from_linear_unchecked` must return a value that is, for all intents and
112/// purposes, indistinguishable from the original value. The details of this depend on
113/// `Self`.
114pub unsafe trait Linearize {
115 /// `[T; Self::LENGTH]`
116 ///
117 /// This type exists due to a limitation of the rust type system. In a future version
118 /// of this crate, all uses of it will be replaced by `[T; Self::LENGTH]`.
119 type Storage<T>: Storage<Self, T>;
120
121 /// `[T; Self::LENGTH]`
122 ///
123 /// This type exists due to a limitation of the rust type system. In a future version
124 /// of this crate, all uses of it will be replaced by `[T; Self::LENGTH]`.
125 type CopyStorage<T>: CopyStorage<Self, T>
126 where
127 T: Copy;
128
129 /// The cardinality of this type.
130 const LENGTH: usize;
131
132 /// Maps this value to the natural numbers.
133 ///
134 /// This function is a bijection to the interval `[0, Self::LENGTH)`.
135 fn linearize(&self) -> usize;
136
137 /// The inverse of the `linearize` function.
138 ///
139 /// # Safety
140 ///
141 /// `linear` must be less than [`Self::LENGTH`].
142 unsafe fn from_linear_unchecked(linear: usize) -> Self
143 where
144 Self: Sized;
145}
146
147/// Extension trait for types implementing [Linearize].
148pub trait LinearizeExt: Linearize + Sealed {
149 /// A safe version of [Linearize::from_linear_unchecked].
150 ///
151 /// This function returns `None` if `linear >= Self::LENGTH`.
152 fn from_linear(linear: usize) -> Option<Self>
153 where
154 Self: Sized;
155
156 /// Returns an iterator over all values of this type.
157 fn variants() -> Variants<Self>
158 where
159 Self: Sized;
160
161 /// Linearizes this value and stores the value in a [Linearized] object.
162 ///
163 /// See the documentation of [Linearized] for why this might be useful.
164 fn linearized(&self) -> Linearized<Self>;
165}
166
167impl<T> LinearizeExt for T
168where
169 T: Linearize + ?Sized,
170{
171 fn from_linear(linear: usize) -> Option<Self>
172 where
173 Self: Sized,
174 {
175 (linear < Self::LENGTH).then(|| unsafe {
176 // SAFETY: This closure is only called if linear < Self::LENGTH.
177 Self::from_linear_unchecked(linear)
178 })
179 }
180
181 fn variants() -> Variants<Self>
182 where
183 Self: Sized,
184 {
185 Variants::new()
186 }
187
188 fn linearized(&self) -> Linearized<Self> {
189 Linearized::new(self)
190 }
191}
192
193impl<T> Sealed for T where T: Linearize + ?Sized {}
194
195mod sealed {
196 pub trait Sealed {}
197}
198
199pub mod iter {
200 //! All iterators exposed by this crate.
201 //!
202 //! This module exists only to keep the top-level namespace clean.
203 pub use crate::{
204 map::iters::{IntoIter, Iter, IterMut},
205 variants::Variants,
206 };
207}