haystack_core/data/
grid.rs1use super::dict::HDict;
4use std::fmt;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, PartialEq)]
11pub struct HCol {
12 pub name: String,
13 pub meta: HDict,
14}
15
16impl HCol {
17 pub fn new(name: impl Into<String>) -> Self {
19 Self {
20 name: name.into(),
21 meta: HDict::new(),
22 }
23 }
24
25 pub fn with_meta(name: impl Into<String>, meta: HDict) -> Self {
27 Self {
28 name: name.into(),
29 meta,
30 }
31 }
32}
33
34#[derive(Debug, Clone, Default, PartialEq)]
41pub struct HGrid {
42 pub meta: HDict,
43 pub cols: Vec<HCol>,
44 pub rows: Vec<HDict>,
45}
46
47impl HGrid {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn from_parts(meta: HDict, cols: Vec<HCol>, rows: Vec<HDict>) -> Self {
58 Self { meta, cols, rows }
59 }
60
61 pub fn from_parts_arc(meta: HDict, cols: Vec<HCol>, rows: Vec<Arc<HDict>>) -> Self {
67 let owned_rows: Vec<HDict> = rows
68 .into_iter()
69 .map(|arc| Arc::try_unwrap(arc).unwrap_or_else(|a| (*a).clone()))
70 .collect();
71 Self {
72 meta,
73 cols,
74 rows: owned_rows,
75 }
76 }
77
78 pub fn col(&self, name: &str) -> Option<&HCol> {
80 self.cols.iter().find(|c| c.name == name)
81 }
82
83 pub fn is_empty(&self) -> bool {
85 self.rows.is_empty()
86 }
87
88 pub fn is_err(&self) -> bool {
92 self.meta.has("err")
93 }
94
95 pub fn len(&self) -> usize {
97 self.rows.len()
98 }
99
100 pub fn row(&self, index: usize) -> Option<&HDict> {
102 self.rows.get(index)
103 }
104
105 pub fn iter(&self) -> impl Iterator<Item = &HDict> {
107 self.rows.iter()
108 }
109
110 pub fn num_cols(&self) -> usize {
112 self.cols.len()
113 }
114
115 pub fn col_names(&self) -> impl Iterator<Item = &str> {
117 self.cols.iter().map(|c| c.name.as_str())
118 }
119}
120
121impl fmt::Display for HGrid {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "HGrid(cols: [")?;
124 for (i, col) in self.cols.iter().enumerate() {
125 if i > 0 {
126 write!(f, ", ")?;
127 }
128 write!(f, "{}", col.name)?;
129 }
130 write!(f, "], rows: {})", self.rows.len())
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::kinds::{Kind, Number};
138
139 fn sample_grid() -> HGrid {
140 let cols = vec![HCol::new("id"), HCol::new("dis"), HCol::new("area")];
141
142 let mut row1 = HDict::new();
143 row1.set("id", Kind::Ref(crate::kinds::HRef::from_val("site-1")));
144 row1.set("dis", Kind::Str("Site One".into()));
145 row1.set(
146 "area",
147 Kind::Number(Number::new(4500.0, Some("ft\u{00B2}".into()))),
148 );
149
150 let mut row2 = HDict::new();
151 row2.set("id", Kind::Ref(crate::kinds::HRef::from_val("site-2")));
152 row2.set("dis", Kind::Str("Site Two".into()));
153 row2.set(
154 "area",
155 Kind::Number(Number::new(3200.0, Some("ft\u{00B2}".into()))),
156 );
157
158 HGrid::from_parts(HDict::new(), cols, vec![row1, row2])
159 }
160
161 #[test]
162 fn empty_grid() {
163 let g = HGrid::new();
164 assert!(g.is_empty());
165 assert_eq!(g.len(), 0);
166 assert_eq!(g.num_cols(), 0);
167 assert!(!g.is_err());
168 assert_eq!(g.row(0), None);
169 }
170
171 #[test]
172 fn grid_with_data() {
173 let g = sample_grid();
174 assert!(!g.is_empty());
175 assert_eq!(g.len(), 2);
176 assert_eq!(g.num_cols(), 3);
177 }
178
179 #[test]
180 fn col_lookup() {
181 let g = sample_grid();
182
183 let id_col = g.col("id").unwrap();
184 assert_eq!(id_col.name, "id");
185
186 let dis_col = g.col("dis").unwrap();
187 assert_eq!(dis_col.name, "dis");
188
189 assert!(g.col("nonexistent").is_none());
190 }
191
192 #[test]
193 fn col_names() {
194 let g = sample_grid();
195 let names: Vec<&str> = g.col_names().collect();
196 assert_eq!(names, vec!["id", "dis", "area"]);
197 }
198
199 #[test]
200 fn row_access() {
201 let g = sample_grid();
202
203 let r0 = g.row(0).unwrap();
204 assert_eq!(r0.get("dis"), Some(&Kind::Str("Site One".into())));
205
206 let r1 = g.row(1).unwrap();
207 assert_eq!(r1.get("dis"), Some(&Kind::Str("Site Two".into())));
208
209 assert!(g.row(2).is_none());
210 }
211
212 #[test]
213 fn iteration() {
214 let g = sample_grid();
215 let rows: Vec<&HDict> = g.iter().collect();
216 assert_eq!(rows.len(), 2);
217 }
218
219 #[test]
220 fn is_err_false_for_normal_grid() {
221 let g = sample_grid();
222 assert!(!g.is_err());
223 }
224
225 #[test]
226 fn is_err_true_with_err_marker() {
227 let mut meta = HDict::new();
228 meta.set("err", Kind::Marker);
229 meta.set("dis", Kind::Str("some error message".into()));
230
231 let g = HGrid::from_parts(meta, vec![], vec![]);
232 assert!(g.is_err());
233 assert!(g.is_empty());
234 }
235
236 #[test]
237 fn col_with_meta() {
238 let mut meta = HDict::new();
239 meta.set("unit", Kind::Str("kW".into()));
240
241 let col = HCol::with_meta("power", meta);
242 assert_eq!(col.name, "power");
243 assert!(col.meta.has("unit"));
244 }
245
246 #[test]
247 fn display() {
248 let g = sample_grid();
249 let s = g.to_string();
250 assert!(s.contains("id"));
251 assert!(s.contains("dis"));
252 assert!(s.contains("area"));
253 assert!(s.contains("rows: 2"));
254 }
255
256 #[test]
257 fn equality() {
258 let a = sample_grid();
259 let b = sample_grid();
260 assert_eq!(a, b);
261 }
262
263 #[test]
264 fn default_is_empty() {
265 let g = HGrid::default();
266 assert!(g.is_empty());
267 assert_eq!(g.num_cols(), 0);
268 }
269
270 #[test]
271 fn from_parts() {
272 let cols = vec![HCol::new("name")];
273 let mut row = HDict::new();
274 row.set("name", Kind::Str("test".into()));
275
276 let g = HGrid::from_parts(HDict::new(), cols, vec![row]);
277 assert_eq!(g.len(), 1);
278 assert_eq!(g.num_cols(), 1);
279 }
280}