dbc_rs/signal/
std.rs

1use super::Signal;
2use std::{
3    fmt::{Display, Formatter, Result},
4    string::String,
5};
6
7impl Signal {
8    #[must_use = "return value should be used"]
9    pub fn to_dbc_string(&self) -> String {
10        let mut result = String::with_capacity(100); // Pre-allocate reasonable capacity
11
12        result.push_str(" SG_ ");
13        result.push_str(self.name());
14        result.push_str(" : ");
15        result.push_str(&self.start_bit().to_string());
16        result.push('|');
17        result.push_str(&self.length().to_string());
18        result.push('@');
19
20        // Byte order: 0 for BigEndian (Motorola), 1 for LittleEndian (Intel)
21        // Per Vector DBC spec v1.0.1: "Big endian is stored as '0', little endian is stored as '1'"
22        match self.byte_order() {
23            crate::ByteOrder::BigEndian => result.push('0'), // @0 = Big Endian (Motorola)
24            crate::ByteOrder::LittleEndian => result.push('1'), // @1 = Little Endian (Intel)
25        }
26
27        // Sign: + for unsigned, - for signed
28        if self.is_unsigned() {
29            result.push('+');
30        } else {
31            result.push('-');
32        }
33
34        // Factor and offset: (factor,offset)
35        result.push_str(" (");
36        use core::fmt::Write;
37        write!(result, "{}", self.factor()).unwrap();
38        result.push(',');
39        write!(result, "{}", self.offset()).unwrap();
40        result.push(')');
41
42        // Min and max: [min|max]
43        result.push_str(" [");
44        write!(result, "{}", self.min()).unwrap();
45        result.push('|');
46        write!(result, "{}", self.max()).unwrap();
47        result.push(']');
48
49        // Unit: "unit" or ""
50        result.push(' ');
51        if let Some(unit) = self.unit() {
52            result.push('"');
53            result.push_str(unit);
54            result.push('"');
55        } else {
56            result.push_str("\"\"");
57        }
58
59        // Receivers serialization delegated to Receivers::to_dbc_string()
60        result.push(' ');
61        result.push_str(&self.receivers().to_dbc_string());
62
63        result
64    }
65}
66
67impl Display for Signal {
68    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
69        write!(f, "{}", self.to_dbc_string())
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::Parser;
77
78    #[test]
79    fn test_signal_to_dbc_string_round_trip() {
80        // Test round-trip: parse -> to_dbc_string -> parse
81        // Note: DBC spec Section 9.5 says receivers are comma-separated
82        // Per spec, '*' is not valid - we use Vector__XXX for no specific receiver
83        let test_cases = vec![
84            (
85                // '*' is parsed as None, serialized as Vector__XXX per spec
86                r#"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *"#,
87                " SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" Vector__XXX",
88            ),
89            (
90                // Parser accepts space-separated (tool extension), serializer outputs comma-separated (per spec)
91                r#"SG_ Temperature : 16|8@1- (1,-40) [-40|215] "°C" TCM BCM"#,
92                " SG_ Temperature : 16|8@1- (1,-40) [-40|215] \"°C\" TCM,BCM",
93            ),
94            (
95                // '*' is parsed as None, serialized as Vector__XXX per spec
96                r#"SG_ Flag : 24|1@0+ (1,0) [0|1] "" *"#,
97                " SG_ Flag : 24|1@0+ (1,0) [0|1] \"\" Vector__XXX",
98            ),
99        ];
100
101        for (input_line, expected_output) in test_cases {
102            // Parse the signal
103            let mut parser = Parser::new(input_line.as_bytes()).unwrap();
104            let signal = Signal::parse(&mut parser).unwrap();
105
106            // Convert to DBC string
107            let dbc_string = signal.to_dbc_string();
108            assert_eq!(dbc_string, expected_output);
109
110            // Round-trip: parse the output
111            let mut parser2 = Parser::new(dbc_string.as_bytes()).unwrap();
112            // Skip only the leading space, Signal::parse will handle SG_ keyword
113            parser2.skip_newlines_and_spaces();
114            let signal2 = Signal::parse(&mut parser2).unwrap();
115
116            // Verify round-trip
117            assert_eq!(signal.name(), signal2.name());
118            assert_eq!(signal.start_bit(), signal2.start_bit());
119            assert_eq!(signal.length(), signal2.length());
120            assert_eq!(signal.byte_order(), signal2.byte_order());
121            assert_eq!(signal.is_unsigned(), signal2.is_unsigned());
122            assert_eq!(signal.factor(), signal2.factor());
123            assert_eq!(signal.offset(), signal2.offset());
124            assert_eq!(signal.min(), signal2.min());
125            assert_eq!(signal.max(), signal2.max());
126            assert_eq!(signal.unit(), signal2.unit());
127        }
128    }
129}