1use core::fmt;
5
6use crate::error::{Error, Result};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
36#[non_exhaustive]
37pub enum Kind {
38 #[default]
40 Plain,
41 AbsoluteTemperature,
43 TemperatureDifference,
45}
46
47impl Kind {
48 #[must_use]
50 pub const fn name(self) -> &'static str {
51 match self {
52 Self::Plain => "plain",
53 Self::AbsoluteTemperature => "absolute_temperature",
54 Self::TemperatureDifference => "temperature_difference",
55 }
56 }
57
58 #[must_use]
60 pub fn from_name(name: &str) -> Option<Self> {
61 match name {
62 "plain" => Some(Self::Plain),
63 "absolute_temperature" => Some(Self::AbsoluteTemperature),
64 "temperature_difference" => Some(Self::TemperatureDifference),
65 _ => None,
66 }
67 }
68
69 pub const fn add(self, rhs: Self) -> Result<Self> {
76 match (self, rhs) {
77 (Self::Plain, Self::Plain) => Ok(Self::Plain),
78 (Self::AbsoluteTemperature, Self::TemperatureDifference)
79 | (Self::TemperatureDifference, Self::AbsoluteTemperature) => {
80 Ok(Self::AbsoluteTemperature)
81 }
82 (Self::TemperatureDifference, Self::TemperatureDifference) => {
83 Ok(Self::TemperatureDifference)
84 }
85 _ => Err(Error::KindMismatch {
86 from: self,
87 to: rhs,
88 operation: "addition",
89 suggestions: Vec::new(),
90 }),
91 }
92 }
93
94 pub const fn sub(self, rhs: Self) -> Result<Self> {
101 match (self, rhs) {
102 (Self::Plain, Self::Plain) => Ok(Self::Plain),
103 (Self::AbsoluteTemperature, Self::AbsoluteTemperature)
104 | (Self::TemperatureDifference, Self::TemperatureDifference) => {
105 Ok(Self::TemperatureDifference)
106 }
107 (Self::AbsoluteTemperature, Self::TemperatureDifference) => {
108 Ok(Self::AbsoluteTemperature)
109 }
110 _ => Err(Error::KindMismatch {
111 from: self,
112 to: rhs,
113 operation: "subtraction",
114 suggestions: Vec::new(),
115 }),
116 }
117 }
118}
119
120impl fmt::Display for Kind {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str(self.name())
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131#[non_exhaustive]
132pub enum Shape {
133 Linear,
135 Affine,
137}
138
139impl Shape {
140 #[must_use]
142 pub const fn name(self) -> &'static str {
143 match self {
144 Self::Linear => "linear",
145 Self::Affine => "affine",
146 }
147 }
148
149 #[must_use]
151 pub fn from_name(name: &str) -> Option<Self> {
152 match name {
153 "linear" => Some(Self::Linear),
154 "affine" => Some(Self::Affine),
155 _ => None,
156 }
157 }
158}
159
160impl fmt::Display for Shape {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 f.write_str(self.name())
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
168#[non_exhaustive]
169pub enum Exactness {
170 Defined,
172 Measured,
174}
175
176impl Exactness {
177 #[must_use]
179 pub const fn name(self) -> &'static str {
180 match self {
181 Self::Defined => "defined",
182 Self::Measured => "measured",
183 }
184 }
185
186 #[must_use]
188 pub fn from_name(name: &str) -> Option<Self> {
189 match name {
190 "defined" => Some(Self::Defined),
191 "measured" => Some(Self::Measured),
192 _ => None,
193 }
194 }
195
196 #[must_use]
198 pub const fn combine(self, other: Self) -> Self {
199 match (self, other) {
200 (Self::Defined, Self::Defined) => Self::Defined,
201 _ => Self::Measured,
202 }
203 }
204}
205
206impl fmt::Display for Exactness {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.write_str(self.name())
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 const ALL: [Kind; 3] = [
217 Kind::Plain,
218 Kind::AbsoluteTemperature,
219 Kind::TemperatureDifference,
220 ];
221
222 #[test]
223 fn names_round_trip() {
224 for k in ALL {
225 assert_eq!(Kind::from_name(k.name()), Some(k));
226 }
227 assert_eq!(Kind::from_name("nope"), None);
228 assert_eq!(Shape::from_name("affine"), Some(Shape::Affine));
229 assert_eq!(Exactness::from_name("measured"), Some(Exactness::Measured));
230 }
231
232 #[test]
233 fn abs_minus_abs_is_diff() {
234 let (a, d) = (Kind::AbsoluteTemperature, Kind::TemperatureDifference);
235 assert_eq!(a.sub(a), Ok(d));
236 assert_eq!(a.add(d), Ok(a));
237 assert_eq!(d.add(a), Ok(a));
238 assert_eq!(a.sub(d), Ok(a));
239 assert_eq!(d.add(d), Ok(d));
240 assert_eq!(d.sub(d), Ok(d));
241 assert!(a.add(a).is_err());
242 assert!(d.sub(a).is_err());
243 }
244
245 #[test]
246 fn plain_does_not_mix_with_temperature() {
247 for k in [Kind::AbsoluteTemperature, Kind::TemperatureDifference] {
248 assert!(Kind::Plain.add(k).is_err());
249 assert!(k.add(Kind::Plain).is_err());
250 assert!(Kind::Plain.sub(k).is_err());
251 assert!(k.sub(Kind::Plain).is_err());
252 }
253 assert_eq!(Kind::Plain.add(Kind::Plain), Ok(Kind::Plain));
254 }
255
256 #[test]
257 fn exactness_combine() {
258 assert_eq!(
259 Exactness::Defined.combine(Exactness::Defined),
260 Exactness::Defined
261 );
262 assert_eq!(
263 Exactness::Defined.combine(Exactness::Measured),
264 Exactness::Measured
265 );
266 }
267}