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}