dbc_rs/message/
message_builder.rs

1#[cfg(any(feature = "alloc", feature = "kernel"))]
2use crate::compat::{Box, String, Vec, str_to_string};
3use crate::{
4    error, {Error, Message, ParseOptions, Result, Signal},
5};
6
7#[cfg(any(feature = "alloc", feature = "kernel"))]
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(any(feature = "alloc", feature = "kernel"))]
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(str_to_string(name));
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(str_to_string(sender));
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.id.ok_or(Error::message(error::lang::MESSAGE_ID_REQUIRED))?;
73        let name = self.name.ok_or(Error::message(error::lang::MESSAGE_NAME_EMPTY))?;
74        let dlc = self.dlc.ok_or(Error::message(error::lang::MESSAGE_DLC_REQUIRED))?;
75        let sender = self.sender.ok_or(Error::message(error::lang::MESSAGE_SENDER_EMPTY))?;
76        Ok((id, name, dlc, sender, self.signals))
77    }
78
79    #[must_use = "validation result should be checked"]
80    pub fn validate(self) -> Result<Self> {
81        let (id, name, dlc, sender, signals) = self.extract_fields()?;
82        // Convert Vec to Option array for validation (all Some)
83        let signals_options: Vec<Option<Signal<'static>>> =
84            signals.iter().cloned().map(Some).collect();
85        let signals_options_slice: &[Option<Signal<'static>>] = &signals_options;
86        Message::validate_internal(
87            id,
88            &name,
89            dlc,
90            &sender,
91            signals_options_slice,
92            signals_options_slice.len(),
93            ParseOptions::new(), // Builder always uses strict mode
94        )
95        .map_err(Error::from)?;
96        Ok(Self {
97            id: Some(id),
98            name: Some(name),
99            dlc: Some(dlc),
100            sender: Some(sender),
101            signals,
102        })
103    }
104
105    pub fn build(self) -> Result<Message<'static>> {
106        let (id, name, dlc, sender, signals) = self.extract_fields()?;
107        // Convert Vec to Option array for validation (all Some)
108        let signals_options: Vec<Option<Signal<'static>>> =
109            signals.iter().cloned().map(Some).collect();
110        let signals_options_slice: &[Option<Signal<'static>>] = &signals_options;
111        // Validate before construction
112        Message::validate_internal(
113            id,
114            &name,
115            dlc,
116            &sender,
117            signals_options_slice,
118            signals_options_slice.len(),
119            ParseOptions::new(), // Builder always uses strict mode
120        )
121        .map_err(|e| match e {
122            crate::error::ParseError::Message(msg) => Error::dbc(msg),
123            _ => Error::ParseError(e),
124        })?;
125        // Convert owned strings to static references by leaking Box<str>
126        // name and sender are already String types, so convert directly
127        let name_boxed: Box<str> = name.into_boxed_str();
128        let name_static: &'static str = Box::leak(name_boxed);
129        let sender_boxed: Box<str> = sender.into_boxed_str();
130        let sender_static: &'static str = Box::leak(sender_boxed);
131        // Convert Vec to slice and leak to get static lifetime
132        let signals_boxed: Box<[Signal<'static>]> = signals.into_boxed_slice();
133        let signals_static: &'static [Signal<'static>] = Box::leak(signals_boxed);
134        Ok(Message::new(
135            id,
136            name_static,
137            dlc,
138            sender_static,
139            signals_static,
140        ))
141    }
142}