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