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 (&col_id, _value) in &self.data {
20      // Check column existence
21      if !record.data.contains_key(&col_id) {
22        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Missing column {} in record data", col_id),kind: MechErrorKind::None});
23      }
24
25      // Get expected kind
26      let expected_kind = self.kinds.get(self.key_index(col_id)?).ok_or_else(|| MechError { id: line!(), file: file!().to_string(), tokens: vec![], msg: format!("Missing kind for column {}", col_id), kind: MechErrorKind::None})?;
27
28      // Get actual kind
29      let actual_kind = record.kinds.get(record.key_index(col_id)?).ok_or_else(|| MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Missing kind for column {} in compared record", col_id),kind: MechErrorKind::None,})?;
30
31      // Compare kinds
32      if expected_kind != actual_kind {
33        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Kind mismatch for column {} (expected {:?}, found {:?})", col_id, expected_kind, actual_kind),kind: MechErrorKind::None,});
34      }
35
36      // Check field names
37      let expected_name = self.field_names.get(&col_id);
38      let actual_name = record.field_names.get(&col_id);
39
40      if expected_name != actual_name {
41        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: "".to_string(),kind: MechErrorKind::None,});
42      }
43    }
44
45    Ok(())
46  }
47
48  fn key_index(&self, col_id: u64) -> MResult<usize> {
49    self.data.keys().position(|&id| id == col_id).ok_or_else(|| MechError {
50      id: line!(),
51      file: file!().to_string(),
52      tokens: vec![],
53      msg: format!("Column id {} not found in key_index", col_id),
54      kind: MechErrorKind::None,
55    })
56  }
57
58  #[cfg(feature = "pretty_print")]
59  pub fn to_html(&self) -> String {
60    let mut bindings = Vec::new();
61
62    for (key, value) in &self.data {
63      let name = self.field_names.get(key).unwrap();
64
65      let binding_html = format!(
66        "<span class=\"mech-binding\">\
67          <span class=\"mech-binding-name\">{}</span>\
68          <span class=\"mech-binding-colon-op\">:</span>\
69          <span class=\"mech-binding-value\">{}</span>\
70        </span>",
71        name,
72        value.to_html(),
73      );
74
75      bindings.push(binding_html);
76    }
77
78    format!(
79      "<span class=\"mech-record\">\
80        <span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span>\
81      </span>",
82      bindings.join("<span class=\"mech-separator\">, </span>")
83    )
84  }
85
86  pub fn get(&self, key: &u64) -> Option<&Value> {
87    self.data.get(key)
88  }
89
90  pub fn new(vec: Vec<(&str, Value)>) -> MechRecord {
91    let mut data = IndexMap::new();
92    let mut field_names = HashMap::new();
93
94    for (name, value) in vec {
95      let col_id = hash_str(name);
96
97      field_names.insert(col_id, name.to_string());
98      data.insert(col_id, value);
99    }
100
101    let kinds = data.iter().map(|(_, v)| v.kind()).collect();
102
103    MechRecord {
104      cols: data.len(),
105      kinds,
106      data,
107      field_names,
108    }
109  }
110
111  pub fn from_vec(vec: Vec<((u64,String),Value)>) -> MechRecord {
112    let mut data = IndexMap::new();
113    let mut field_names = HashMap::new();
114    for ((k,s),v) in vec {
115      field_names.insert(k,s);
116      data.insert(k,v);
117    }
118    let kinds = data.iter().map(|(_,v)| v.kind()).collect();
119    MechRecord{cols: data.len(), kinds, data, field_names}
120  }
121
122  pub fn insert_field(&mut self, key: u64, value: Value) {
123    self.cols += 1;
124    self.kinds.push(value.kind());
125    self.data.insert(key, value);
126  }
127
128  pub fn kind(&self) -> ValueKind {
129    ValueKind::Record(self.data.iter()
130      .map(|(k,v)| (self.field_names.get(k).unwrap().to_string(), v.kind()))
131      .collect())
132  }
133
134  pub fn size_of(&self) -> usize {
135    self.data.iter().map(|(_,v)| v.size_of()).sum()
136  }
137
138  pub fn shape(&self) -> Vec<usize> {
139    vec![1,self.cols]
140  }
141}
142
143#[cfg(feature = "pretty_print")]
144impl PrettyPrint for MechRecord {
145  fn pretty_print(&self) -> String {
146    let mut builder = Builder::default();
147    let mut key_strings = vec![];
148    let mut element_strings = vec![];
149    for (k,v) in &self.data {
150      let field_name = self.field_names.get(k).unwrap();
151      key_strings.push(format!("{}<{}>",field_name, v.kind()));
152      element_strings.push(v.pretty_print());
153    }
154    builder.push_record(key_strings);
155    builder.push_record(element_strings);
156    let mut table = builder.build();
157    table.with(Style::modern_rounded());
158    format!("{table}")
159  }
160}
161
162#[cfg(feature = "record")]
163impl Hash for MechRecord {
164  fn hash<H: Hasher>(&self, state: &mut H) {
165    for (k,v) in self.data.iter() {
166      k.hash(state);
167      v.hash(state);
168    }
169  }
170}
171
172