dbc_rs/version/
version_builder.rs

1#[cfg(any(feature = "alloc", feature = "kernel"))]
2use crate::compat::{Box, String, display_to_string, format_two};
3use crate::{Error, Result, Version, error::messages};
4
5/// Builder for creating `Version` programmatically.
6///
7/// This builder allows you to construct version strings when building DBC files
8/// programmatically. You can either specify a complete version string or build
9/// it incrementally using semantic version components.
10///
11/// # Examples
12///
13/// ```rust,no_run
14/// use dbc_rs::VersionBuilder;
15///
16/// // Direct version string
17/// let version = VersionBuilder::new().version("1.0").build()?;
18///
19/// // Semantic versioning
20/// let version2 = VersionBuilder::new()
21///     .major(1)
22///     .minor(2)
23///     .patch(3)
24///     .build()?;
25/// assert_eq!(version2.as_str(), "1.2.3");
26/// # Ok::<(), dbc_rs::Error>(())
27/// ```
28///
29/// # Feature Requirements
30///
31/// This builder requires the `alloc` feature to be enabled.
32#[derive(Debug, Default)]
33pub struct VersionBuilder {
34    version: Option<String>,
35}
36
37impl VersionBuilder {
38    /// Creates a new `VersionBuilder` with no version set.
39    ///
40    /// # Examples
41    ///
42    /// ```rust,no_run
43    /// use dbc_rs::VersionBuilder;
44    ///
45    /// let builder = VersionBuilder::new();
46    /// // Must set version before building
47    /// let version = builder.version("1.0").build()?;
48    /// # Ok::<(), dbc_rs::Error>(())
49    /// ```
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Sets the complete version string.
55    ///
56    /// This method overrides any previously set version components (major, minor, patch).
57    ///
58    /// # Arguments
59    ///
60    /// * `version` - The complete version string (e.g., "1.0", "1.2.3", "1.0-beta")
61    ///
62    /// # Examples
63    ///
64    /// ```rust,no_run
65    /// use dbc_rs::VersionBuilder;
66    ///
67    /// let version = VersionBuilder::new()
68    ///     .version("1.2.3")
69    ///     .build()?;
70    /// assert_eq!(version.as_str(), "1.2.3");
71    ///
72    /// // Overrides previous components
73    /// let version2 = VersionBuilder::new()
74    ///     .major(1)
75    ///     .minor(2)
76    ///     .version("3.0") // Overrides previous
77    ///     .build()?;
78    /// assert_eq!(version2.as_str(), "3.0");
79    /// # Ok::<(), dbc_rs::Error>(())
80    /// ```
81    #[must_use]
82    pub fn version(mut self, version: &str) -> Self {
83        #[cfg(all(feature = "kernel", not(feature = "alloc")))]
84        {
85            self.version = Some(String::from(version));
86        }
87        #[cfg(all(feature = "alloc", not(feature = "kernel")))]
88        {
89            self.version = Some(crate::compat::str_to_string(version));
90        }
91        self
92    }
93
94    /// Sets the major version number.
95    ///
96    /// If `minor()` or `patch()` are called after this, they will append to the version string.
97    /// If `version()` is called after this, it will override this value.
98    ///
99    /// # Arguments
100    ///
101    /// * `major` - The major version number (0-255)
102    ///
103    /// # Examples
104    ///
105    /// ```rust,no_run
106    /// use dbc_rs::VersionBuilder;
107    ///
108    /// let version = VersionBuilder::new()
109    ///     .major(1)
110    ///     .build()?;
111    /// assert_eq!(version.as_str(), "1");
112    ///
113    /// // Combine with minor and patch
114    /// let version2 = VersionBuilder::new()
115    ///     .major(1)
116    ///     .minor(2)
117    ///     .patch(3)
118    ///     .build()?;
119    /// assert_eq!(version2.as_str(), "1.2.3");
120    /// # Ok::<(), dbc_rs::Error>(())
121    /// ```
122    #[must_use]
123    pub fn major(mut self, major: u8) -> Self {
124        self.version = Some(display_to_string(major));
125        self
126    }
127
128    /// Sets the minor version number.
129    ///
130    /// If a version string already exists (from `major()` or `version()`), this appends
131    /// the minor version. Otherwise, it creates a new version string with just the minor number.
132    ///
133    /// # Arguments
134    ///
135    /// * `minor` - The minor version number (0-255)
136    ///
137    /// # Examples
138    ///
139    /// ```rust,no_run
140    /// use dbc_rs::VersionBuilder;
141    ///
142    /// // With major
143    /// let version = VersionBuilder::new()
144    ///     .major(1)
145    ///     .minor(2)
146    ///     .build()?;
147    /// assert_eq!(version.as_str(), "1.2");
148    ///
149    /// // Without major (creates version "2")
150    /// let version2 = VersionBuilder::new()
151    ///     .minor(2)
152    ///     .build()?;
153    /// assert_eq!(version2.as_str(), "2");
154    /// # Ok::<(), dbc_rs::Error>(())
155    /// ```
156    #[must_use]
157    pub fn minor(mut self, minor: u8) -> Self {
158        if let Some(ref mut v) = self.version {
159            *v = format_two(v.as_str(), ".", minor);
160        } else {
161            // If major wasn't called, treat this as just the version string
162            self.version = Some(display_to_string(minor));
163        }
164        self
165    }
166
167    /// Sets the patch version number.
168    ///
169    /// If a version string already exists (from `major()`, `minor()`, or `version()`),
170    /// this appends the patch version. Otherwise, it creates a new version string with
171    /// just the patch number.
172    ///
173    /// # Arguments
174    ///
175    /// * `patch` - The patch version number (0-255)
176    ///
177    /// # Examples
178    ///
179    /// ```rust,no_run
180    /// use dbc_rs::VersionBuilder;
181    ///
182    /// // Full semantic version
183    /// let version = VersionBuilder::new()
184    ///     .major(1)
185    ///     .minor(2)
186    ///     .patch(3)
187    ///     .build()?;
188    /// assert_eq!(version.as_str(), "1.2.3");
189    ///
190    /// // Without major/minor (creates version "3")
191    /// let version2 = VersionBuilder::new()
192    ///     .patch(3)
193    ///     .build()?;
194    /// assert_eq!(version2.as_str(), "3");
195    /// # Ok::<(), dbc_rs::Error>(())
196    /// ```
197    #[must_use]
198    pub fn patch(mut self, patch: u8) -> Self {
199        if let Some(ref mut v) = self.version {
200            *v = format_two(v.as_str(), ".", patch);
201        } else {
202            // If major/minor weren't called, treat this as just the version string
203            self.version = Some(display_to_string(patch));
204        }
205        self
206    }
207
208    /// Builds the `Version` from the builder configuration.
209    ///
210    /// This validates that a version has been set and constructs a `Version` instance
211    /// with static lifetime.
212    ///
213    /// # Returns
214    ///
215    /// Returns `Ok(Version)` if successful, or `Err(Error::Version)` if:
216    /// - No version has been set (empty version)
217    ///
218    /// # Examples
219    ///
220    /// ```rust,no_run
221    /// use dbc_rs::VersionBuilder;
222    ///
223    /// // Build with version string
224    /// let version = VersionBuilder::new()
225    ///     .version("1.0")
226    ///     .build()?;
227    /// assert_eq!(version.as_str(), "1.0");
228    ///
229    /// // Build with semantic versioning
230    /// let version2 = VersionBuilder::new()
231    ///     .major(1)
232    ///     .minor(2)
233    ///     .patch(3)
234    ///     .build()?;
235    /// assert_eq!(version2.as_str(), "1.2.3");
236    /// # Ok::<(), dbc_rs::Error>(())
237    /// ```
238    ///
239    /// # Errors
240    ///
241    /// ```rust,no_run
242    /// use dbc_rs::VersionBuilder;
243    ///
244    /// // Missing version
245    /// let result = VersionBuilder::new().build();
246    /// assert!(result.is_err());
247    ///
248    /// // Empty string is allowed
249    /// let version = VersionBuilder::new().version("").build()?;
250    /// assert_eq!(version.as_str(), "");
251    /// # Ok::<(), dbc_rs::Error>(())
252    /// ```
253    pub fn build(self) -> Result<Version<'static>> {
254        let version = self.version.ok_or_else(|| {
255            Error::Version(crate::error::str_to_error_string(messages::VERSION_EMPTY))
256        })?;
257
258        // Convert owned String to static reference by leaking Box<str>
259        let boxed: Box<str> = version.into_boxed_str();
260        let static_ref: &'static str = Box::leak(boxed);
261        Ok(Version::new(static_ref))
262    }
263}
264
265#[cfg(all(test, feature = "alloc"))]
266mod tests {
267    use super::VersionBuilder;
268    use crate::{compat::ToString, error::lang};
269
270    #[test]
271    fn test_version_builder_version_string() {
272        let version = VersionBuilder::new().version("1.0").build().unwrap();
273        assert_eq!(version.to_string(), "1.0");
274    }
275
276    #[test]
277    fn test_version_builder_major_only() {
278        let version = VersionBuilder::new().major(1).build().unwrap();
279        assert_eq!(version.to_string(), "1");
280    }
281
282    #[test]
283    fn test_version_builder_major_minor() {
284        let version = VersionBuilder::new().major(1).minor(0).build().unwrap();
285        assert_eq!(version.to_string(), "1.0");
286    }
287
288    #[test]
289    fn test_version_builder_full() {
290        let version = VersionBuilder::new().major(1).minor(2).patch(3).build().unwrap();
291        assert_eq!(version.to_string(), "1.2.3");
292    }
293
294    #[test]
295    #[cfg(feature = "alloc")]
296    fn test_version_builder_missing_version() {
297        use crate::Error;
298
299        let result = VersionBuilder::new().build();
300        assert!(result.is_err());
301        match result.unwrap_err() {
302            Error::Version(msg) => assert!(msg.contains(lang::VERSION_EMPTY)),
303            _ => panic!("Expected Version error"),
304        }
305    }
306
307    #[test]
308    fn test_version_builder_with_special_chars() {
309        let version = VersionBuilder::new().version("1.0-beta").build().unwrap();
310        assert_eq!(version.to_string(), "1.0-beta");
311    }
312
313    #[test]
314    fn test_version_builder_minor_without_major() {
315        let version = VersionBuilder::new().minor(2).build().unwrap();
316        assert_eq!(version.to_string(), "2");
317    }
318
319    #[test]
320    fn test_version_builder_patch_without_major_minor() {
321        let version = VersionBuilder::new().patch(3).build().unwrap();
322        assert_eq!(version.to_string(), "3");
323    }
324
325    #[test]
326    fn test_version_builder_patch_without_major() {
327        let version = VersionBuilder::new().minor(2).patch(3).build().unwrap();
328        assert_eq!(version.to_string(), "2.3");
329    }
330
331    #[test]
332    fn test_version_builder_version_overrides_major() {
333        let version = VersionBuilder::new().major(1).version("2.0").build().unwrap();
334        assert_eq!(version.to_string(), "2.0");
335    }
336
337    #[test]
338    fn test_version_builder_version_overrides_major_minor() {
339        let version = VersionBuilder::new().major(1).minor(2).version("3.0").build().unwrap();
340        assert_eq!(version.to_string(), "3.0");
341    }
342
343    #[test]
344    fn test_version_builder_major_minor_patch_sequence() {
345        let version = VersionBuilder::new().major(1).minor(2).patch(3).build().unwrap();
346        assert_eq!(version.to_string(), "1.2.3");
347    }
348
349    #[test]
350    fn test_version_builder_empty_string() {
351        let version = VersionBuilder::new().version("").build().unwrap();
352        assert_eq!(version.to_string(), "");
353    }
354
355    #[test]
356    fn test_version_builder_long_version() {
357        let long_version = "1.2.3.4.5.6.7.8.9.10";
358        let version = VersionBuilder::new().version(long_version).build().unwrap();
359        assert_eq!(version.to_string(), long_version);
360    }
361}