simple_version/lib.rs
1#![no_std]
2
3
4#[cfg(test)]
5mod tests;
6
7
8/// A struct that represents a version consisting of major, minor, patch, and an optional build number.
9///
10/// The generic parameter `T` specifies the numerical type to use for each version component.
11///
12/// ***
13/// # Examples
14///
15/// Creating a version without a build number:
16///
17/// ```rust
18/// use simple_version::Version;
19///
20/// let version: Version<u32> = Version::new(1, 2, 3);
21/// ```
22///
23/// ***
24///
25/// Creating a version with a build number:
26///
27/// ```rust
28/// use simple_version::Version;
29///
30/// let version: Version<u32> = Version::new(1, 2, 3).build(4);
31/// ```
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
33pub struct Version<T: Ord> {
34 /// The major version component.
35 ///
36 /// ***
37 /// # Examples
38 ///
39 /// ```rust
40 /// use simple_version::Version;
41 ///
42 /// let mut version = Version::<u32>::new(1, 2, 3);
43 ///
44 /// // read major version
45 /// println!("{}", version.major);
46 ///
47 /// // set major version
48 /// version.major = 4;
49 /// ```
50 pub major: T,
51
52 /// The minor version component.
53 ///
54 /// ***
55 /// # Examples
56 ///
57 /// ```rust
58 /// use simple_version::Version;
59 ///
60 /// let mut version = Version::<u32>::new(1, 2, 3);
61 ///
62 /// // read minor version
63 /// println!("{}", version.minor);
64 ///
65 /// // set minor version
66 /// version.minor = 4;
67 /// ```
68 pub minor: T,
69
70 /// The patch version component.
71 ///
72 /// ***
73 /// # Examples
74 ///
75 /// ```rust
76 /// use simple_version::Version;
77 ///
78 /// let mut version = Version::<u32>::new(1, 2, 3);
79 ///
80 /// // read patch version
81 /// println!("{}", version.patch);
82 ///
83 /// // set patch version
84 /// version.patch = 4;
85 /// ```
86 pub patch: T,
87
88 /// The optional build number.
89 ///
90 /// ***
91 /// # Examples
92 ///
93 /// ```rust
94 /// use simple_version::Version;
95 ///
96 /// // version with a build number
97 /// let mut version = Version::<u32>::new(1, 2, 3).build(4);
98 ///
99 /// // read build number
100 /// if let Some(build) = version.build {
101 /// println!("{}", build);
102 /// }
103 ///
104 /// // set build number
105 /// version.build = Some(5);
106 /// ```
107 pub build: Option<T>,
108}
109
110
111impl<T: Ord> Version<T> {
112 /// Creates a new `Version<T>` without a build number.
113 ///
114 /// ***
115 /// # Arguments
116 ///
117 /// - `major`: the major version component
118 /// - `minor`: the minor version component
119 /// - `patch`: the patch version component
120 ///
121 /// ***
122 /// # Examples
123 ///
124 /// ```rust
125 /// use simple_version::Version;
126 ///
127 /// let version: Version<u32> = Version::new(1, 2, 3);
128 /// ```
129 pub fn new(major: T, minor: T, patch: T) -> Version<T> {
130 Version { major, minor, patch, build: None }
131 }
132
133 /// Adds a build number to the existing version object and returns it.
134 ///
135 /// ***
136 /// # Arguments
137 ///
138 /// - `build`: the build number
139 ///
140 /// ***
141 /// # Examples
142 ///
143 /// ```rust
144 /// use simple_version::Version;
145 ///
146 /// let version: Version<u32> = Version::new(1, 2, 3).build(4);
147 /// ```
148 pub fn build(mut self, build: T) -> Version<T> {
149 self.build = Some(build);
150 self
151 }
152}
153
154
155impl<T: Ord + core::str::FromStr> Version<T> {
156 /// Creates a `Version<T>` from a string in the `major.minor.patch[+build]` format.
157 ///
158 /// Returns `None` if the string does not contain exactly three dot-separated segments
159 /// for the core version or if any component fails to parse into `T`.
160 ///
161 /// ***
162 /// # Examples
163 ///
164 /// ```rust
165 /// use simple_version::Version;
166 ///
167 /// let version = Version::<u32>::from_string("1.2.3").unwrap();
168 /// assert_eq!(version, Version::new(1, 2, 3));
169 ///
170 /// let version_with_build = Version::<u32>::from_string("1.2.3+4").unwrap();
171 /// assert_eq!(version_with_build, Version::new(1, 2, 3).build(4));
172 /// ```
173 pub fn from_string(version: &str) -> Option<Self> {
174 let (core_parts, build_part) = match version.split_once('+') {
175 Some((core, build)) => (core, Some(build)),
176 None => (version, None),
177 };
178
179 let mut iter = core_parts.split('.');
180 let major = iter.next()?.parse().ok()?;
181 let minor = iter.next()?.parse().ok()?;
182 let patch = iter.next()?.parse().ok()?;
183
184 // Ensure there are exactly three core components.
185 if iter.next().is_some() {
186 return None;
187 }
188
189 let build = match build_part {
190 Some(raw_build) => Some(raw_build.parse().ok()?),
191 None => None,
192 };
193
194 Some(Version { major, minor, patch, build })
195 }
196}
197
198
199impl<T: Ord + core::fmt::Display> core::fmt::Display for Version<T> {
200 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
201 if let Some(build) = &self.build {
202 write!(f, "{}.{}.{}+{}", self.major, self.minor, self.patch, build)
203 } else {
204 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
205 }
206 }
207}
208
209
210/// Creates a new `Version<T>` from the `CARGO_PKG_VERSION` environment variable
211/// at compile time (i.e., from your crate's `Cargo.toml`).
212///
213/// ***
214/// # Examples
215///
216/// ```rust
217/// use simple_version::{Version, version_from_pkg};
218///
219/// let version: Version<u32> = version_from_pkg!(u32);
220/// // Now `version` might be e.g. 1.2.3 if your crate's version is "1.2.3"
221/// ```
222#[macro_export]
223macro_rules! version_from_pkg {
224 ($t:ty) => {{
225 let _major: $t = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap();
226 let _minor: $t = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap();
227 let _patch: $t = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap();
228 $crate::Version::new(_major, _minor, _patch)
229 }};
230}