Skip to repository content213 lines · 7.2 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:38:16.394Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
table_row.rs
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