sdcx/
sdc.rs

1use crate::errors::{SemanticError, ValidateError};
2use crate::parser::sdc_grammar_trait as grammar;
3use crate::sdc::util::Validate;
4use std::fmt;
5
6pub mod argument;
7pub mod command;
8pub mod sdc_error;
9pub mod util;
10pub use argument::Argument;
11pub use command::*;
12
13/// SDC
14#[derive(Clone, Debug, Default, PartialEq)]
15pub struct Sdc {
16    pub header: Vec<String>,
17    pub version: Option<SdcVersion>,
18    pub commands: Vec<Command>,
19}
20
21impl Sdc {
22    pub fn validate(&self, force_version: Option<SdcVersion>) -> Vec<ValidateError> {
23        let version = self.version.unwrap_or(SdcVersion::SDC2_1);
24        let version = force_version.unwrap_or(version);
25        let mut ret = vec![];
26        for command in &self.commands {
27            ret.append(&mut command.validate(version));
28        }
29        ret
30    }
31
32    pub fn normalize(&mut self) {
33        self.commands.sort()
34    }
35}
36
37impl fmt::Display for Sdc {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        for s in &self.header {
40            write!(f, "{}", s)?;
41        }
42        if let Some(version) = self.version {
43            writeln!(f, "{}", version)?;
44        }
45        for c in &self.commands {
46            writeln!(f, "{}", c)?;
47        }
48
49        Ok(())
50    }
51}
52
53impl TryFrom<&grammar::Source<'_>> for Sdc {
54    type Error = SemanticError;
55
56    fn try_from(value: &grammar::Source<'_>) -> Result<Self, SemanticError> {
57        let mut sdc = Sdc::default();
58        let mut is_header = true;
59        let mut is_first_command = true;
60        for source in &value.source_list {
61            match source.source_list_group.as_ref() {
62                grammar::SourceListGroup::CommandLine(x) => {
63                    is_header = false;
64                    let command = x.command_line.command.as_ref().try_into()?;
65
66                    match command {
67                        Command::Set(x) if x.variable_name.as_str() == "sdc_version" => {
68                            if is_first_command {
69                                if let Ok(sdc_version) = x.value.as_str().try_into() {
70                                    sdc.version = Some(sdc_version);
71                                } else {
72                                    return Err(SemanticError::UnknownVersion(
73                                        x.location().clone(),
74                                    ));
75                                }
76                            } else {
77                                return Err(SemanticError::SdcVersionPlacement(
78                                    x.location().clone(),
79                                ));
80                            }
81                        }
82                        _ => sdc.commands.push(command),
83                    }
84
85                    if is_first_command {
86                        is_first_command = false;
87                    }
88                }
89                grammar::SourceListGroup::TermComment(x) => {
90                    if is_header {
91                        sdc.header.push(x.term_comment.term_comment.text().into());
92                    }
93                }
94                _ => (),
95            }
96        }
97        Ok(sdc)
98    }
99}
100
101/// SDC version
102#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
103pub enum SdcVersion {
104    SDC1_1 = 0,
105    SDC1_2 = 1,
106    SDC1_3 = 2,
107    SDC1_4 = 3,
108    SDC1_5 = 4,
109    SDC1_6 = 5,
110    SDC1_7 = 6,
111    SDC1_8 = 7,
112    SDC1_9 = 8,
113    SDC2_0 = 9,
114    SDC2_1 = 10,
115}
116
117impl SdcVersion {
118    pub fn within(&self, from: SdcVersion, to: SdcVersion) -> (bool, SdcVersion) {
119        (&from <= self && self <= &to, *self)
120    }
121
122    pub fn version_string(&self) -> &str {
123        match self {
124            SdcVersion::SDC1_1 => "1.1",
125            SdcVersion::SDC1_2 => "1.2",
126            SdcVersion::SDC1_3 => "1.3",
127            SdcVersion::SDC1_4 => "1.4",
128            SdcVersion::SDC1_5 => "1.5",
129            SdcVersion::SDC1_6 => "1.6",
130            SdcVersion::SDC1_7 => "1.7",
131            SdcVersion::SDC1_8 => "1.8",
132            SdcVersion::SDC1_9 => "1.9",
133            SdcVersion::SDC2_0 => "2.0",
134            SdcVersion::SDC2_1 => "2.1",
135        }
136    }
137}
138
139impl fmt::Display for SdcVersion {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            SdcVersion::SDC1_1 => "set sdc_version 1.1".fmt(f),
143            SdcVersion::SDC1_2 => "set sdc_version 1.2".fmt(f),
144            SdcVersion::SDC1_3 => "set sdc_version 1.3".fmt(f),
145            SdcVersion::SDC1_4 => "set sdc_version 1.4".fmt(f),
146            SdcVersion::SDC1_5 => "set sdc_version 1.5".fmt(f),
147            SdcVersion::SDC1_6 => "set sdc_version 1.6".fmt(f),
148            SdcVersion::SDC1_7 => "set sdc_version 1.7".fmt(f),
149            SdcVersion::SDC1_8 => "set sdc_version 1.8".fmt(f),
150            SdcVersion::SDC1_9 => "set sdc_version 1.9".fmt(f),
151            SdcVersion::SDC2_0 => "set sdc_version 2.0".fmt(f),
152            SdcVersion::SDC2_1 => "set sdc_version 2.1".fmt(f),
153        }
154    }
155}
156
157impl TryFrom<&str> for SdcVersion {
158    type Error = ();
159
160    fn try_from(value: &str) -> Result<Self, Self::Error> {
161        match value {
162            "1.1" => Ok(SdcVersion::SDC1_1),
163            "1.2" => Ok(SdcVersion::SDC1_2),
164            "1.3" => Ok(SdcVersion::SDC1_3),
165            "1.4" => Ok(SdcVersion::SDC1_4),
166            "1.5" => Ok(SdcVersion::SDC1_5),
167            "1.6" => Ok(SdcVersion::SDC1_6),
168            "1.7" => Ok(SdcVersion::SDC1_7),
169            "1.8" => Ok(SdcVersion::SDC1_8),
170            "1.9" => Ok(SdcVersion::SDC1_9),
171            "2.0" => Ok(SdcVersion::SDC2_0),
172            "2.1" => Ok(SdcVersion::SDC2_1),
173            _ => Err(()),
174        }
175    }
176}