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