can_dbc/ast/
environment_variable.rs

1use can_dbc_pest::{Pair, Rule};
2
3use crate::ast::{AccessNode, AccessType, EnvType};
4use crate::parser::{
5    collect_expected, expect_empty, inner_str, next, next_optional_rule, next_rule, parse_int,
6    parse_min_max_int, single_inner_str, validated_inner, DbcError,
7};
8
9#[derive(Clone, Debug, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct EnvironmentVariable {
12    pub name: String,
13    pub typ: EnvType,
14    pub min: i64,
15    pub max: i64,
16    pub unit: String,
17    pub initial_value: i64,
18    pub ev_id: i64,
19    pub access_type: AccessType,
20    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
21    pub access_nodes: Vec<AccessNode>,
22}
23
24impl TryFrom<Pair<'_, Rule>> for EnvironmentVariable {
25    type Error = DbcError;
26
27    /// Parse environment variable: `EV_ variable_name : type [min|max] "unit" access_type access_node node_name1 node_name2;`
28    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
29        // 1) Validate wrapper and get iterator
30        let mut pairs = validated_inner(value, Rule::environment_variable)?;
31
32        // 2) Required: env_var (wrapper containing env_var_name)
33        let name = single_inner_str(next_rule(&mut pairs, Rule::env_var)?, Rule::env_var_name)?;
34
35        // 3) Required: env var type (one of three rules)
36        let typ = next(&mut pairs)?.as_rule().try_into()?;
37
38        // 4) Optional: min_max
39        let (mut min, mut max) = (0i64, 0i64);
40        if let Some(min_max_pair) = next_optional_rule(&mut pairs, Rule::min_max) {
41            (min, max) = parse_min_max_int(min_max_pair)?;
42        }
43
44        // 5) Optional: unit
45        let mut unit = String::new();
46        if let Some(unit_pair) = next_optional_rule(&mut pairs, Rule::unit) {
47            unit = inner_str(unit_pair);
48        }
49
50        // 6) Optional: init_value
51        let mut initial_value = 0;
52        if let Some(init_pair) = next_optional_rule(&mut pairs, Rule::init_value) {
53            initial_value = parse_int(&init_pair)?;
54        }
55
56        // 7) Optional: ev_id
57        let mut ev_id = 0i64;
58        if let Some(ev_pair) = next_optional_rule(&mut pairs, Rule::ev_id) {
59            ev_id = parse_int(&ev_pair)?;
60        }
61
62        // 8) Required: access_type
63        let access_type = next_rule(&mut pairs, Rule::access_type)?.try_into()?;
64
65        // 9) Remaining: zero or more node_name entries -> collect into AccessNode
66        let access_nodes = collect_expected::<AccessNode>(&mut pairs, Rule::node_name)?;
67
68        expect_empty(&pairs)?;
69
70        Ok(Self {
71            name,
72            typ,
73            min,
74            max,
75            unit,
76            initial_value,
77            ev_id,
78            access_type,
79            access_nodes,
80        })
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::test_helpers::*;
88
89    #[test]
90    fn environment_variable_test() {
91        let def = r#"
92EV_ IUV: 0 [-22|20] "mm" 3 7 DUMMY_NODE_VECTOR0 VECTOR_XXX;
93"#;
94        let exp = EnvironmentVariable {
95            name: "IUV".to_string(),
96            typ: EnvType::Integer,
97            min: -22,
98            max: 20,
99            unit: "mm".to_string(),
100            initial_value: 3,
101            ev_id: 7,
102            access_type: AccessType::DummyNodeVector0,
103            access_nodes: vec![AccessNode::Name("VECTOR_XXX".to_string())],
104        };
105        let val = test_into::<EnvironmentVariable>(def.trim_start(), Rule::environment_variable);
106        assert_eq!(val, exp);
107    }
108}