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}