erased_cells/
lib.rs

1//! Encoding and manipulation of runtime-dynamic cell values.
2//!
3//! # Synopsis
4//!
5//! This crate enables the manipulation of heterogeneous values and buffers of Rust primitive numeric types.
6//! It is useful in cases where the numeric encoding is either not known at compile-time, or when
7//! multiple encodings are in use yet need to be treated in a homogenous way. The types are
8//! normalized using discriminated unions (`enums`).
9//!
10//! There are three core enums:
11//!
12//! * [`CellType`]: An enumeration of each supported primitive type.
13//! * [`CellValue`]: A scalar primitive value stored as a [`CellType`] associated variant.
14//! * [`CellBuffer`]: A `Vec<_>` of primitive values stored as a [`CellType`] associated variant.
15//!
16//! When the `masked` feature is enabled (the default) three additional constructs are available:
17//!
18//! * [`Mask`]: A bit array used to store the associated validity of a [`CellValue`] within a [`MaskedCellBuffer`].
19//! * [`MaskedCellBuffer`]: The combination of  [`CellBuffer`] and [`Mask`].
20//! * [`NoData`]: Specification of a sentinel value for invalid data, used in converting
21//! between "`Vec<T: CellEncoding>`" and [`MaskedCellBuffer`].
22//!
23//! # Examples
24//!
25//! Usage examples:
26//!
27//! * [`CellBuffer` example](crate::CellBuffer#example)
28//! * [`MaskedCellBuffer` example](crate::MaskedCellBuffer#example)
29//!
30//! # Feature Flags
31//!
32//! The following feature flags are available.
33//!
34//! | Name     | Description                          | Default |
35//! |:--------:|--------------------------------------|:-------:|
36//! | `masked` | Enable the `MaskedCellBuffer` API    | `true`  |
37//! | `serde`  | Derive `serde` traits for core types | `true`  |
38//!
39
40mod buffer;
41mod ctype;
42mod encoding;
43pub mod error;
44#[cfg(feature = "masked")]
45mod masked;
46mod value;
47
48pub use buffer::*;
49pub use ctype::*;
50pub use encoding::*;
51#[cfg(feature = "masked")]
52pub use masked::*;
53use std::fmt::{Debug, Formatter};
54pub use value::ops::*;
55pub use value::*;
56
57/// A [callback style](https://danielkeep.github.io/tlborm/book/pat-callbacks.html)
58/// macro used to construct various implementations covering all [`CellType`]s.
59///
60/// It calls the passed identifier as a macro with two parameters:
61/// * the cell type id (e.g. `UInt8`),
62/// * the cell type primitive (e.g. `u8`).
63///
64/// # Example
65/// ```rust
66/// use erased_cells::{with_ct, CellType};
67/// fn primitive_name(ct: CellType) -> &'static str {
68///     macro_rules! primitive_name {
69///        ($(($id:ident, $p:ident)),*) => {
70///             match ct {
71///                 $(CellType::$id => stringify!($p),)*
72///             }
73///        };
74///     }
75///     with_ct!(primitive_name)
76/// }
77///
78/// assert_eq!(primitive_name(CellType::Float32), "f32");
79/// ```
80#[macro_export]
81macro_rules! with_ct {
82    ($callback:ident) => {
83        $callback! {
84            (UInt8, u8),
85            (UInt16, u16),
86            (UInt32, u32),
87            (UInt64, u64),
88            (Int8, i8),
89            (Int16, i16),
90            (Int32, i32),
91            (Int64, i64),
92            (Float32, f32),
93            (Float64, f64)
94        }
95    };
96}
97
98/// Operations common to buffers of [`CellValue`]s.
99pub trait BufferOps {
100    /// Construct a [`CellBuffer`] from a `Vec<T>`.
101    fn from_vec<T: CellEncoding>(data: Vec<T>) -> Self;
102
103    /// Construct a [`CellBuffer`] of given `len` length and `ct` `CellType`
104    ///
105    /// All cells will be filled with the `CellType`'s corresponding default value.
106    fn with_defaults(len: usize, ct: CellType) -> Self;
107
108    /// Create a buffer of size `len` with all values `value`.
109    fn fill(len: usize, value: CellValue) -> Self;
110
111    /// Fill a buffer of size `len` with values from a closure.
112    ///
113    /// First parameter of the closure is the current index.  
114    fn fill_via<T, F>(len: usize, f: F) -> Self
115    where
116        T: CellEncoding,
117        F: Fn(usize) -> T;
118
119    /// Get the length of the buffer.
120    fn len(&self) -> usize;
121
122    /// Determine if the buffer has zero values in it.
123    fn is_empty(&self) -> bool {
124        self.len() == 0
125    }
126
127    /// Get the cell type of the encoded value.
128    fn cell_type(&self) -> CellType;
129
130    /// Get the [`CellValue`] at index `idx`.
131    ///
132    /// # Panics
133    /// Will panic if `index` >= `self.len()`.
134    fn get(&self, index: usize) -> CellValue;
135
136    /// Store `value` at position `idx`.
137    ///
138    /// Returns `Err(NarrowingError)` if `value.cell_type() != self.cell_type()`
139    /// and overflow could occur.
140    ///
141    /// # Panics
142    /// Will panic if `index` >= `self.len()`.
143    fn put(&mut self, index: usize, value: CellValue) -> error::Result<()>;
144
145    /// Create a new [`CellBuffer`] whereby all [`CellValue`]s are converted to `cell_type`.
146    ///
147    /// Returns `Ok(CellBuffer)` if conversion is possible, and `Err(Error)` if
148    /// contained values cannot fit in `cell_type` without clamping.
149    fn convert(&self, cell_type: CellType) -> error::Result<Self>
150    where
151        Self: Sized;
152
153    /// Compute the minimum and maximum values the buffer.
154    fn min_max(&self) -> (CellValue, CellValue);
155
156    /// Convert `self` into a `Vec<T>`.
157    fn to_vec<T: CellEncoding>(self) -> error::Result<Vec<T>>;
158}
159
160/// Newtype wrapper for debug rendering utility.
161pub(crate) struct Elided<'a, T>(&'a [T]);
162
163impl<T: Debug> Debug for Elided<'_, T> {
164    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
165        static MAX_LEN: usize = 10;
166        fn render<T: Debug>(values: &[T], f: &mut Formatter<'_>) -> std::fmt::Result {
167            match values.len() {
168                len if len > MAX_LEN => {
169                    render(&values[..5], f)?;
170                    f.write_str(", ... ")?;
171                    render(&values[len - 5..], f)?;
172                }
173                1 => {
174                    f.write_fmt(format_args!("{:?}", values[0]))?;
175                }
176                len => {
177                    for i in 0..(len - 1) {
178                        render(&values[i..=i], f)?;
179                        f.write_str(", ")?;
180                    }
181                    render(std::slice::from_ref(&values[len - 1]), f)?;
182                }
183            }
184            Ok(())
185        }
186
187        render(self.0, f)
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use crate::Elided;
194
195    #[test]
196    fn elided() {
197        let s = format!("{:?}", Elided(&[1; 3]));
198        assert_eq!(s, "1, 1, 1");
199        let s = format!("{:?}", Elided(&[0; 30]));
200        assert_eq!(s, "0, 0, 0, 0, 0, ... 0, 0, 0, 0, 0");
201    }
202}