mech_core/
structures.rs

1use crate::matrix::Matrix;
2use crate::*;
3use crate::nodes::Matrix as Mat;
4use crate::{MechError, MechErrorKind, hash_str, nodes::Kind as NodeKind, nodes::*, humanize};
5use std::collections::HashMap;
6
7use na::{Vector3, DVector, Vector2, Vector4, RowDVector, Matrix1, Matrix3, Matrix4, RowVector3, RowVector4, RowVector2, DMatrix, Rotation3, Matrix2x3, Matrix3x2, Matrix6, Matrix2};
8use std::hash::{Hash, Hasher};
9use indexmap::set::IndexSet;
10use indexmap::map::*;
11use tabled::{
12  builder::Builder,
13  settings::{object::Rows,Panel, Span, Alignment, Modify, Style},
14  Tabled,
15};
16use paste::paste;
17use serde::ser::{Serialize, Serializer, SerializeStruct};
18use serde::de::{self, Deserialize, SeqAccess, Deserializer, MapAccess, Visitor};
19use std::fmt;
20use std::cell::RefCell;
21use std::rc::Rc;
22
23// Set --------------------------------------------------------------------------
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct MechSet {
27  pub kind: ValueKind,
28  pub num_elements: usize,
29  pub set: IndexSet<Value>,
30}
31
32impl MechSet {
33
34  pub fn to_html(&self) -> String {
35    let mut src = String::new();
36    for (i, element) in self.set.iter().enumerate() {
37      let e = element.to_html();
38      if i == 0 {
39        src = format!("{}", e);
40      } else {
41        src = format!("{}, {}", src, e);
42      }
43    }
44    format!("<span class=\"mech-set\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>",src)
45  }
46
47  pub fn kind(&self) -> ValueKind {
48    let size = if self.num_elements > 0 { Some(self.num_elements) } else { None };
49    ValueKind::Set(Box::new(self.kind.clone()), size)
50  }
51
52  pub fn size_of(&self) -> usize {
53    self.set.iter().map(|x| x.size_of()).sum()
54  }
55
56  pub fn from_vec(vec: Vec<Value>) -> MechSet {
57    let mut set = IndexSet::new();
58    for v in vec {
59      set.insert(v);
60    }
61    let kind = if set.len() > 0 { set.iter().next().unwrap().kind() } else { ValueKind::Empty };
62    MechSet{
63      kind,
64      num_elements: set.len(),
65      set}
66  }
67
68  pub fn pretty_print(&self) -> String {
69    let mut builder = Builder::default();
70    let mut element_strings = vec![];
71    for x in self.set.iter() {
72      element_strings.push(x.pretty_print());
73    }
74    builder.push_record(element_strings);
75
76    let style = Style::empty()
77      .top(' ')
78      .left('║')
79      .right('║')
80      .bottom(' ')
81      .vertical(' ')
82      .intersection_bottom(' ')
83      .corner_top_left('╔')
84      .corner_top_right('╗')
85      .corner_bottom_left('╚')
86      .corner_bottom_right('╝');
87    let mut table = builder.build();
88    table.with(style);
89    format!("{table}")
90  }
91
92}
93
94impl Hash for MechSet {
95  fn hash<H: Hasher>(&self, state: &mut H) {
96    for x in self.set.iter() {
97      x.hash(state)
98    }
99  }
100}
101
102// Map ------------------------------------------------------------------
103
104#[derive(Clone, Debug, PartialEq, Eq)]
105pub struct MechMap {
106  pub key_kind: ValueKind,
107  pub value_kind: ValueKind,
108  pub num_elements: usize,
109  pub map: IndexMap<Value,Value>,
110}
111
112impl MechMap {
113
114  pub fn to_html(&self) -> String {
115    let mut src = String::new();
116    for (i, (key, value)) in self.map.iter().enumerate() {
117      let k = key.to_html();
118      let v = value.to_html();
119      if i == 0 {
120        src = format!("{}: {}", k, v);
121      } else {
122        src = format!("{}, {}: {}", src, k, v);
123      }
124    }
125    format!("<span class=\"mech-map\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>",src)
126  }
127
128  pub fn kind(&self) -> ValueKind {
129    ValueKind::Map(Box::new(self.key_kind.clone()), Box::new(self.value_kind.clone()))
130  }
131
132  pub fn size_of(&self) -> usize {
133    self.map.iter().map(|(k,v)| k.size_of() + v.size_of()).sum()
134  }
135
136  pub fn pretty_print(&self) -> String {
137    let mut builder = Builder::default();
138    let mut element_strings = vec![];
139    let mut key_strings = vec![];
140    for (k,v) in self.map.iter() {
141      element_strings.push(v.pretty_print());
142      key_strings.push(k.pretty_print());
143    }    
144    builder.push_record(key_strings);
145    builder.push_record(element_strings);
146    let mut table = builder.build();
147    table.with(Style::modern_rounded());
148    format!("{table}")
149  }
150
151  pub fn from_vec(vec: Vec<(Value,Value)>) -> MechMap {
152    let mut map = IndexMap::new();
153    for (k,v) in vec {
154      map.insert(k,v);
155    }
156    MechMap{
157      key_kind: map.keys().next().unwrap().kind(),
158      value_kind: map.values().next().unwrap().kind(),
159      num_elements: map.len(),
160      map}
161  }
162}
163
164impl Hash for MechMap {
165  fn hash<H: Hasher>(&self, state: &mut H) {
166    for x in self.map.iter() {
167      x.hash(state)
168    }
169  }
170}
171
172// Table ------------------------------------------------------------------
173
174#[derive(Clone, Debug, PartialEq, Eq)]
175pub struct MechTable {
176  pub rows: usize,
177  pub cols: usize,
178  pub data: IndexMap<u64,(ValueKind,Matrix<Value>)>,
179  pub col_names: HashMap<u64,String>,
180}
181
182impl MechTable {
183
184  pub fn from_records(records: Vec<MechRecord>) -> MResult<MechTable> {
185    if records.is_empty() {
186      return Err(MechError { id: line!(), file: file!().to_string(), tokens: vec![], msg: "Cannot create MechTable from empty record list.".to_string(), kind: MechErrorKind::None});
187    }
188
189    let first = &records[0];
190    let rows = records.len();
191    let cols = first.cols;
192
193    let mut col_data: IndexMap<u64, Vec<Value>> = IndexMap::new();
194
195    for (&col_id, value) in &first.data {
196      col_data.insert(col_id, vec![value.clone()]);
197    }
198
199    let mut kinds = IndexMap::new();
200    for (col_id, kind) in first.data.keys().zip(&first.kinds) {
201      kinds.insert(*col_id, kind.clone());
202    }
203
204    let col_names = first.field_names.clone();
205
206    for record in records.iter().skip(1) {
207      first.check_record_schema(record)?;
208      for (&col_id, value) in &record.data {
209        col_data.entry(col_id).or_insert_with(Vec::new).push(value.clone());
210      }
211    }
212
213    let data: IndexMap<u64, (ValueKind, Matrix<Value>)> = col_data
214      .into_iter()
215      .map(|(col_id, values)| {
216        let kind = kinds[&col_id].clone();
217        let matrix = Matrix::DVector(new_ref(DVector::from_vec(values)));
218        (col_id, (kind, matrix))
219      })
220      .collect();
221
222    Ok(MechTable {rows,cols,data,col_names})
223  }
224  
225  pub fn from_kind(kind: ValueKind) -> MResult<MechTable> {
226    match kind {
227      ValueKind::Table(tbl,sze) => {
228        let mut data = IndexMap::new();
229        let mut col_names = HashMap::new();
230        for (col_id, col_kind) in &tbl {
231          let matrix = Matrix::DVector(new_ref(DVector::from_vec(vec![Value::Empty; sze])));
232          col_names.insert(hash_str(col_id), col_id.clone());
233          data.insert(hash_str(&col_id), (col_kind.clone(), matrix));
234        }
235        Ok(MechTable {rows: sze, cols: tbl.len(), data, col_names})
236      }
237      _ => {
238        return Err(MechError { id: line!(), file: file!().to_string(), tokens: vec![], msg: "Cannot create MechTable from non-table kind.".to_string(), kind: MechErrorKind::None });
239      }
240    }
241  }
242
243  pub fn empty_table(&self, rows: usize) -> MechTable {
244    let mut data = IndexMap::new();
245    for col in self.data.iter() {
246      let (key, (kind, matrix)) = col;
247      // make a new vector the length of ix with values Value::Empty
248      let elements = vec![Value::Empty; rows];
249      let new_matrix = Matrix::DVector(new_ref(DVector::from_vec(elements)));
250      data.insert(*key, (kind.clone(), new_matrix));
251    }
252    MechTable { rows: rows, cols: self.cols, data, col_names: self.col_names.clone() }
253  }
254
255  pub fn check_record_schema(&self, record: &MechRecord) -> MResult<bool> {
256
257    for (&col_id, record_value) in &record.data {
258      // Check that the column exists in the table
259      // self.get data col id _or continue to the next column
260      let (expected_kind, column_matrix) = match self.data.get(&col_id) {
261        Some(data) => data,
262        None => {
263          continue;
264        }
265      };
266
267      // Check actual value kind
268      let actual_kind = record_value.kind();
269
270      if expected_kind != &actual_kind {
271        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} kind mismatch (expected: {:?}, found: {:?})",col_id, expected_kind, actual_kind),kind: MechErrorKind::None,});
272      }
273
274      // Check column name
275      if let Some(expected_name) = self.col_names.get(&col_id) {
276        if let Some(field_name) = record.field_names.get(&col_id) {
277          if expected_name != field_name {
278            return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} name mismatch (expected: '{}', found: '{}')",col_id, expected_name, field_name),kind: MechErrorKind::None,});
279          }
280        }
281      }
282    }
283
284    Ok(true)
285  }
286  
287  pub fn check_table_schema(&self, record: &MechTable) -> MResult<bool> {
288
289    // Check that the column names match
290    for (&col_id, col_name) in &self.col_names {
291      if let Some(record_name) = record.col_names.get(&col_id) {
292        if col_name != record_name {
293          return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} name mismatch (expected: '{}', found: '{}')",col_id, col_name, record_name),kind: MechErrorKind::None,});
294        }
295      } else {
296        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} not found in record",col_id),kind: MechErrorKind::None,});
297      }
298    }
299
300    // Check that the data kinds match
301    for (&col_id, (expected_kind, _)) in &self.data {
302      if let Some((record_kind, _)) = record.data.get(&col_id) {
303        if expected_kind != record_kind {
304          return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} kind mismatch (expected: {:?}, found: {:?})",col_id, expected_kind, record_kind),kind: MechErrorKind::None,});
305        }
306      } else {
307        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Schema mismatch: column {} not found in record",col_id),kind: MechErrorKind::None,});
308      }
309    }
310
311    Ok(true)
312  }
313
314  pub fn append_table(&mut self, other: &MechTable) -> MResult<()> {
315    self.check_table_schema(other)?;
316    for (&col_id, (_, other_matrix)) in &other.data {
317      let (_, self_matrix) = self.data.get_mut(&col_id).ok_or(MechError {
318        id: line!(),
319        file: file!().to_string(),
320        tokens: vec![],
321        msg: format!("Column {} not found in destination table", col_id),
322        kind: MechErrorKind::None,
323      })?;
324
325      self_matrix.append(other_matrix).map_err(|err| MechError {
326        id: line!(),
327        file: file!().to_string(),
328        tokens: vec![],
329        msg: "".to_string(),
330        kind: MechErrorKind::None,
331      })?;
332    }
333    self.rows += other.rows;
334    Ok(())
335  }
336
337  pub fn append_record(&mut self, record: MechRecord) -> MResult<()> {
338    // Validate schema (this includes column count, types, and optional name checks)
339    self.check_record_schema(&record)?;
340
341    // Append each value to the corresponding column in the matrix
342    for (&col_id, value) in &record.data {
343      if let Some((_kind, column_matrix)) = self.data.get_mut(&col_id) {
344        let result = column_matrix.push(value.clone());
345      } else {
346        continue;
347      }
348    }
349
350    // Increment row count
351    self.rows += 1;
352
353    Ok(())
354  }
355
356  pub fn get_record(&self, ix: usize) -> Option<MechRecord> {
357    if ix > self.rows {
358      return None;
359    }
360
361    let mut data: IndexMap<u64, Value> = IndexMap::new();
362    data = self.data.iter().map(|(key, (kind, matrix))| {
363      let value = matrix.index1d(ix);
364      let name = self.col_names.get(key).unwrap();
365      (hash_str(name), value.clone())
366    }).collect();
367
368    let mut kinds = Vec::with_capacity(self.cols);
369    kinds = self.data.iter().map(|(_, (kind, _))| kind.clone()).collect();
370
371    let mut field_names = self.col_names.clone();
372   
373    Some(MechRecord{cols: self.cols, kinds, data, field_names})
374  }
375
376
377  pub fn to_html(&self) -> String {
378    let mut html = String::new();
379
380    // Start table
381    html.push_str("<table class=\"mech-table\">");
382
383    // Build thead
384    html.push_str("<thead class=\"mech-table-header\"><tr>");
385    for (key, (kind, _matrix)) in self.data.iter() {
386        let col_name = self
387            .col_names
388            .get(key)
389            .cloned()
390            .unwrap_or_else(|| key.to_string());
391
392        let kind_str = format!(
393            "<span class=\"mech-kind-annotation\">&lt;<span class=\"mech-kind\">{}</span>&gt;</span>",
394            kind
395        );
396
397        html.push_str(&format!(
398            "<th class=\"mech-table-field\">\
399                <div class=\"mech-field\">\
400                  <span class=\"mech-field-name\">{}</span>\
401                  <span class=\"mech-field-kind\">{}</span>\
402                </div>\
403            </th>",
404            col_name, kind_str
405        ));
406    }
407    html.push_str("</tr></thead>");
408
409    // Build tbody
410    html.push_str("<tbody class=\"mech-table-body\">");
411    for row_idx in 1..=self.rows {
412        html.push_str("<tr class=\"mech-table-row\">");
413        for (_key, (_kind, matrix)) in self.data.iter() {
414            let value = matrix.index1d(row_idx);
415            html.push_str(&format!(
416                "<td class=\"mech-table-column\">{}</td>",
417                value.to_html()
418            ));
419        }
420        html.push_str("</tr>");
421    }
422    html.push_str("</tbody></table>");
423    html
424  }
425
426  pub fn new(rows: usize, cols: usize, data: IndexMap<u64,(ValueKind,Matrix<Value>)>, col_names: HashMap<u64,String>) -> MechTable {
427    MechTable{rows, cols, data, col_names}
428  }
429
430  pub fn kind(&self) -> ValueKind {
431    let column_kinds: Vec<(String, ValueKind)> = self.data.iter()
432      .filter_map(|(key, (kind, _))| {
433        let col_name = self.col_names.get(key)?;
434        Some((col_name.clone(), kind.clone()))
435      })
436      .collect();
437    ValueKind::Table(column_kinds, self.rows)
438  }
439  
440  pub fn size_of(&self) -> usize {
441    self.data.iter().map(|(_,(_,v))| v.size_of()).sum()
442  }
443
444  pub fn rows(&self) -> usize {
445    self.rows
446  }
447
448  pub fn cols(&self) -> usize {
449    self.cols
450  }
451
452  pub fn get(&self, key: &u64) -> Option<&(ValueKind,Matrix<Value>)> {
453    self.data.get(key)
454  }
455
456  pub fn pretty_print(&self) -> String {
457    let mut builder = Builder::default();
458    for (k,(knd,val)) in &self.data {
459      let name = self.col_names.get(k).unwrap();
460      let val_string: String = val.as_vec().iter()
461        .map(|x| x.pretty_print())
462        .collect::<Vec<String>>()
463        .join("\n");
464      let mut col_string = vec![format!("{}<{}>", name.to_string(), knd), val_string];
465      builder.push_column(col_string);
466    }
467    let mut table = builder.build();
468    table.with(Style::modern_rounded());
469    format!("{table}")
470  }
471
472  pub fn shape(&self) -> Vec<usize> {
473    vec![self.rows,self.cols]
474  }
475}
476
477impl Hash for MechTable {
478  fn hash<H: Hasher>(&self, state: &mut H) {
479    for (k,(knd,val)) in self.data.iter() {
480      k.hash(state);
481      knd.hash(state);
482      val.hash(state);
483    }
484  }
485}
486
487// Record ------------------------------------------------------------------
488
489#[derive(Clone, Debug, PartialEq, Eq)]
490pub struct MechRecord {
491  pub cols: usize,
492  pub kinds: Vec<ValueKind>,
493  pub data: IndexMap<u64,Value>,
494  pub field_names: HashMap<u64,String>,
495}
496
497impl MechRecord {
498
499  pub fn check_record_schema(&self, record: &MechRecord) -> MResult<()> {
500    for (&col_id, _value) in &self.data {
501      // Check column existence
502      if !record.data.contains_key(&col_id) {
503        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: format!("Missing column {} in record data", col_id),kind: MechErrorKind::None});
504      }
505
506      // Get expected kind
507      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})?;
508
509      // Get actual kind
510      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,})?;
511
512      // Compare kinds
513      if expected_kind != actual_kind {
514        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,});
515      }
516
517      // Check field names
518      let expected_name = self.field_names.get(&col_id);
519      let actual_name = record.field_names.get(&col_id);
520
521      if expected_name != actual_name {
522        return Err(MechError {id: line!(),file: file!().to_string(),tokens: vec![],msg: "".to_string(),kind: MechErrorKind::None,});
523      }
524    }
525
526    Ok(())
527  }
528
529  fn key_index(&self, col_id: u64) -> MResult<usize> {
530    self.data.keys().position(|&id| id == col_id).ok_or_else(|| MechError {
531      id: line!(),
532      file: file!().to_string(),
533      tokens: vec![],
534      msg: format!("Column id {} not found in key_index", col_id),
535      kind: MechErrorKind::None,
536    })
537  }
538
539  pub fn to_html(&self) -> String {
540    let mut bindings = Vec::new();
541
542    for (key, value) in &self.data {
543      let name = self.field_names.get(key).unwrap();
544
545      let binding_html = format!(
546        "<span class=\"mech-binding\">\
547          <span class=\"mech-binding-name\">{}</span>\
548          <span class=\"mech-binding-colon-op\">:</span>\
549          <span class=\"mech-binding-value\">{}</span>\
550        </span>",
551        name,
552        value.to_html(),
553      );
554
555      bindings.push(binding_html);
556    }
557
558    format!(
559      "<span class=\"mech-record\">\
560        <span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span>\
561      </span>",
562      bindings.join("<span class=\"mech-separator\">, </span>")
563    )
564  }
565
566  pub fn get(&self, key: &u64) -> Option<&Value> {
567    self.data.get(key)
568  }
569
570  pub fn new(vec: Vec<(&str, Value)>) -> MechRecord {
571    use std::collections::hash_map::DefaultHasher;
572    use std::hash::{Hash, Hasher};
573
574    let mut data = IndexMap::new();
575    let mut field_names = HashMap::new();
576
577    for (name, value) in vec {
578      let col_id = hash_str(name);
579
580      field_names.insert(col_id, name.to_string());
581      data.insert(col_id, value);
582    }
583
584    let kinds = data.iter().map(|(_, v)| v.kind()).collect();
585
586    MechRecord {
587      cols: data.len(),
588      kinds,
589      data,
590      field_names,
591    }
592  }
593
594  pub fn from_vec(vec: Vec<((u64,String),Value)>) -> MechRecord {
595    let mut data = IndexMap::new();
596    let mut field_names = HashMap::new();
597    for ((k,s),v) in vec {
598      field_names.insert(k,s);
599      data.insert(k,v);
600    }
601    let kinds = data.iter().map(|(_,v)| v.kind()).collect();
602    MechRecord{cols: data.len(), kinds, data, field_names}
603  }
604
605  pub fn insert_field(&mut self, key: u64, value: Value) {
606    self.cols += 1;
607    self.kinds.push(value.kind());
608    self.data.insert(key, value);
609  }
610
611  pub fn kind(&self) -> ValueKind {
612    ValueKind::Record(self.data.iter()
613      .map(|(k,v)| (self.field_names.get(k).unwrap().to_string(), v.kind()))
614      .collect())
615  }
616
617  pub fn size_of(&self) -> usize {
618    self.data.iter().map(|(_,v)| v.size_of()).sum()
619  }
620
621  pub fn pretty_print(&self) -> String {
622    let mut builder = Builder::default();
623    let mut key_strings = vec![];
624    let mut element_strings = vec![];
625    for (k,v) in &self.data {
626      let field_name = self.field_names.get(k).unwrap();
627      key_strings.push(format!("{}<{}>",field_name, v.kind()));
628      element_strings.push(v.pretty_print());
629    }
630    builder.push_record(key_strings);
631    builder.push_record(element_strings);
632    let mut table = builder.build();
633    table.with(Style::modern_rounded());
634    format!("{table}")
635  }
636
637  pub fn shape(&self) -> Vec<usize> {
638    vec![1,self.cols]
639  }
640}
641
642impl Hash for MechRecord {
643  fn hash<H: Hasher>(&self, state: &mut H) {
644    for (k,v) in self.data.iter() {
645      k.hash(state);
646      v.hash(state);
647    }
648  }
649}
650
651// Tuple ----------------------------------------------------------------------
652
653#[derive(Clone, Debug, PartialEq, Eq)]
654pub struct MechTuple {
655  pub elements: Vec<Box<Value>>
656}
657
658impl MechTuple {
659
660  pub fn to_html(&self) -> String {
661    let mut elements = Vec::new();
662    for element in &self.elements {
663      elements.push(element.to_html());
664    }
665    format!("<span class=\"mech-tuple\"><span class=\"mech-start-brace\">(</span>{}<span class=\"mech-end-brace\">)</span></span>", elements.join(", "))
666  }
667
668  pub fn pretty_print(&self) -> String {
669    let mut builder = Builder::default();
670    let string_elements: Vec<String> = self.elements.iter().map(|e| e.pretty_print()).collect::<Vec<String>>();
671    builder.push_record(string_elements);
672    let mut table = builder.build();
673    let style = Style::empty()
674      .top(' ')
675      .left('│')
676      .right('│')
677      .bottom(' ')
678      .vertical(' ')
679      .intersection_bottom('ʼ')
680      .corner_top_left('╭')
681      .corner_top_right('╮')
682      .corner_bottom_left('╰')
683      .corner_bottom_right('╯');
684    table.with(style);
685    format!("{table}")
686  }
687
688  pub fn get(&self, index: usize) -> Option<&Value> {
689    if index < self.elements.len() {
690      Some(self.elements[index].as_ref())
691    } else {
692      None
693    }
694  }
695
696  pub fn from_vec(elements: Vec<Value>) -> Self {
697    MechTuple{elements: elements.iter().map(|m| Box::new(m.clone())).collect::<Vec<Box<Value>>>()}
698  }
699
700  pub fn size(&self) -> usize {
701    self.elements.len()
702  }
703
704  pub fn kind(&self) -> ValueKind {
705    ValueKind::Tuple(self.elements.iter().map(|x| x.kind()).collect())
706  }
707
708  pub fn size_of(&self) -> usize {
709    self.elements.iter().map(|x| x.size_of()).sum()
710  }
711
712}
713
714impl Hash for MechTuple {
715  fn hash<H: Hasher>(&self, state: &mut H) {
716    for x in self.elements.iter() {
717        x.hash(state)
718    }
719  }
720}
721
722// Enum -----------------------------------------------------------------------
723
724#[derive(Clone, Debug, PartialEq, Eq)]
725pub struct MechEnum {
726  pub id: u64,
727  pub variants: Vec<(u64, Option<Value>)>,
728}
729
730impl MechEnum {
731
732  pub fn to_html(&self) -> String {
733    let mut variants = Vec::new();
734    for (id, value) in &self.variants {
735      let value_html = match value {
736        Some(v) => v.to_html(),
737        None => "None".to_string(),
738      };
739      variants.push(format!("<span class=\"mech-enum-variant\">{}: {}</span>", id, value_html));
740    }
741    format!("<span class=\"mech-enum\"><span class=\"mech-start-brace\">{{</span>{}<span class=\"mech-end-brace\">}}</span></span>", variants.join(", "))
742  }
743
744  pub fn kind(&self) -> ValueKind {
745    ValueKind::Enum(self.id)
746  }
747
748  pub fn size_of(&self) -> usize {
749    self.variants.iter().map(|(_,v)| v.as_ref().map_or(0, |x| x.size_of())).sum()
750  }
751
752  pub fn pretty_print(&self) -> String {
753    let mut builder = Builder::default();
754    let string_elements: Vec<String> = vec![format!("{}{:?}",self.id,self.variants)];
755    builder.push_record(string_elements);
756    let mut table = builder.build();
757    table.with(Style::modern_rounded());
758    format!("{table}")
759  }
760
761}
762
763impl Hash for MechEnum {
764  fn hash<H: Hasher>(&self, state: &mut H) {
765    self.id.hash(state);
766    self.variants.hash(state);
767  }
768}