can_dbc/ast/
value_description.rs

1use can_dbc_pest::{Pair, Rule};
2
3use crate::ast::{MessageId, ValDescription};
4use crate::parser::{collect_expected, next_string, validated_inner, DbcError};
5
6/// Encoding for signal raw values.
7#[derive(Clone, Debug, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum ValueDescription {
10    Signal {
11        message_id: MessageId,
12        name: String,
13        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
14        value_descriptions: Vec<ValDescription>,
15    },
16    EnvironmentVariable {
17        name: String,
18        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
19        value_descriptions: Vec<ValDescription>,
20    },
21}
22
23impl TryFrom<Pair<'_, Rule>> for ValueDescription {
24    type Error = DbcError;
25
26    /// Parse value description: `VAL_ message_id signal_name value1 "description1" value2 "description2" ... ;`
27    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
28        let mut pairs = validated_inner(value, Rule::value_table_def)?;
29
30        // Check if first item is message_id (optional)
31        let mut message_id = None;
32        if let Some(first_pair) = pairs.next() {
33            if first_pair.as_rule() == Rule::message_id {
34                message_id = Some(first_pair.try_into()?);
35            } else {
36                // Put it back and treat as signal_name (environment variable case)
37                let name = first_pair.as_str().to_string();
38                let value_descriptions =
39                    collect_expected(&mut pairs, Rule::table_value_description)?;
40                return Ok(Self::EnvironmentVariable {
41                    name,
42                    value_descriptions,
43                });
44            }
45        }
46
47        let name = next_string(&mut pairs, Rule::signal_name)?;
48        let value_descriptions = collect_expected(&mut pairs, Rule::table_value_description)?;
49
50        if let Some(message_id) = message_id {
51            Ok(Self::Signal {
52                message_id,
53                name,
54                value_descriptions,
55            })
56        } else {
57            Ok(Self::EnvironmentVariable {
58                name,
59                value_descriptions,
60            })
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::test_helpers::*;
69
70    #[test]
71    fn value_description_for_signal_test() {
72        let def = r#"
73VAL_ 837 UF_HZ_OI 255 "NOP";
74"#;
75        let exp = ValueDescription::Signal {
76            message_id: MessageId::Standard(837),
77            name: "UF_HZ_OI".to_string(),
78            value_descriptions: vec![ValDescription {
79                id: 255,
80                description: "NOP".to_string(),
81            }],
82        };
83        let val = test_into::<ValueDescription>(def.trim_start(), Rule::value_table_def);
84        assert_eq!(val, exp);
85    }
86
87    #[test]
88    fn value_description_for_env_var_test() {
89        let def = r#"
90VAL_ MY_ENV_VAR 255 "NOP";
91"#;
92        let exp = ValueDescription::EnvironmentVariable {
93            name: "MY_ENV_VAR".to_string(),
94            value_descriptions: vec![ValDescription {
95                id: 255,
96                description: "NOP".to_string(),
97            }],
98        };
99        let val = test_into::<ValueDescription>(def.trim_start(), Rule::value_table_def);
100        assert_eq!(val, exp);
101    }
102}