layout/gv/
record.rs

1//! A collection of helper functions that are related to records. Records are
2//! recursive data-structures that contain boxes and labels. This is where you
3//! can find code for figuring out sizes and finding the location of a named
4//! 'port'.
5
6use crate::std_shapes::shapes::ShapeKind;
7use crate::std_shapes::shapes::*;
8
9pub fn print_record(rec: &RecordDef, indent: usize) {
10    match rec {
11        RecordDef::Text(label, port) => {
12            println!("\"{}\"", label);
13            if let Option::Some(port) = port {
14                println!("\"{}\"", port);
15            }
16        }
17        RecordDef::Array(arr) => {
18            print!("{}", " ".repeat(indent));
19            println!("[");
20            for elem in arr {
21                print_record(elem, indent + 1);
22            }
23            print!("{}", " ".repeat(indent));
24            println!("]");
25        }
26    }
27}
28
29struct RecordParser {
30    input: Vec<char>,
31    pos: usize,
32}
33
34struct RecordParserFrame {
35    label: String,
36    arr: Vec<RecordDef>,
37}
38
39impl RecordParserFrame {
40    pub fn new() -> Self {
41        Self {
42            label: String::new(),
43            arr: Vec::new(),
44        }
45    }
46
47    /// Split a label such as "<f0> XXX" into the port part "f0" and the text
48    /// part "XXX".
49    fn split_label_to_text_and_port(str: &str) -> (String, Option<String>) {
50        let str = str.trim();
51        if str.starts_with('<') {
52            if let Option::Some(idx) = str.find('>') {
53                let port = &str[1..idx];
54                return (
55                    str[idx + 1..].trim().to_string(),
56                    Option::Some(port.to_string()),
57                );
58            }
59        }
60        (str.to_string(), Option::None)
61    }
62
63    pub fn finalize_label(&mut self) {
64        if !self.label.trim().is_empty() {
65            let ret = Self::split_label_to_text_and_port(&self.label);
66            let text = RecordDef::Text(ret.0, ret.1);
67            self.arr.push(text);
68            self.label.clear();
69        }
70    }
71
72    pub fn finalize_record(&mut self) -> RecordDef {
73        self.finalize_label();
74        match self.arr.len() {
75            0 => RecordDef::Text(String::from(""), Option::None),
76            _ => RecordDef::Array(self.arr.clone()),
77        }
78    }
79}
80impl RecordParser {
81    pub fn new(input: &str) -> Self {
82        Self {
83            input: input.chars().collect(),
84            pos: 0,
85        }
86    }
87
88    pub fn parse(&mut self) -> RecordDef {
89        let mut frame = RecordParserFrame::new();
90        loop {
91            // Read one char.
92            let ch = self.input[self.pos];
93
94            match ch {
95                '{' => {
96                    self.pos += 1;
97                    // Finalize the label.
98                    frame.finalize_label();
99                    // Parse the sub row:
100                    let ret = self.parse();
101                    frame.arr.push(ret);
102                }
103                '|' => {
104                    // New record in the row.
105                    self.pos += 1;
106                    frame.finalize_label();
107                }
108                '}' => {
109                    // Finish the row.
110                    self.pos += 1;
111                    // Finalize the row.
112                    frame.finalize_label();
113                    return frame.finalize_record();
114                }
115                _ => {
116                    self.pos += 1;
117
118                    // Handle regular chars. Add them to the current label.
119                    frame.label.push(ch);
120                }
121            }
122            // Are we at the end of the buffer?
123            if self.pos == self.input.len() {
124                return frame.finalize_record();
125            }
126        }
127    }
128}
129
130pub fn parse_record_string(label: &str) -> RecordDef {
131    RecordParser::new(label).parse()
132}
133
134// Construct a record from a description string.
135pub fn record_builder(label: &str) -> ShapeKind {
136    let res = parse_record_string(label);
137    ShapeKind::Record(res)
138}