mech_core/structures/
record.rs

1use crate::*;
2use indexmap::map::*;
3
4// Record ------------------------------------------------------------------
5
6#[cfg(feature = "record")]
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct MechRecord {
9  pub cols: usize,
10  pub kinds: Vec<ValueKind>,
11  pub data: IndexMap<u64,Value>,
12  pub field_names: HashMap<u64,String>,
13}
14
15#[cfg(feature = "record")]
16impl MechRecord {
17
18  pub fn check_record_schema(&self, record: &MechRecord) -> MResult<()> {
19    for (&field_id, _value) in &self.data {
20      // Check field existence
21      if !record.data.contains_key(&field_id) {
22        return Err(MechError2::new(MissingFieldInRecordError { field_id }, None).with_compiler_loc());
23      }
24      // Get expected kind
25      let expected_kind = self.kinds.get(self.key_index(field_id)?)
26        .ok_or_else(|| MechError2::new(MissingKindForFieldError { field_id }, None).with_compiler_loc())?;
27      // Get actual kind
28      let actual_kind = record.kinds.get(record.key_index(field_id)?)
29        .ok_or_else(|| MechError2::new(MissingKindInComparedRecordError { field_id }, None).with_compiler_loc())?;
30      // Compare kinds
31      if expected_kind != actual_kind {
32        return Err(MechError2::new(
33          RecordFieldKindMismatchError {
34            field_id,
35            expected_kind: expected_kind.clone(),
36            actual_kind: actual_kind.clone(),
37          },
38          None
39        ).with_compiler_loc());
40      }
41      // Check field names
42      if self.field_names.get(&field_id) != record.field_names.get(&field_id) {
43        return Err(MechError2::new(
44          RecordFieldNameMismatchError {
45            field_id,
46            expected_name: self.field_names.get(&field_id).cloned(),
47            actual_name: record.field_names.get(&field_id).cloned(),
48          },
49          None
50        ).with_compiler_loc());
51      }
52    }
53    Ok(())
54  }
55
56  fn key_index(&self, field_id: u64) -> MResult<usize> {
57    self.data.keys().position(|&id| id == field_id).ok_or_else(|| {
58      MechError2::new(
59        KeyNotFoundInKeyIndexError { field_id },
60        None
61      ).with_compiler_loc()
62    })
63  }
64
65  #[cfg(feature = "pretty_print")]
66  pub fn to_html(&self) -> String {
67    let mut bindings = Vec::new();
68
69    for (key, value) in &self.data {
70      let name = self.field_names.get(key).unwrap();
71
72      let binding_html = format!(
73        "<span class=\"mech-binding\">\
74          <span class=\"mech-binding-name\">{}</span>\
75          <span class=\"mech-binding-colon-op\">:</span>\
76          <span class=\"mech-binding-value\">{}</span>\
77        </span>",
78        name,
79        value.to_html(),
80      );
81
82      bindings.push(binding_html);
83    }
84
85    format!(
86      "<span class=\"mech-record\">\
87        <span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span>\
88      </span>",
89      bindings.join("<span class=\"mech-separator\">, </span>")
90    )
91  }
92
93  pub fn get(&self, key: &u64) -> Option<&Value> {
94    self.data.get(key)
95  }
96
97  pub fn new(vec: Vec<(&str, Value)>) -> MechRecord {
98    let mut data = IndexMap::new();
99    let mut field_names = HashMap::new();
100
101    for (name, value) in vec {
102      let col_id = hash_str(name);
103
104      field_names.insert(col_id, name.to_string());
105      data.insert(col_id, value);
106    }
107
108    let kinds = data.iter().map(|(_, v)| v.kind()).collect();
109
110    MechRecord {
111      cols: data.len(),
112      kinds,
113      data,
114      field_names,
115    }
116  }
117
118  pub fn from_vec(vec: Vec<((u64,String),Value)>) -> MechRecord {
119    let mut data = IndexMap::new();
120    let mut field_names = HashMap::new();
121    for ((k,s),v) in vec {
122      field_names.insert(k,s);
123      data.insert(k,v);
124    }
125    let kinds = data.iter().map(|(_,v)| v.kind()).collect();
126    MechRecord{cols: data.len(), kinds, data, field_names}
127  }
128
129  pub fn insert_field(&mut self, key: u64, value: Value) {
130    self.cols += 1;
131    self.kinds.push(value.kind());
132    self.data.insert(key, value);
133  }
134
135  pub fn kind(&self) -> ValueKind {
136    ValueKind::Record(self.data.iter()
137      .map(|(k,v)| (self.field_names.get(k).unwrap().to_string(), v.kind()))
138      .collect())
139  }
140
141  pub fn size_of(&self) -> usize {
142    self.data.iter().map(|(_,v)| v.size_of()).sum()
143  }
144
145  pub fn shape(&self) -> Vec<usize> {
146    vec![1,self.cols]
147  }
148}
149
150#[cfg(feature = "pretty_print")]
151impl PrettyPrint for MechRecord {
152  fn pretty_print(&self) -> String {
153    let mut builder = Builder::default();
154    let mut key_strings = vec![];
155    let mut element_strings = vec![];
156    for (k,v) in &self.data {
157      let field_name = self.field_names.get(k).unwrap();
158      key_strings.push(format!("{}<{}>",field_name, v.kind()));
159      element_strings.push(v.pretty_print());
160    }
161    builder.push_record(key_strings);
162    builder.push_record(element_strings);
163    let mut table = builder.build();
164    table.with(Style::modern_rounded());
165    format!("{table}")
166  }
167}
168
169#[cfg(feature = "record")]
170impl Hash for MechRecord {
171  fn hash<H: Hasher>(&self, state: &mut H) {
172    for (k,v) in self.data.iter() {
173      k.hash(state);
174      v.hash(state);
175    }
176  }
177}
178
179#[derive(Debug, Clone)]
180pub struct MissingFieldInRecordError {
181  pub field_id: u64,
182}
183
184impl MechErrorKind2 for MissingFieldInRecordError {
185  fn name(&self) -> &str {
186    "MissingFieldInRecord"
187  }
188  fn message(&self) -> String {
189    format!("Record is missing required field `{}`.", self.field_id)
190  }
191}
192
193#[derive(Debug, Clone)]
194pub struct MissingKindForFieldError {
195  pub field_id: u64,
196}
197
198impl MechErrorKind2 for MissingKindForFieldError {
199  fn name(&self) -> &str {
200    "MissingKindForField"
201  }
202  fn message(&self) -> String {
203    format!("Missing expected kind for field `{}` in schema.", self.field_id)
204  }
205}
206
207#[derive(Debug, Clone)]
208pub struct MissingKindInComparedRecordError {
209  pub field_id: u64,
210}
211
212impl MechErrorKind2 for MissingKindInComparedRecordError {
213  fn name(&self) -> &str {
214    "MissingKindInComparedRecord"
215  }
216  fn message(&self) -> String {
217    format!("Missing kind for field `{}` in the compared record.", self.field_id)
218  }
219}
220
221#[derive(Debug, Clone)]
222pub struct RecordFieldKindMismatchError {
223  pub field_id: u64,
224  pub expected_kind: ValueKind,
225  pub actual_kind: ValueKind,
226}
227
228impl MechErrorKind2 for RecordFieldKindMismatchError {
229  fn name(&self) -> &str {
230    "RecordFieldKindMismatch"
231  }
232  fn message(&self) -> String {
233    format!(
234      "Kind mismatch for column `{}` (expected `{}`, found `{}`).",
235      self.field_id, self.expected_kind, self.actual_kind
236    )
237  }
238}
239
240#[derive(Debug, Clone)]
241pub struct RecordFieldNameMismatchError {
242  pub field_id: u64,
243  pub expected_name: Option<String>,
244  pub actual_name: Option<String>,
245}
246
247impl MechErrorKind2 for RecordFieldNameMismatchError {
248  fn name(&self) -> &str {
249    "RecordFieldNameMismatch"
250  }
251  fn message(&self) -> String {
252    match (&self.expected_name, &self.actual_name) {
253      (Some(e), Some(a)) => format!(
254        "Field name mismatch for field `{}` (expected `{}`, found `{}`).",
255        self.field_id, e, a
256      ),
257      (Some(e), None) => format!(
258        "Field name mismatch for field `{}` (expected `{}`, but no field found).",
259        self.field_id, e
260      ),
261      _ => format!("Field name mismatch for field `{}`.", self.field_id),
262    }
263  }
264}
265
266#[derive(Debug, Clone)]
267pub struct KeyNotFoundInKeyIndexError {
268  pub field_id: u64,
269}
270
271impl MechErrorKind2 for KeyNotFoundInKeyIndexError {
272  fn name(&self) -> &str {
273    "KeyNotFoundInKeyIndex"
274  }
275  fn message(&self) -> String {
276    format!("Key id `{}` not found in key_index.", self.field_id)
277  }
278}