msuk_scifi/unit/temperature.rs
1//! Generic temperature macros and "dispatching", etc.
2pub mod k;
3
4pub type TemperatureInternalType = f64;
5
6#[macro_export]
7/// Implement `as_FOOBAR`, `as_rounded_FOOBAR`, and `try_as_FOOBAR`
8/// for (e.g.) a temperature type.
9///
10/// # Arguments
11/// * `Temperature`-type.
12/// * `for`, followed by a comma separated list of e.g. primitives that are to be supported.
13///
14/// # Examples
15/// ```
16/// # use msuk_scifi::{implement_as_for_temperature, unit::temperature::TemperatureInternalType};
17/// # use paste::paste;
18/// # struct K { value: f64 };
19/// implement_as_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
20/// ```
21//
22macro_rules! implement_as_for_temperature {
23 ($c:ty, for $($t:ty),+) => {$(paste! {
24 impl $c {
25 /// Try convert [<$c>] into primitive [<$t>].
26 ///
27 /// # Returns
28 /// `None` if conversion is not possible, otherwise `Some([<$t>])`.
29 pub fn [<try_as_ $t>](&self) -> Option<$t> {
30 // Since K is always >= 0, we only need to check the MAX bound.
31 if self.value <= $t::MAX as TemperatureInternalType {
32 Some(self.value.round() as $t)
33 } else {
34 None
35 }
36 }
37
38 /// Performs a direct, truncating conversion from [<$c>] to [<$t>].
39 /// See `[<try_as_ $t>]` for a safe, but a tiny bit slower alternative.
40 ///
41 /// For rounding, use [<as_rounded_ $t>].
42 ///
43 /// WARNING: in case of [<$c>â] being too large for [<$t>], wrap on overflow will happen.
44 pub fn [<as_ $t>](&self) -> $t {
45 self.value as $t
46 }
47
48 /// Performs a direct, rounding coversion from [<$c>] to [<$t>].
49 ///
50 /// WARNING: in case of [<$c>] being too large for [<$t>], wrap on overflow will happen.
51 pub fn [<as_rounded_ $t>](&self) -> $t {
52 self.value.round() as $t
53 }
54 }
55 })+};
56}
57
58#[macro_export]
59/// Implement a number of `From<X>` for the given type.
60///
61/// # Implements
62/// * `From<X> for $type`
63/// * `From<$type> for X`
64///
65/// # Arguments
66/// * `$type`â some "temperature" type, e.g. `K` (for Kelvin).
67/// * `for`, followed by a comma separated list of e.g. primitives that are to be supported.
68///
69/// # Examples
70/// ```
71/// # use msuk_scifi::{implement_from_for_temperature, implement_as_for_temperature, unit::temperature::TemperatureInternalType};
72/// # use paste::paste;
73/// # struct K { value: f64 };
74/// # implement_as_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
75/// implement_from_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
76/// ```
77//
78macro_rules! implement_from_for_temperature {
79 ($type:ty, for $($t:ty),+) => {
80 $(paste! {
81 impl From<$type> for $t {
82 fn from(value: $type) -> $t {
83 value.[<as_ $t>]()
84 }
85 }
86
87 impl From<$t> for $type {
88 fn from(value: $t) -> Self {
89 Self { value: (value as TemperatureInternalType).max(0.0) }
90 }
91 }
92 })+
93 };
94}
95
96#[macro_export]
97/// Implement various arithmetic functionality for the given temperature type.
98///
99/// # Implements
100/// * [Add], [Sub], [Mul], [Div], and associated variants thereof.
101///
102/// # Arguments
103/// * Some "temperature" type, e.g. `K` (for Kelvin).
104/// * `for`, followed by a comma separated list of e.g. primitives that are to be supported.
105///
106/// # Examples
107/// ```
108/// # use msuk_scifi::{implement_arith_generics_for_temperature, implement_from_for_temperature, implement_as_for_temperature, unit::temperature::TemperatureInternalType};
109/// # use std::ops::{Add, Sub, Mul, Div};
110/// # use paste::paste;
111/// # struct K { value: TemperatureInternalType };
112/// # implement_from_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
113/// # implement_as_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
114/// implement_arith_generics_for_temperature!(K, for u32, i32, u64, i64, f32, f64);
115/// ```
116//
117macro_rules! implement_arith_generics_for_temperature {
118 // Can't use <T:Into<f64>> generics with these, so - macro'd copy-pasta it is.
119 ($c:ty, for $($t:ty),+) => {
120 impl Add<$c> for $c {
121 type Output = Self;
122 fn add(self, rhs: $c) -> Self::Output { Self::Output::from(self.value + rhs.value)}
123 }
124
125 impl Sub<$c> for $c {
126 type Output = Self;
127 fn sub(self, rhs: $c) -> Self::Output { Self::Output::from(self.value - rhs.value)}
128 }
129
130 // Mul<$c> for $c is -NOT- defined. Aside blackbody Tâ´, multiplying temperatures with
131 // each other doesn't make sense.
132
133 impl Div<$c> for $c {
134 type Output = TemperatureInternalType;
135 fn div(self, rhs: $c) -> Self::Output { self.value / {
136 if rhs.value == 0.0 { panic!("It burns! It burns! DBZ!"); }
137 rhs.value
138 }}
139 }
140
141 $(
142 impl Add<$c> for $t {
143 type Output = $c;
144 /// Add `rhs` to a number, e.g. `10 + rhs`.
145 fn add(self, rhs: Self::Output) -> Self::Output {
146 (self as TemperatureInternalType + rhs.value).into()
147 }
148 }
149
150 impl Add<$t> for $c {
151 type Output = Self;
152 fn add(self, rhs: $t) -> Self::Output { Self::Output::from(self.value + rhs as TemperatureInternalType) }
153 }
154
155 impl Sub<$c> for $t {
156 type Output = $c;
157 /// Subtract `rhs` from a number, e.g. `10 - rhs`.
158 fn sub(self, rhs: Self::Output) -> Self::Output {
159 (self as TemperatureInternalType - rhs.value).into()
160 }
161 }
162
163 impl Sub<$t> for $c {
164 type Output = Self;
165 fn sub(self, rhs: $t) -> Self::Output { Self::Output::from(self.value - rhs as TemperatureInternalType) }
166 }
167
168 impl Mul<$c> for $t {
169 type Output = $c;
170 /// Multiply by `rhs`, e.g. `10 * rhs`.
171 fn mul(self, rhs: Self::Output) -> Self::Output {
172 (self as TemperatureInternalType + rhs.value).into()
173 }
174 }
175
176 impl Mul<$t> for $c {
177 type Output = Self;
178 fn mul(self, rhs: $t) -> Self::Output { Self::Output::from(self.value * rhs as TemperatureInternalType) }
179 }
180
181 impl Div<$c> for $t {
182 type Output = $c;
183 /// Divide by `rhs`, e.g. `10 / rhs`.
184 fn div(self, rhs: Self::Output) -> Self::Output {
185 (self as TemperatureInternalType / {
186 if rhs.value == 0.0 {
187 let context = stringify!(Div<$c> for $t);
188 $crate::dbz_panic!(context, self, rhs.value);
189 }
190 rhs }).into()
191 }
192 }
193
194 impl Div<$t> for $c {
195 type Output = Self;
196 fn div(self, rhs: $t) -> Self::Output {
197 let rhs = rhs as TemperatureInternalType;
198 ( self.value / {
199 if rhs == 0.0 {
200 let context = stringify!(Div<$t> for $c);
201 $crate::dbz_panic!(context, self.value, rhs);
202 }
203 rhs }).into()
204 }
205 }
206 )+
207 };
208}
209
210#[cfg(test)]
211mod temperature_tests {
212 #[should_panic]
213 #[test]
214 fn dbz_context_formatting_works() {
215 use super::*;
216 use std::ops::{Add, Sub, Mul, Div};
217 use paste::paste;
218 struct X { value: f64 }
219 let a = 10.0;
220 let z = X { value: 0.0 };
221 implement_as_for_temperature!(X, for f64);
222 implement_from_for_temperature!(X, for f64);
223 implement_arith_generics_for_temperature!(X, for f64);
224 let _: X = a / z;
225 }
226}