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 {
55 Self { meta, cols, rows }
56 }
57
58 pub fn from_parts_arc(meta: HDict, cols: Vec<HCol>, rows: Vec<Arc<HDict>>) -> Self {
64 let owned_rows: Vec<HDict> = rows
65 .into_iter()
66 .map(|arc| Arc::try_unwrap(arc).unwrap_or_else(|a| (*a).clone()))
67 .collect();
68 Self {
69 meta,
70 cols,
71 rows: owned_rows,
72 }
73 }
74
75 pub fn col(&self, name: &str) -> Option<&HCol> {
77 self.cols.iter().find(|c| c.name == name)
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.rows.is_empty()
83 }
84
85 pub fn is_err(&self) -> bool {
89 self.meta.has("err")
90 }
91
92 pub fn len(&self) -> usize {
94 self.rows.len()
95 }
96
97 pub fn row(&self, index: usize) -> Option<&HDict> {
99 self.rows.get(index)
100 }
101
102 pub fn iter(&self) -> impl Iterator<Item = &HDict> {
104 self.rows.iter()
105 }
106
107 pub fn num_cols(&self) -> usize {
109 self.cols.len()
110 }
111
112 pub fn col_names(&self) -> impl Iterator<Item = &str> {
114 self.cols.iter().map(|c| c.name.as_str())
115 }
116}
117
118impl fmt::Display for HGrid {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "HGrid(cols: [")?;
121 for (i, col) in self.cols.iter().enumerate() {
122 if i > 0 {
123 write!(f, ", ")?;
124 }
125 write!(f, "{}", col.name)?;
126 }
127 write!(f, "], rows: {})", self.rows.len())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::kinds::{Kind, Number};
135
136 fn sample_grid() -> HGrid {
137 let cols = vec![HCol::new("id"), HCol::new("dis"), HCol::new("area")];
138
139 let mut row1 = HDict::new();
140 row1.set("id", Kind::Ref(crate::kinds::HRef::from_val("site-1")));
141 row1.set("dis", Kind::Str("Site One".into()));
142 row1.set(
143 "area",
144 Kind::Number(Number::new(4500.0, Some("ft\u{00B2}".into()))),
145 );
146
147 let mut row2 = HDict::new();
148 row2.set("id", Kind::Ref(crate::kinds::HRef::from_val("site-2")));
149 row2.set("dis", Kind::Str("Site Two".into()));
150 row2.set(
151 "area",
152 Kind::Number(Number::new(3200.0, Some("ft\u{00B2}".into()))),
153 );
154
155 HGrid::from_parts(HDict::new(), cols, vec![row1, row2])
156 }
157
158 #[test]
159 fn empty_grid() {
160 let g = HGrid::new();
161 assert!(g.is_empty());
162 assert_eq!(g.len(), 0);
163 assert_eq!(g.num_cols(), 0);
164 assert!(!g.is_err());
165 assert_eq!(g.row(0), None);
166 }
167
168 #[test]
169 fn grid_with_data() {
170 let g = sample_grid();
171 assert!(!g.is_empty());
172 assert_eq!(g.len(), 2);
173 assert_eq!(g.num_cols(), 3);
174 }
175
176 #[test]
177 fn col_lookup() {
178 let g = sample_grid();
179
180 let id_col = g.col("id").unwrap();
181 assert_eq!(id_col.name, "id");
182
183 let dis_col = g.col("dis").unwrap();
184 assert_eq!(dis_col.name, "dis");
185
186 assert!(g.col("nonexistent").is_none());
187 }
188
189 #[test]
190 fn col_names() {
191 let g = sample_grid();
192 let names: Vec<&str> = g.col_names().collect();
193 assert_eq!(names, vec!["id", "dis", "area"]);
194 }
195
196 #[test]
197 fn row_access() {
198 let g = sample_grid();
199
200 let r0 = g.row(0).unwrap();
201 assert_eq!(r0.get("dis"), Some(&Kind::Str("Site One".into())));
202
203 let r1 = g.row(1).unwrap();
204 assert_eq!(r1.get("dis"), Some(&Kind::Str("Site Two".into())));
205
206 assert!(g.row(2).is_none());
207 }
208
209 #[test]
210 fn iteration() {
211 let g = sample_grid();
212 let rows: Vec<&HDict> = g.iter().collect();
213 assert_eq!(rows.len(), 2);
214 }
215
216 #[test]
217 fn is_err_false_for_normal_grid() {
218 let g = sample_grid();
219 assert!(!g.is_err());
220 }
221
222 #[test]
223 fn is_err_true_with_err_marker() {
224 let mut meta = HDict::new();
225 meta.set("err", Kind::Marker);
226 meta.set("dis", Kind::Str("some error message".into()));
227
228 let g = HGrid::from_parts(meta, vec![], vec![]);
229 assert!(g.is_err());
230 assert!(g.is_empty());
231 }
232
233 #[test]
234 fn col_with_meta() {
235 let mut meta = HDict::new();
236 meta.set("unit", Kind::Str("kW".into()));
237
238 let col = HCol::with_meta("power", meta);
239 assert_eq!(col.name, "power");
240 assert!(col.meta.has("unit"));
241 }
242
243 #[test]
244 fn display() {
245 let g = sample_grid();
246 let s = g.to_string();
247 assert!(s.contains("id"));
248 assert!(s.contains("dis"));
249 assert!(s.contains("area"));
250 assert!(s.contains("rows: 2"));
251 }
252
253 #[test]
254 fn equality() {
255 let a = sample_grid();
256 let b = sample_grid();
257 assert_eq!(a, b);
258 }
259
260 #[test]
261 fn default_is_empty() {
262 let g = HGrid::default();
263 assert!(g.is_empty());
264 assert_eq!(g.num_cols(), 0);
265 }
266
267 #[test]
268 fn from_parts() {
269 let cols = vec![HCol::new("name")];
270 let mut row = HDict::new();
271 row.set("name", Kind::Str("test".into()));
272
273 let g = HGrid::from_parts(HDict::new(), cols, vec![row]);
274 assert_eq!(g.len(), 1);
275 assert_eq!(g.num_cols(), 1);
276 }
277}