donet_core/
dcswitch.rs

1/*
2    This file is part of Donet.
3
4    Copyright © 2024 Max Rodriguez <[email protected]>
5
6    Donet is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Affero General Public License,
8    as published by the Free Software Foundation, either version 3
9    of the License, or (at your option) any later version.
10
11    Donet is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU Affero General Public License for more details.
15
16    You should have received a copy of the GNU Affero General Public
17    License along with Donet. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20//! Data model that represents a DC switch statement.
21
22use crate::dcfield::DCField;
23use crate::hashgen::*;
24use std::collections::HashMap;
25
26/// Represents a case in a DC switch declaration.
27#[derive(Debug)]
28pub struct SwitchCase<'dc> {
29    switch: &'dc DCSwitch<'dc>,
30    /// Note that in the legacy DC language, switch cases
31    /// always assume to break, no matter if a break
32    /// statement was parsed at syntax analysis. This
33    /// legacy behavior is not followed by Donet.
34    breaks: bool,
35    /// Empty byte array signifies default case.
36    value: Vec<u8>,
37    fields: Vec<DCField<'dc>>,
38}
39
40impl std::fmt::Display for SwitchCase<'_> {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        if self.is_default() {
43            writeln!(f, "default:")?;
44        } else {
45            write!(f, "case ")?;
46            self.switch.key.format_packed_data(f, &self.value, false)?;
47            writeln!(f, ":")?;
48        }
49
50        for field in &self.fields {
51            f.write_str(&field.to_string())?;
52        }
53        if self.breaks {
54            writeln!(f, "break;")?;
55        }
56        Ok(())
57    }
58}
59
60impl LegacyDCHash for SwitchCase<'_> {
61    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
62        if !self.is_default() {
63            hashgen.add_blob(self.value.clone());
64        }
65
66        hashgen.add_int(self.get_num_fields() as i32);
67
68        for field in &self.fields {
69            field.generate_hash(hashgen);
70        }
71    }
72}
73
74impl<'dc> SwitchCase<'dc> {
75    /// Returns true if this case is a default case.
76    pub fn is_default(&self) -> bool {
77        self.value.is_empty()
78    }
79
80    /// Returns the number of fields in the case.
81    pub fn get_num_fields(&self) -> usize {
82        self.fields.len()
83    }
84
85    pub fn get_field(&self, index: usize) -> Option<&'dc DCField> {
86        self.fields.get(index)
87    }
88
89    pub fn get_field_by_name(&self, _name: String) -> Option<&'dc DCField> {
90        todo!()
91    }
92}
93
94/// Represents a DC Switch statement, which can appear inside
95/// a dclass declaration and represents two or more alternative
96/// unpacking schemes based on the first field read.
97#[derive(Debug)]
98pub struct DCSwitch<'dc> {
99    name: Option<String>,
100    key: DCField<'dc>,
101    cases: Vec<SwitchCase<'dc>>,
102    default_case: Option<SwitchCase<'dc>>,
103    case_fields: Vec<&'dc DCField<'dc>>,
104    cases_by_value: HashMap<Vec<u8>, usize>,
105}
106
107impl From<interim::DCSwitch> for DCSwitch<'_> {
108    fn from(value: interim::DCSwitch) -> Self {
109        Self {
110            name: value.name,
111            key: todo!(),
112            cases: vec![],
113            default_case: None,
114            case_fields: vec![],
115            cases_by_value: HashMap::default(),
116        }
117    }
118}
119
120impl std::fmt::Display for DCSwitch<'_> {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "switch")?;
123
124        if let Some(name) = &self.name {
125            write!(f, " {}", name)?;
126        }
127        write!(f, " (")?;
128        f.write_str(&self.key.to_string())?;
129
130        writeln!(f, ") {{")?;
131
132        for case in &self.cases {
133            f.write_str(&case.to_string())?;
134        }
135        writeln!(f, "}};")
136    }
137}
138
139impl LegacyDCHash for DCSwitch<'_> {
140    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
141        if let Some(name) = self.get_name() {
142            hashgen.add_string(name)
143        }
144
145        self.key.generate_hash(hashgen);
146
147        hashgen.add_int(self.get_num_cases() as i32);
148
149        for case in &self.cases {
150            case.generate_hash(hashgen);
151        }
152
153        if let Some(default) = &self.default_case {
154            default.generate_hash(hashgen);
155        }
156    }
157}
158
159impl<'dc> DCSwitch<'dc> {
160    /// Returns the optional identifier for this switch.
161    #[inline(always)]
162    pub fn get_name(&self) -> Option<String> {
163        self.name.clone()
164    }
165
166    /// Returns the key parameter on which the switch is based.
167    ///
168    /// The value of this parameter in the record determines which
169    /// one of the several cases within the switch will be used.
170    #[inline(always)]
171    pub fn get_key_parameter(&self) -> &'dc DCField {
172        &self.key
173    }
174
175    /// Returns the number of different cases within the switch.
176    ///
177    /// The legal values for case_index range from 0 to n_cases - 1.
178    #[inline(always)]
179    pub fn get_num_cases(&self) -> usize {
180        self.cases.len()
181    }
182
183    /// Returns case reference from given index wrapped in an Option.
184    pub fn get_case(&self, index: usize) -> Option<&'dc SwitchCase> {
185        self.cases.get(index)
186    }
187
188    /// Returns default case reference wrapped in an Option.
189    ///
190    /// A default case is optional, so `None` can be returned.
191    pub fn get_default_case(&self) -> Option<&'dc SwitchCase> {
192        self.default_case.as_ref()
193    }
194
195    /// Returns the index of the case with the given packed value.
196    ///
197    /// `None` is returned if no case with that value is found.
198    pub fn get_case_index_by_value(&self, value: Vec<u8>) -> Option<usize> {
199        self.cases_by_value.get(&value).copied()
200    }
201
202    // TODO
203    pub fn apply_switch(&self, _value: Vec<u8>, _length: usize) {}
204}
205
206/// Contains intermediate DC Switch structure and logic
207/// for semantic analysis as the DC Switch is being built.
208pub(crate) mod interim {
209    #[derive(Debug)]
210    pub struct SwitchCase {
211        pub breaks: bool,
212        pub value: Vec<u8>,
213    }
214
215    impl SwitchCase {
216        fn add_case_field(&mut self) {}
217    }
218
219    #[derive(Debug)]
220    pub struct DCSwitch {
221        pub name: Option<String>,
222        pub key_parameter: u8, // TODO
223    }
224
225    impl DCSwitch {
226        fn add_case(&mut self) {}
227    }
228}