dbc_rs/value_descriptions/builder/
build.rs

1use super::ValueDescriptionsBuilder;
2use crate::{
3    Error, MAX_NAME_SIZE, MAX_VALUE_DESCRIPTIONS, Result, ValueDescriptions, compat,
4    error::check_max_limit,
5};
6
7impl ValueDescriptionsBuilder {
8    /// Extracts and validates the required fields from the builder.
9    fn extract_fields(&self) -> Result<&[(u64, String)]> {
10        if self.entries.is_empty() {
11            return Err(Error::Validation(Error::VALUE_DESCRIPTIONS_EMPTY));
12        }
13
14        if let Some(err) = check_max_limit(
15            self.entries.len(),
16            MAX_VALUE_DESCRIPTIONS,
17            Error::Decoding(Error::VALUE_DESCRIPTIONS_TOO_MANY),
18        ) {
19            return Err(err);
20        }
21
22        Ok(&self.entries)
23    }
24
25    /// Validates the builder configuration without consuming it.
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if:
30    /// - The entry list is empty
31    /// - The number of entries exceeds the maximum allowed
32    ///
33    /// # Examples
34    ///
35    /// ```rust,no_run
36    /// use dbc_rs::ValueDescriptionsBuilder;
37    ///
38    /// let builder = ValueDescriptionsBuilder::new()
39    ///     .add_entry(0, "Off")
40    ///     .add_entry(1, "On")
41    ///     .validate()?;
42    /// # Ok::<(), dbc_rs::Error>(())
43    /// ```
44    #[must_use = "validation result should be checked"]
45    pub fn validate(self) -> Result<Self> {
46        self.extract_fields()?;
47        Ok(self)
48    }
49
50    /// Builds the `ValueDescriptions` from the builder.
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if:
55    /// - The entry list is empty
56    /// - The number of entries exceeds the maximum allowed
57    /// - Any description exceeds MAX_NAME_SIZE
58    ///
59    /// # Examples
60    ///
61    /// ```rust,no_run
62    /// use dbc_rs::ValueDescriptionsBuilder;
63    ///
64    /// let value_descriptions = ValueDescriptionsBuilder::new()
65    ///     .add_entry(0, "Park")
66    ///     .add_entry(1, "Drive")
67    ///     .build()?;
68    /// # Ok::<(), dbc_rs::Error>(())
69    /// ```
70    pub fn build(self) -> Result<ValueDescriptions> {
71        let entries = self.extract_fields()?;
72
73        // Convert std types to compat types
74        let mut compat_entries: compat::Vec<
75            (u64, compat::String<{ MAX_NAME_SIZE }>),
76            { MAX_VALUE_DESCRIPTIONS },
77        > = compat::Vec::new();
78
79        for (value, desc) in entries {
80            let compat_desc = compat::String::try_from(desc.as_str())
81                .map_err(|_| Error::Validation(Error::MAX_NAME_SIZE_EXCEEDED))?;
82            compat_entries
83                .push((*value, compat_desc))
84                .map_err(|_| Error::Validation(Error::VALUE_DESCRIPTIONS_TOO_MANY))?;
85        }
86
87        Ok(ValueDescriptions::new(compat_entries))
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn test_builder_new() {
97        let builder = ValueDescriptionsBuilder::new();
98        // Empty builder should fail validation
99        assert!(builder.build().is_err());
100    }
101
102    #[test]
103    fn test_builder_default() {
104        let builder = ValueDescriptionsBuilder::default();
105        // Empty builder should fail validation
106        assert!(builder.build().is_err());
107    }
108
109    #[test]
110    fn test_builder_empty_fails() {
111        let result = ValueDescriptionsBuilder::new().build();
112        assert!(result.is_err());
113    }
114
115    #[test]
116    fn test_builder_validate_empty_fails() {
117        let result = ValueDescriptionsBuilder::new().validate();
118        assert!(result.is_err());
119    }
120
121    #[test]
122    fn test_builder_validate_valid() {
123        let result = ValueDescriptionsBuilder::new().add_entry(0, "Off").validate();
124        assert!(result.is_ok());
125    }
126
127    #[test]
128    fn test_builder_at_max_limit() {
129        let mut builder = ValueDescriptionsBuilder::new();
130        for i in 0..MAX_VALUE_DESCRIPTIONS {
131            builder = builder.add_entry(i as u64, format!("Value{}", i));
132        }
133
134        let vd = builder.build().unwrap();
135        assert_eq!(vd.len(), MAX_VALUE_DESCRIPTIONS);
136    }
137
138    #[test]
139    fn test_builder_ignores_entries_over_limit() {
140        let mut builder = ValueDescriptionsBuilder::new();
141        // Add exactly MAX_VALUE_DESCRIPTIONS + 1 entries
142        for i in 0..=MAX_VALUE_DESCRIPTIONS {
143            builder = builder.add_entry(i as u64, format!("Value{}", i));
144        }
145
146        // Builder should silently ignore entries over the limit
147        let vd = builder.build().unwrap();
148        assert_eq!(vd.len(), MAX_VALUE_DESCRIPTIONS);
149    }
150
151    #[test]
152    fn test_builder_description_name_too_long() {
153        // Create a description that exceeds MAX_NAME_SIZE
154        let long_desc = "A".repeat(MAX_NAME_SIZE + 1);
155        let result = ValueDescriptionsBuilder::new().add_entry(0, long_desc).build();
156
157        assert!(result.is_err());
158    }
159
160    #[test]
161    fn test_builder_single_entry() {
162        let vd = ValueDescriptionsBuilder::new().add_entry(42, "Answer").build().unwrap();
163
164        assert_eq!(vd.len(), 1);
165        assert_eq!(vd.get(42), Some("Answer"));
166    }
167
168    #[test]
169    fn test_builder_clone() {
170        let builder = ValueDescriptionsBuilder::new().add_entry(0, "Off").add_entry(1, "On");
171
172        let cloned = builder.clone();
173        let vd1 = builder.build().unwrap();
174        let vd2 = cloned.build().unwrap();
175
176        assert_eq!(vd1.len(), vd2.len());
177    }
178}