Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T06:47:41.153Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

table_row.rs

213 lines · 7.2 KB · rust
1//! A newtype for a table row that enforces a fixed column count at runtime.
2//!
3//! This type ensures that all rows in a table have the same width, preventing accidental creation or mutation of rows with inconsistent lengths.
4//! It is especially useful for CSV or tabular data where rectangular invariants must be maintained, but the number of columns is only known at runtime.
5//! By using `TableRow`, we gain stronger guarantees and safer APIs compared to a bare `Vec<T>`, without requiring const generics.
6
7use std::{
8    any::type_name,
9    ops::{
10        Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
11    },
12};
13
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct TableRow<T>(Vec<T>);
16
17impl<T> TableRow<T> {
18    pub fn from_element(element: T, length: usize) -> Self
19    where
20        T: Clone,
21    {
22        Self::from_vec(vec![element; length], length)
23    }
24
25    /// Constructs a `TableRow` from a `Vec<T>`, panicking if the length does not match `expected_length`.
26    ///
27    /// Use this when you want to ensure at construction time that the row has the correct number of columns.
28    /// This enforces the rectangular invariant for table data, preventing accidental creation of malformed rows.
29    ///
30    /// # Panics
31    /// Panics if `data.len() != expected_length`.
32    pub fn from_vec(data: Vec<T>, expected_length: usize) -> Self {
33        Self::try_from_vec(data, expected_length).unwrap_or_else(|e| {
34            let name = type_name::<Vec<T>>();
35            panic!("Expected {name} to be created successfully: {e}");
36        })
37    }
38
39    /// Attempts to construct a `TableRow` from a `Vec<T>`, returning an error if the length does not match `expected_len`.
40    ///
41    /// This is a fallible alternative to `from_vec`, allowing you to handle inconsistent row lengths gracefully.
42    /// Returns `Ok(TableRow)` if the length matches, or an `Err` with a descriptive message otherwise.
43    pub fn try_from_vec(data: Vec<T>, expected_len: usize) -> Result<Self, String> {
44        if data.len() != expected_len {
45            Err(format!(
46                "Row length {} does not match expected {}",
47                data.len(),
48                expected_len
49            ))
50        } else {
51            Ok(Self(data))
52        }
53    }
54
55    /// Returns reference to element by column index.
56    ///
57    /// # Panics
58    /// Panics if `col` is out of bounds (i.e., `col >= self.cols()`).
59    pub fn expect_get(&self, col: impl Into<usize>) -> &T {
60        let col = col.into();
61        self.0.get(col).unwrap_or_else(|| {
62            panic!(
63                "Expected table row of `{}` to have {col:?}",
64                type_name::<T>()
65            )
66        })
67    }
68
69    pub fn get(&self, col: impl Into<usize>) -> Option<&T> {
70        self.0.get(col.into())
71    }
72
73    pub fn as_slice(&self) -> &[T] {
74        &self.0
75    }
76
77    pub fn as_mut_slice(&mut self) -> &mut [T] {
78        &mut self.0
79    }
80
81    pub fn into_vec(self) -> Vec<T> {
82        self.0
83    }
84
85    /// Like [`map`], but borrows the row and clones each element before mapping.
86    ///
87    /// This is useful when you want to map over a borrowed row without consuming it,
88    /// but your mapping function requires ownership of each element.
89    ///
90    /// # Difference
91    /// - `map_cloned` takes `&self`, clones each element, and applies `f(T) -> U`.
92    /// - [`map`] takes `self` by value and applies `f(T) -> U` directly, consuming the row.
93    /// - [`map_ref`] takes `&self` and applies `f(&T) -> U` to references of each element.
94    pub fn map_cloned<F, U>(&self, f: F) -> TableRow<U>
95    where
96        F: FnMut(T) -> U,
97        T: Clone,
98    {
99        self.clone().map(f)
100    }
101
102    /// Consumes the row and transforms all elements within it in a length-safe way.
103    ///
104    /// # Difference
105    /// - `map` takes ownership of the row (`self`) and applies `f(T) -> U` to each element.
106    /// - Use this when you want to transform and consume the row in one step.
107    /// - See also [`map_cloned`] (for mapping over a borrowed row with cloning) and [`map_ref`] (for mapping over references).
108    pub fn map<F, U>(self, f: F) -> TableRow<U>
109    where
110        F: FnMut(T) -> U,
111    {
112        TableRow(self.0.into_iter().map(f).collect())
113    }
114
115    /// Borrows the row and transforms all elements by reference in a length-safe way.
116    ///
117    /// # Difference
118    /// - `map_ref` takes `&self` and applies `f(&T) -> U` to each element by reference.
119    /// - Use this when you want to map over a borrowed row without cloning or consuming it.
120    /// - See also [`map`] (for consuming the row) and [`map_cloned`] (for mapping with cloning).
121    pub fn map_ref<F, U>(&self, f: F) -> TableRow<U>
122    where
123        F: FnMut(&T) -> U,
124    {
125        TableRow(self.0.iter().map(f).collect())
126    }
127
128    /// Number of columns (alias to `len()` with more semantic meaning)
129    pub fn cols(&self) -> usize {
130        self.0.len()
131    }
132}
133
134///// Convenience traits /////
135pub trait IntoTableRow<T> {
136    fn into_table_row(self, expected_length: usize) -> TableRow<T>;
137}
138impl<T> IntoTableRow<T> for Vec<T> {
139    fn into_table_row(self, expected_length: usize) -> TableRow<T> {
140        TableRow::from_vec(self, expected_length)
141    }
142}
143
144// Index implementations for convenient access
145impl<T> Index<usize> for TableRow<T> {
146    type Output = T;
147
148    fn index(&self, index: usize) -> &Self::Output {
149        &self.0[index]
150    }
151}
152
153impl<T> IndexMut<usize> for TableRow<T> {
154    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
155        &mut self.0[index]
156    }
157}
158
159// Range indexing implementations for slice operations
160impl<T> Index<Range<usize>> for TableRow<T> {
161    type Output = [T];
162
163    fn index(&self, index: Range<usize>) -> &Self::Output {
164        <Vec<T> as Index<Range<usize>>>::index(&self.0, index)
165    }
166}
167
168impl<T> Index<RangeFrom<usize>> for TableRow<T> {
169    type Output = [T];
170
171    fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
172        <Vec<T> as Index<RangeFrom<usize>>>::index(&self.0, index)
173    }
174}
175
176impl<T> Index<RangeTo<usize>> for TableRow<T> {
177    type Output = [T];
178
179    fn index(&self, index: RangeTo<usize>) -> &Self::Output {
180        <Vec<T> as Index<RangeTo<usize>>>::index(&self.0, index)
181    }
182}
183
184impl<T> Index<RangeToInclusive<usize>> for TableRow<T> {
185    type Output = [T];
186
187    fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
188        <Vec<T> as Index<RangeToInclusive<usize>>>::index(&self.0, index)
189    }
190}
191
192impl<T> Index<RangeFull> for TableRow<T> {
193    type Output = [T];
194
195    fn index(&self, index: RangeFull) -> &Self::Output {
196        <Vec<T> as Index<RangeFull>>::index(&self.0, index)
197    }
198}
199
200impl<T> Index<RangeInclusive<usize>> for TableRow<T> {
201    type Output = [T];
202
203    fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
204        <Vec<T> as Index<RangeInclusive<usize>>>::index(&self.0, index)
205    }
206}
207
208impl<T> IndexMut<RangeInclusive<usize>> for TableRow<T> {
209    fn index_mut(&mut self, index: RangeInclusive<usize>) -> &mut Self::Output {
210        <Vec<T> as IndexMut<RangeInclusive<usize>>>::index_mut(&mut self.0, index)
211    }
212}
213
Served at tenant.openagents/omega Member data and write actions are omitted.