dbc_rs/message/
message_builder.rs

1use crate::{
2    error::{Error, Result, messages},
3    message::Message,
4    signal::Signal,
5};
6
7#[cfg(feature = "alloc")]
8#[derive(Debug, Clone, Default)]
9pub struct MessageBuilder {
10    id: Option<u32>,
11    name: Option<String>,
12    dlc: Option<u8>,
13    sender: Option<String>,
14    signals: Vec<Signal<'static>>,
15}
16
17#[cfg(feature = "alloc")]
18impl MessageBuilder {
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    #[must_use]
24    pub fn id(mut self, id: u32) -> Self {
25        self.id = Some(id);
26        self
27    }
28
29    #[must_use]
30    pub fn name(mut self, name: impl AsRef<str>) -> Self {
31        self.name = Some(name.as_ref().to_string());
32        self
33    }
34
35    #[must_use]
36    pub fn dlc(mut self, dlc: u8) -> Self {
37        self.dlc = Some(dlc);
38        self
39    }
40
41    #[must_use]
42    pub fn sender(mut self, sender: impl AsRef<str>) -> Self {
43        self.sender = Some(sender.as_ref().to_string());
44        self
45    }
46
47    #[must_use]
48    pub fn add_signal(mut self, signal: Signal<'static>) -> Self {
49        self.signals.push(signal);
50        self
51    }
52
53    #[must_use]
54    pub fn add_signals(mut self, signals: impl IntoIterator<Item = Signal<'static>>) -> Self {
55        self.signals.extend(signals);
56        self
57    }
58
59    #[must_use]
60    pub fn signals(mut self, signals: Vec<Signal<'static>>) -> Self {
61        self.signals = signals;
62        self
63    }
64
65    #[must_use]
66    pub fn clear_signals(mut self) -> Self {
67        self.signals.clear();
68        self
69    }
70
71    fn extract_fields(self) -> Result<(u32, String, u8, String, Vec<Signal<'static>>)> {
72        let id = self
73            .id
74            .ok_or_else(|| Error::Message(messages::MESSAGE_ID_REQUIRED.to_string()))?;
75        let name = self
76            .name
77            .ok_or_else(|| Error::Message(messages::MESSAGE_NAME_EMPTY.to_string()))?;
78        let dlc = self
79            .dlc
80            .ok_or_else(|| Error::Message(messages::MESSAGE_DLC_REQUIRED.to_string()))?;
81        let sender = self
82            .sender
83            .ok_or_else(|| Error::Message(messages::MESSAGE_SENDER_EMPTY.to_string()))?;
84        Ok((id, name, dlc, sender, self.signals))
85    }
86
87    #[must_use = "validation result should be checked"]
88    pub fn validate(self) -> Result<Self> {
89        let (id, name, dlc, sender, signals) = self.extract_fields()?;
90        // Convert Vec to Option array for validation (all Some)
91        let signals_options: Vec<Option<Signal<'static>>> =
92            signals.iter().cloned().map(Some).collect();
93        let signals_options_slice: &[Option<Signal<'static>>] = &signals_options;
94        Message::validate_internal(
95            id,
96            &name,
97            dlc,
98            &sender,
99            signals_options_slice,
100            signals_options_slice.len(),
101            crate::ParseOptions::new(), // Builder always uses strict mode
102        )
103        .map_err(Error::from)?;
104        Ok(Self {
105            id: Some(id),
106            name: Some(name),
107            dlc: Some(dlc),
108            sender: Some(sender),
109            signals,
110        })
111    }
112
113    pub fn build(self) -> Result<Message<'static>> {
114        let (id, name, dlc, sender, signals) = self.extract_fields()?;
115        // Convert Vec to Option array for validation (all Some)
116        let signals_options: Vec<Option<Signal<'static>>> =
117            signals.iter().cloned().map(Some).collect();
118        let signals_options_slice: &[Option<Signal<'static>>] = &signals_options;
119        // Validate before construction
120        Message::validate_internal(
121            id,
122            &name,
123            dlc,
124            &sender,
125            signals_options_slice,
126            signals_options_slice.len(),
127            crate::ParseOptions::new(), // Builder always uses strict mode
128        )
129        .map_err(|e| match e {
130            crate::error::ParseError::Version(msg) => Error::Dbc(String::from(msg)),
131            _ => Error::ParseError(e),
132        })?;
133        // Convert owned strings to static references by leaking Box<str>
134        let name_boxed: Box<str> = name.into_boxed_str();
135        let name_static: &'static str = Box::leak(name_boxed);
136        let sender_boxed: Box<str> = sender.into_boxed_str();
137        let sender_static: &'static str = Box::leak(sender_boxed);
138        // Convert Vec to slice and leak to get static lifetime
139        let signals_boxed: Box<[Signal<'static>]> = signals.into_boxed_slice();
140        let signals_static: &'static [Signal<'static>] = Box::leak(signals_boxed);
141        Ok(Message::new(
142            id,
143            name_static,
144            dlc,
145            sender_static,
146            signals_static,
147        ))
148    }
149}