1use approx::relative_eq;
2
3pub trait TemperatureUnit {
5 fn celsius(&self) -> f32;
7 fn fahrenheit(&self) -> f32;
9}
10
11#[derive(Clone, Copy, Debug, Default)]
13pub struct Celsius(pub f32);
14
15impl TemperatureUnit for Celsius {
16 fn celsius(&self) -> f32 {
17 self.0
18 }
19
20 fn fahrenheit(&self) -> f32 {
21 convert_celsius_to_fahrenheit(self.0)
22 }
23}
24
25impl From<Fahrenheit> for Celsius {
26 fn from(value: Fahrenheit) -> Self {
27 Self(convert_fahrenheit_to_celsius(value.0))
28 }
29}
30
31impl PartialEq for Celsius {
32 fn eq(&self, other: &Self) -> bool {
33 relative_eq!(self.0, other.0, epsilon = 0.01)
34 }
35}
36
37#[derive(Clone, Copy, Debug, Default)]
39pub struct Fahrenheit(pub f32);
40
41impl TemperatureUnit for Fahrenheit {
42 fn celsius(&self) -> f32 {
43 convert_fahrenheit_to_celsius(self.0)
44 }
45
46 fn fahrenheit(&self) -> f32 {
47 self.0
48 }
49}
50
51impl From<Celsius> for Fahrenheit {
52 fn from(value: Celsius) -> Self {
53 Self(convert_celsius_to_fahrenheit(value.0))
54 }
55}
56
57impl PartialEq for Fahrenheit {
58 fn eq(&self, other: &Self) -> bool {
59 relative_eq!(self.0, other.0, epsilon = 0.01)
60 }
61}
62
63fn convert_celsius_to_fahrenheit(temperature: f32) -> f32 {
65 temperature * 1.8 + 32.0
66}
67
68fn convert_fahrenheit_to_celsius(temperature: f32) -> f32 {
70 (temperature - 32.0) * 0.55555
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use approx::assert_relative_eq;
77 use rstest::rstest;
78
79 #[rstest]
80 #[case(0.0, 32.0)]
81 #[case(15.73, 60.31)]
82 #[case(-7.49, 18.52)]
83 #[case(37.5, 99.5)]
84 fn test_celcius_to_fahrenheit_conversion(#[case] input: f32, #[case] expected_output: f32) {
85 assert_relative_eq!(
86 convert_celsius_to_fahrenheit(input),
87 expected_output,
88 epsilon = 0.01
89 );
90 }
91
92 #[rstest]
93 #[case(32.0, 0.0)]
94 #[case(60.31, 15.73)]
95 #[case(18.52, -7.49)]
96 #[case(99.5, 37.5)]
97 fn test_fahrenheit_to_celsius_conversion(#[case] input: f32, #[case] expected_output: f32) {
98 assert_relative_eq!(
99 convert_fahrenheit_to_celsius(input),
100 expected_output,
101 epsilon = 0.01
102 )
103 }
104
105 #[rstest]
106 #[case(0.0, 32.0)]
107 #[case(15.73, 60.31)]
108 #[case(-7.49, 18.52)]
109 #[case(37.5, 99.5)]
110 fn test_celsius(#[case] celsius: f32, #[case] expected_fahrenheit: f32) {
111 let temperature = Celsius(celsius);
112 assert_relative_eq!(temperature.celsius(), celsius, epsilon = f32::EPSILON);
113 assert_relative_eq!(
114 temperature.fahrenheit(),
115 expected_fahrenheit,
116 epsilon = 0.01
117 );
118 }
119
120 #[rstest]
121 #[case(32.0, 0.0)]
122 #[case(60.31, 15.73)]
123 #[case(18.52, -7.49)]
124 #[case(99.5, 37.5)]
125 fn test_fahrenheit(#[case] fahrenheit: f32, #[case] expected_celsius: f32) {
126 let temperature = Fahrenheit(fahrenheit);
127 assert_relative_eq!(temperature.fahrenheit(), fahrenheit, epsilon = f32::EPSILON);
128 assert_relative_eq!(temperature.celsius(), expected_celsius, epsilon = 0.01);
129 }
130
131 #[rstest]
132 #[case(0.0, 0.001)]
133 #[case(0.004, 0.0)]
134 #[case(15.73, 15.728)]
135 fn test_celsius_eq(#[case] a: f32, #[case] b: f32) {
136 assert_eq!(Celsius(a), Celsius(b));
137 }
138
139 #[rstest]
140 #[case(0.0, 10.3)]
141 #[case(0.0, 0.09)]
142 #[case(37.5, 38.9)]
143 fn test_celsius_ne(#[case] a: f32, #[case] b: f32) {
144 assert_ne!(Celsius(a), Celsius(b))
145 }
146
147 #[rstest]
148 #[case(32.0, 32.001)]
149 #[case(32.004, 32.0)]
150 #[case(60.31, 60.308)]
151 fn test_fahrenheit_eq(#[case] a: f32, #[case] b: f32) {
152 assert_eq!(Fahrenheit(a), Fahrenheit(b));
153 }
154
155 #[rstest]
156 #[case(0.0, 10.3)]
157 #[case(0.0, 0.09)]
158 #[case(99.5, 100.9)]
159 fn test_fahrenheit_ne(#[case] a: f32, #[case] b: f32) {
160 assert_ne!(Fahrenheit(a), Fahrenheit(b))
161 }
162}