1use crate::*;
2use indexmap::map::*;
3
4#[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 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 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 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 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 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