dbc_rs/dbc/builder/core.rs
1use super::DbcBuilder;
2use crate::{
3 Dbc, MessageBuilder, NodesBuilder, Receivers, ReceiversBuilder, SignalBuilder,
4 ValueDescriptionsBuilder, VersionBuilder,
5};
6use std::collections::BTreeMap;
7
8impl DbcBuilder {
9 /// Creates a new empty `DbcBuilder`.
10 ///
11 /// # Examples
12 ///
13 /// ```rust,no_run
14 /// use dbc_rs::{DbcBuilder, VersionBuilder, NodesBuilder, MessageBuilder};
15 ///
16 /// let dbc = DbcBuilder::new()
17 /// .version(VersionBuilder::new().version("1.0"))
18 /// .nodes(NodesBuilder::new().add_node("ECM"))
19 /// .add_message(MessageBuilder::new()
20 /// .id(512)
21 /// .name("Brake")
22 /// .dlc(4)
23 /// .sender("ECM"))
24 /// .build()?;
25 /// # Ok::<(), dbc_rs::Error>(())
26 /// ```
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 /// Creates a `DbcBuilder` from an existing `Dbc`.
32 ///
33 /// This allows you to modify an existing DBC file by creating a builder
34 /// initialized with all data from the provided DBC.
35 ///
36 /// # Arguments
37 ///
38 /// * `dbc` - The existing `Dbc` to create a builder from
39 ///
40 /// # Examples
41 ///
42 /// ```rust,no_run
43 /// use dbc_rs::{Dbc, DbcBuilder, MessageBuilder};
44 ///
45 /// let original = Dbc::parse(r#"VERSION "1.0"\nBU_: ECM\n"#)?;
46 /// let modified = DbcBuilder::from_dbc(&original)
47 /// .add_message(MessageBuilder::new().id(256).name("Msg").dlc(8).sender("ECM"))
48 /// .build()?;
49 /// # Ok::<(), dbc_rs::Error>(())
50 /// ```
51 pub fn from_dbc(dbc: &Dbc) -> Self {
52 // Convert version to builder (store builder, not final type)
53 let version = if let Some(v) = dbc.version() {
54 VersionBuilder::new().version(v.as_str())
55 } else {
56 VersionBuilder::new()
57 };
58
59 // Convert nodes to builder (store builder, not final type)
60 // Note: We unwrap here because we're converting from a valid Dbc, so names should already fit MAX_NAME_SIZE
61 let nodes = {
62 let mut builder = NodesBuilder::new();
63 for node in dbc.nodes().iter() {
64 // Convert compat::String to std::string::String for the builder
65 let node_str = node.to_string();
66 // Should never fail for valid Dbc - unwrap is safe
67 builder = builder.add_node(node_str);
68 }
69 builder
70 };
71
72 // Convert messages to builders (store builders, not final types)
73 let messages: Vec<MessageBuilder> = dbc
74 .messages()
75 .iter()
76 .map(|msg| {
77 let mut msg_builder = MessageBuilder::new()
78 .id(msg.id())
79 .name(msg.name())
80 .dlc(msg.dlc())
81 .sender(msg.sender());
82
83 // Convert signals using SignalBuilder
84 for sig in msg.signals().iter() {
85 let mut sig_builder = SignalBuilder::new()
86 .name(sig.name())
87 .start_bit(sig.start_bit())
88 .length(sig.length())
89 .byte_order(sig.byte_order())
90 .unsigned(sig.is_unsigned())
91 .factor(sig.factor())
92 .offset(sig.offset())
93 .min(sig.min())
94 .max(sig.max());
95
96 if let Some(unit) = sig.unit() {
97 sig_builder = sig_builder.unit(unit);
98 }
99
100 // Convert receivers using ReceiversBuilder
101 let receivers_builder = match sig.receivers() {
102 Receivers::Broadcast => ReceiversBuilder::new().broadcast(),
103 Receivers::None => ReceiversBuilder::new().none(),
104 Receivers::Nodes(nodes) => {
105 let mut rb = ReceiversBuilder::new();
106 // nodes is Vec<String<{MAX_NAME_SIZE}>>, iterate directly
107 for receiver in nodes.iter() {
108 // receiver is &String<{MAX_NAME_SIZE}>, clone it
109 let receiver_str = receiver.clone();
110 // Should never fail for valid Dbc - unwrap is safe
111 rb = rb.add_node(receiver_str);
112 }
113 rb
114 }
115 };
116 sig_builder = sig_builder.receivers(receivers_builder);
117
118 msg_builder = msg_builder.add_signal(sig_builder);
119 }
120
121 msg_builder
122 })
123 .collect();
124
125 // Convert value descriptions from Dbc to builder format (store builders, not final types)
126 let mut value_descriptions: BTreeMap<(Option<u32>, String), ValueDescriptionsBuilder> =
127 BTreeMap::new();
128 for ((message_id, signal_name), vd) in dbc.value_descriptions().iter() {
129 // Store as String and ValueDescriptionsBuilder (no leak)
130 let mut builder = ValueDescriptionsBuilder::new();
131 for (value, desc) in vd.iter() {
132 builder = builder.add_entry(value, desc);
133 }
134 value_descriptions.insert((message_id, signal_name.to_string()), builder);
135 }
136
137 Self {
138 version,
139 nodes,
140 messages,
141 value_descriptions,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::DbcBuilder;
149 use crate::{Dbc, MessageBuilder};
150
151 #[test]
152 fn test_dbc_builder_from_dbc() {
153 // Parse an existing DBC
154 let dbc_content = r#"VERSION "1.0"
155
156BU_: ECM TCM
157
158BO_ 256 Engine : 8 ECM
159 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
160"#;
161 let original_dbc = Dbc::parse(dbc_content).unwrap();
162
163 // Create builder from existing DBC
164 let modified_dbc = DbcBuilder::from_dbc(&original_dbc)
165 .add_message(MessageBuilder::new().id(512).name("Brake").dlc(4).sender("TCM"))
166 .build()
167 .unwrap();
168
169 // Verify original data is preserved
170 assert_eq!(modified_dbc.version().map(|v| v.as_str()), Some("1.0"));
171 assert_eq!(modified_dbc.nodes().len(), 2);
172 assert!(modified_dbc.nodes().contains("ECM"));
173 assert!(modified_dbc.nodes().contains("TCM"));
174
175 // Verify original message is present
176 assert_eq!(modified_dbc.messages().len(), 2);
177 assert!(modified_dbc.messages().iter().any(|m| m.id() == 256));
178 assert!(modified_dbc.messages().iter().any(|m| m.id() == 512));
179
180 // Verify original message's signal is preserved
181 let engine_msg = modified_dbc.messages().iter().find(|m| m.id() == 256).unwrap();
182 assert_eq!(engine_msg.signals().len(), 1);
183 assert_eq!(engine_msg.signals().at(0).unwrap().name(), "RPM");
184 }
185
186 #[test]
187 fn test_dbc_builder_from_dbc_empty() {
188 // Parse a minimal DBC
189 let dbc_content = r#"VERSION "1.0"
190
191BU_:
192"#;
193 let original_dbc = Dbc::parse(dbc_content).unwrap();
194
195 // Create builder from existing DBC
196 let modified_dbc = DbcBuilder::from_dbc(&original_dbc)
197 .add_message(MessageBuilder::new().id(256).name("Test").dlc(8).sender("ECM"))
198 .build()
199 .unwrap();
200
201 // Verify version is preserved
202 assert_eq!(modified_dbc.version().map(|v| v.as_str()), Some("1.0"));
203 // Empty nodes are preserved
204 assert!(modified_dbc.nodes().is_empty());
205 // New message is added
206 assert_eq!(modified_dbc.messages().len(), 1);
207 }
208}