app_version/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/app-version
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use std::fmt;
6use std::num::ParseIntError;
7use std::str::FromStr;
8
9/// A struct representing a semantic version.
10///
11/// This struct contains three components of a version: major, minor, and patch.
12/// It derives common traits for easy comparison and manipulation.
13///
14/// # Examples
15///
16/// Creating a new version:
17///
18/// ```
19/// use app_version::Version;
20///
21/// let version = Version::new(1,0,0);
22/// assert_eq!(version.major(), 1);
23/// assert_eq!(version.minor(), 0);
24/// assert_eq!(version.patch(), 0);
25/// ```
26///
27/// ## Comparison
28///
29/// Versions can be compared for equality:
30///
31/// ```
32/// use app_version::Version;
33///
34/// let version1 = Version::new(1,0,0);
35/// let version2 = Version::new(1,0,1);
36/// assert_ne!(version1, version2);
37/// ```
38#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
39pub struct Version {
40    major: u16,
41    minor: u16,
42    patch: u16,
43}
44
45impl Version {
46    /// Creates a new version.
47    ///
48    /// # Parameters
49    /// - `major`: The major version number.
50    /// - `minor`: The minor version number.
51    /// - `patch`: The patch version number.
52    ///
53    /// # Returns
54    /// A `Version` instance.
55    pub const fn new(major: u16, minor: u16, patch: u16) -> Self {
56        Self {
57            major,
58            minor,
59            patch,
60        }
61    }
62
63    /// Returns the major version number.
64    pub const fn major(&self) -> u16 {
65        self.major
66    }
67
68    /// Returns the minor version number.
69    pub const fn minor(&self) -> u16 {
70        self.minor
71    }
72
73    /// Returns the patch version number.
74    pub const fn patch(&self) -> u16 {
75        self.patch
76    }
77
78    /// Increments the patch version.
79    pub fn increment_patch(&mut self) {
80        self.patch += 1;
81    }
82
83    /// Increments the minor version and resets patch to 0.
84    pub fn increment_minor(&mut self) {
85        self.minor += 1;
86        self.patch = 0;
87    }
88
89    /// Increments the major version and resets minor and patch to 0.
90    pub fn increment_major(&mut self) {
91        self.major += 1;
92        self.minor = 0;
93        self.patch = 0;
94    }
95
96    /// Checks if the current version is compatible with another version.
97    ///
98    /// # Parameters
99    /// - `other`: The other version to check compatibility against.
100    ///
101    /// # Returns
102    /// `true` if the versions are compatible, otherwise `false`.
103    pub fn is_compatible(&self, other: &Self) -> bool {
104        self.major == other.major
105    }
106}
107
108// Implement the `fmt::Display` trait for `Version`
109impl fmt::Display for Version {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
112    }
113}
114
115impl From<(u16, u16, u16)> for Version {
116    fn from(tuple: (u16, u16, u16)) -> Self {
117        Version::new(tuple.0, tuple.1, tuple.2)
118    }
119}
120
121#[derive(Debug)]
122pub enum VersionError {
123    ParseIntError(ParseIntError),
124    InvalidFormat,
125}
126
127impl From<ParseIntError> for VersionError {
128    fn from(error: ParseIntError) -> Self {
129        VersionError::ParseIntError(error)
130    }
131}
132
133impl fmt::Display for VersionError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match self {
136            VersionError::InvalidFormat => write!(f, "Invalid version format"),
137            VersionError::ParseIntError(err) => write!(f, "Parse error: {}", err),
138        }
139    }
140}
141
142impl std::error::Error for VersionError {}
143
144impl FromStr for Version {
145    type Err = VersionError;
146
147    fn from_str(s: &str) -> Result<Self, Self::Err> {
148        let parts: Vec<&str> = s.split('.').collect();
149        if parts.len() != 3 {
150            return Err(VersionError::InvalidFormat);
151        }
152
153        let major = parts[0].parse::<u16>()?;
154        let minor = parts[1].parse::<u16>()?;
155        let patch = parts[2].parse::<u16>()?;
156
157        Ok(Version::new(major, minor, patch))
158    }
159}
160
161/// A trait that provides a version.
162///
163/// Implementers of this trait must define how to return the version
164/// associated with them. This is useful for encapsulating versioning
165/// logic within various components or libraries.
166///
167/// # Examples
168///
169/// A simple implementation of `VersionProvider`:
170///
171/// ```
172/// use app_version::{Version, VersionProvider};
173///
174/// struct MySoftware;
175///
176/// impl VersionProvider for MySoftware {
177///     fn version() -> Version {
178///         Version::new(1, 0, 0)
179///     }
180/// }
181///
182/// let my_version = MySoftware::version();
183/// assert_eq!(my_version, Version::new(1, 0, 0 ));
184/// ```
185
186pub trait VersionProvider {
187    fn version() -> Version;
188}