dbc-rs 0.4.2

Database CAN (DBC) parsing and editing library
Documentation
use super::SignalBuilder;
use super::{ByteOrder, ReceiversBuilder};
use crate::{builder_setter, builder_string_setter};

impl SignalBuilder {
    /// Creates a new `SignalBuilder` with no fields set.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use dbc_rs::SignalBuilder;
    ///
    /// let builder = SignalBuilder::new();
    /// // Must set name, start_bit, length, and byte_order before building
    /// # Ok::<(), dbc_rs::Error>(())
    /// ```
    pub fn new() -> Self {
        Self {
            name: None,
            start_bit: None,
            length: None,
            byte_order: None,
            unsigned: None,
            factor: None,
            offset: None,
            min: None,
            max: None,
            unit: None,
            receivers: ReceiversBuilder::new(),
            comment: None,
        }
    }

    // String field setters generated by macros
    builder_string_setter!(name, "Sets the signal name.");
    builder_string_setter!(
        unit,
        "Sets the unit of measurement for this signal (e.g., \"km/h\", \"rpm\", \"°C\")."
    );
    builder_string_setter!(
        comment,
        "Sets the comment text for this signal (from CM_ SG_ entry)."
    );

    // Scalar field setters generated by macros
    builder_setter!(
        start_bit,
        u16,
        "Sets the start bit position of the signal in the CAN message payload."
    );
    builder_setter!(length, u16, "Sets the length of the signal in bits.");
    builder_setter!(
        byte_order,
        ByteOrder,
        "Sets the byte order (endianness) of the signal."
    );
    builder_setter!(
        unsigned,
        bool,
        "Sets whether the signal is unsigned (`true`) or signed (`false`)."
    );
    builder_setter!(
        factor,
        f64,
        "Sets the scaling factor for converting raw values to physical values (physical = raw * factor + offset)."
    );
    builder_setter!(
        offset,
        f64,
        "Sets the offset for converting raw values to physical values (physical = raw * factor + offset)."
    );
    builder_setter!(min, f64, "Sets the minimum physical value for this signal.");
    builder_setter!(max, f64, "Sets the maximum physical value for this signal.");

    /// Sets the receivers (ECU nodes) that subscribe to this signal.
    ///
    /// # Arguments
    ///
    /// * `receivers` - A `ReceiversBuilder` specifying which nodes receive this signal
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use dbc_rs::{SignalBuilder, ReceiversBuilder};
    ///
    /// let receivers = ReceiversBuilder::new()
    ///     .add_node("ECU1")
    ///     .add_node("ECU2");
    ///
    /// let builder = SignalBuilder::new()
    ///     .receivers(receivers);
    /// # Ok::<(), dbc_rs::Error>(())
    /// ```
    #[must_use = "builder method returns modified builder"]
    pub fn receivers(mut self, receivers: ReceiversBuilder) -> Self {
        self.receivers = receivers;
        self
    }
}

impl Default for SignalBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn minimal_signal() -> SignalBuilder {
        SignalBuilder::new()
            .name("TestSignal")
            .start_bit(0)
            .length(8)
            .byte_order(ByteOrder::LittleEndian)
            .unsigned(true)
            .factor(1.0)
            .offset(0.0)
            .min(0.0)
            .max(255.0)
            .receivers(ReceiversBuilder::new().none())
    }

    #[test]
    fn test_signal_builder_minimal() {
        let signal = minimal_signal().build().unwrap();

        assert_eq!(signal.name(), "TestSignal");
        assert_eq!(signal.start_bit(), 0);
        assert_eq!(signal.length(), 8);
        assert_eq!(signal.byte_order(), ByteOrder::LittleEndian);
        assert!(signal.is_unsigned());
        assert_eq!(signal.factor(), 1.0);
        assert_eq!(signal.offset(), 0.0);
        assert_eq!(signal.min(), 0.0);
        assert_eq!(signal.max(), 255.0);
        assert!(signal.unit().is_none());
    }

    #[test]
    fn test_signal_builder_with_unit() {
        let signal = minimal_signal().unit("rpm").build().unwrap();
        assert_eq!(signal.unit(), Some("rpm"));
    }

    #[test]
    fn test_signal_builder_signed() {
        let signal = minimal_signal().unsigned(false).build().unwrap();
        assert!(!signal.is_unsigned());
    }

    #[test]
    fn test_signal_builder_big_endian() {
        let signal = minimal_signal().byte_order(ByteOrder::BigEndian).build().unwrap();
        assert_eq!(signal.byte_order(), ByteOrder::BigEndian);
    }

    #[test]
    fn test_signal_builder_with_receivers() {
        let signal = minimal_signal()
            .receivers(ReceiversBuilder::new().add_node("ECM").add_node("TCM"))
            .build()
            .unwrap();

        assert_eq!(signal.receivers().len(), 2);
    }

    #[test]
    fn test_signal_builder_with_comment() {
        let signal = minimal_signal()
            .comment("Engine speed calculated over 720 degrees")
            .build()
            .unwrap();

        assert_eq!(
            signal.comment(),
            Some("Engine speed calculated over 720 degrees")
        );
    }

    #[test]
    fn test_signal_builder_without_comment() {
        let signal = minimal_signal().build().unwrap();
        assert_eq!(signal.comment(), None);
    }
}