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}