dbc_rs/version/
version_builder.rs

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