unity_version/version/
release_type.rs

1use serde::{Deserialize, Serialize};
2use std::{cmp::Ordering, fmt, str::FromStr};
3use thiserror::Error;
4
5#[derive(PartialEq, Eq, Ord, Hash, Debug, Clone, Copy, Serialize, Deserialize)]
6pub enum ReleaseType {
7    Alpha,
8    Beta,
9    Patch,
10    Final,
11}
12
13impl PartialOrd for ReleaseType {
14    fn partial_cmp(&self, other: &ReleaseType) -> Option<Ordering> {
15        Some(self.cmp(other))
16    }
17}
18
19impl fmt::Display for ReleaseType {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        if f.alternate() {
22            match *self {
23                ReleaseType::Final => write!(f, "final"),
24                ReleaseType::Patch => write!(f, "patch"),
25                ReleaseType::Beta => write!(f, "beta"),
26                ReleaseType::Alpha => write!(f, "alpha"),
27            }
28        } else {
29            match *self {
30                ReleaseType::Final => write!(f, "f"),
31                ReleaseType::Patch => write!(f, "p"),
32                ReleaseType::Beta => write!(f, "b"),
33                ReleaseType::Alpha => write!(f, "a"),
34            }
35        }
36    }
37}
38
39impl Default for ReleaseType {
40    fn default() -> ReleaseType {
41        ReleaseType::Final
42    }
43}
44
45#[derive(Debug, Error, PartialEq)]
46pub enum ReleaseTypeError {
47    #[error("Unknown release type: {0}")]
48    UnknownType(String),
49
50    #[error("Invalid release type character: {0}")]
51    InvalidCharacter(char),
52}
53
54impl FromStr for ReleaseType {
55    type Err = ReleaseTypeError;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        match s {
59            "a" => Ok(ReleaseType::Alpha),
60            "b" => Ok(ReleaseType::Beta),
61            "p" => Ok(ReleaseType::Patch),
62            "f" => Ok(ReleaseType::Final),
63            _ => Err(ReleaseTypeError::UnknownType(s.to_string())),
64        }
65    }
66}
67
68impl TryFrom<char> for ReleaseType {
69    type Error = ReleaseTypeError;
70
71    fn try_from(value: char) -> Result<Self, Self::Error> {
72        match value {
73            'f' => Ok(ReleaseType::Final),
74            'b' => Ok(ReleaseType::Beta),
75            'a' => Ok(ReleaseType::Alpha),
76            'p' => Ok(ReleaseType::Patch),
77            _ => Err(ReleaseTypeError::InvalidCharacter(value)),
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    use proptest::prelude::*;
87
88    #[test]
89    fn should_return_final_when_given_f_as_input() {
90        let result = ReleaseType::try_from('f');
91        assert_eq!(result, Ok(ReleaseType::Final));
92    }
93    #[test]
94    fn should_return_alpha_when_given_a_as_input() {
95        let result = ReleaseType::try_from('a');
96        assert_eq!(result, Ok(ReleaseType::Alpha));
97    }
98    #[test]
99    fn should_return_beta_when_given_b_as_input() {
100        let result = ReleaseType::try_from('b');
101        assert_eq!(result, Ok(ReleaseType::Beta));
102    }
103    #[test]
104    fn should_return_patch_when_given_p_as_input() {
105        let result = ReleaseType::try_from('p');
106        assert_eq!(result, Ok(ReleaseType::Patch));
107    }
108    #[test]
109    fn should_return_error_when_given_empty_input() {
110        let result = ReleaseType::try_from(' ');
111        assert_eq!(result, Err(ReleaseTypeError::InvalidCharacter(' ')));
112    }
113
114    #[test]
115    fn should_return_correct_to_string_value() {
116        assert_eq!(ReleaseType::Final.to_string(), "f");
117        assert_eq!(ReleaseType::Patch.to_string(), "p");
118        assert_eq!(ReleaseType::Alpha.to_string(), "a");
119        assert_eq!(ReleaseType::Beta.to_string(), "b");
120    }
121    #[test]
122    fn should_format_using_correct_alternative_format() {
123        assert_eq!(&format!("{:#}", ReleaseType::Final), "final");
124        assert_eq!(&format!("{:#}", ReleaseType::Patch), "patch");
125        assert_eq!(&format!("{:#}", ReleaseType::Beta), "beta");
126        assert_eq!(&format!("{:#}", ReleaseType::Alpha), "alpha");
127    }
128
129    proptest! {
130        #[test]
131        fn from_str_does_not_crash(s in "\\PC*") {
132            let _r = ReleaseType::from_str(&s);
133        }
134
135        #[test]
136        fn from_str_supports_all_valid_cases(s in "(a|b|f|p)") {
137            ReleaseType::from_str(&s).unwrap();
138        }
139    }
140}