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}