#![cfg(all(any(feature = "checked", doc), std))]
use onlyerror::{self, Error};
use crate::{Float, Temperature};
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
struct Bounds {
lower: Float,
upper: Float,
}
impl Default for Bounds {
fn default() -> Self {
Self {
#[cfg(feature = "f32")]
lower: f32::NEG_INFINITY,
#[cfg(feature = "f32")]
upper: f32::INFINITY,
#[cfg(not(feature = "f32"))]
lower: f64::NEG_INFINITY,
#[cfg(not(feature = "f32"))]
upper: f64::INFINITY,
}
}
}
impl Bounds {
const fn get_float_max() -> Float {
#[cfg(feature = "f32")]
return f32::MAX;
#[cfg(not(feature = "f32"))]
return f64::MAX;
}
const fn get_float_min() -> Float {
#[cfg(feature = "f32")]
return f32::MIN;
#[cfg(not(feature = "f32"))]
return f64::MIN;
}
pub fn set_lower(&mut self, val: Float) -> Result<(), CheckedTempError> {
if val > self.upper {
return Err(CheckedTempError::BoundTooHigh(val));
} else if val < Bounds::get_float_min() {
return Err(CheckedTempError::BoundTooLow(val));
}
self.lower = val;
Ok(())
}
pub fn set_upper(&mut self, val: Float) -> Result<(), CheckedTempError> {
if val < self.lower {
return Err(CheckedTempError::BoundTooLow(val));
} else if val > Bounds::get_float_max() {
return Err(CheckedTempError::BoundTooHigh(val));
}
self.upper = val;
Ok(())
}
}
#[derive(Debug, Error)]
pub enum CheckedTempError {
#[error("Given bound, {0}, was too low.")]
BoundTooLow(Float),
#[error("Given bound, {0}, was too high.")]
BoundTooHigh(Float),
#[error("The given temperature, {0}, was below absolute zero.")]
BelowAbsoluteZero(Float),
#[error("The given temperature, {0}, was out of bounds. ({1})")]
TempOutOfBounds(Float, &'static str),
#[error("Division by zero is not allowed.")]
DivisionByZero,
#[error("NaN values are not allowed for CheckedTemperature construction.")]
GivenValueIsNan,
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct CheckedTemperature {
temp: Temperature,
bounds: Bounds,
}
impl CheckedTemperature {
fn check(&self, temp: Temperature) -> Result<(), CheckedTempError> {
if temp.is_below_abs_zero() {
return Err(CheckedTempError::BelowAbsoluteZero(temp.get_inner()));
}
if temp.is_nan() {
return Err(CheckedTempError::GivenValueIsNan);
}
if temp.get_inner() > self.bounds.upper {
return Err(CheckedTempError::TempOutOfBounds(
temp.get_inner(),
"Too high!",
));
}
if temp.get_inner() < self.bounds.lower {
return Err(CheckedTempError::TempOutOfBounds(
temp.get_inner(),
"Too low!",
));
}
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn new(temp: Temperature) -> Result<CheckedTemperature, CheckedTempError> {
if temp.is_below_abs_zero() {
return Err(CheckedTempError::BelowAbsoluteZero(temp.get_inner()));
}
if temp.is_nan() {
return Err(CheckedTempError::GivenValueIsNan);
}
if temp.get_inner() > Bounds::get_float_max() {
return Err(CheckedTempError::TempOutOfBounds(
temp.get_inner(),
"Too high!",
));
}
if temp.get_inner() < Bounds::get_float_min() {
return Err(CheckedTempError::TempOutOfBounds(
temp.get_inner(),
"Too low!",
));
}
Ok(CheckedTemperature {
temp,
bounds: Bounds::default(),
})
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn set_temperature(&mut self, new: Temperature) -> Result<(), CheckedTempError> {
self.check(new)?;
self.temp = new;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn get_unchecked(&self) -> Temperature {
self.temp
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn into_unchecked(self) -> Temperature {
self.temp
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn get_inner(&self) -> Float {
self.temp.get_inner()
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn into_inner(self) -> Float {
self.temp.into_inner()
}
fn adjust_bounds(
&mut self,
new_unit: fn(Float) -> Temperature,
) -> Result<(), CheckedTempError> {
let current_unit = match self.temp {
Temperature::Fahrenheit(_) => Temperature::Fahrenheit,
Temperature::Celsius(_) => Temperature::Celsius,
Temperature::Kelvin(_) => Temperature::Kelvin,
};
if new_unit == current_unit {
return Ok(());
}
if self.bounds.lower == Float::NEG_INFINITY && self.bounds.upper == Float::INFINITY {
return Ok(());
}
let set_with_bounds = |b: Float| -> Result<Float, CheckedTempError> {
let current_bound = current_unit(b);
Ok(match new_unit(0.0) {
Temperature::Fahrenheit(_) => current_bound.to_fahrenheit().into_inner(),
Temperature::Celsius(_) => current_bound.to_celsius().into_inner(),
Temperature::Kelvin(_) => current_bound.to_kelvin().into_inner(),
})
};
if self.bounds.lower != Float::NEG_INFINITY {
self.bounds.lower = set_with_bounds(self.bounds.lower)?;
}
if self.bounds.upper != Float::INFINITY {
self.bounds.upper = set_with_bounds(self.bounds.upper)?;
}
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn to_fahrenheit(&self) -> Result<CheckedTemperature, CheckedTempError> {
let mut new = *self;
new.adjust_bounds(Temperature::Fahrenheit)?;
new.temp = new.temp.to_fahrenheit();
Ok(new)
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn to_celsius(&mut self) -> Result<CheckedTemperature, CheckedTempError> {
self.adjust_bounds(Temperature::Celsius)?;
self.temp = self.temp.to_celsius();
Ok(self.to_owned())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn to_kelvin(&mut self) -> Result<CheckedTemperature, CheckedTempError> {
self.adjust_bounds(Temperature::Kelvin)?;
self.temp = self.temp.to_kelvin();
Ok(self.to_owned())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn add(&mut self, temp: Temperature) -> Result<(), CheckedTempError> {
let result = self.temp + temp;
self.check(result)?;
self.temp = result;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn sub(&mut self, temp: Temperature) -> Result<(), CheckedTempError> {
let result = self.temp - temp;
self.check(result)?;
self.temp = result;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn mul(&mut self, num: Float) -> Result<(), CheckedTempError> {
let result = self.temp * num;
self.check(result)?;
self.temp = result;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```should_panic")]
pub fn div(&mut self, num: Float) -> Result<(), CheckedTempError> {
if num == 0.0 {
return Err(CheckedTempError::DivisionByZero);
}
let result = self.temp / num;
self.check(result)?;
self.temp = result;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```should_panic")]
pub fn set_upper_bound(&mut self, bound: Float) -> Result<(), CheckedTempError> {
self.bounds.set_upper(bound)?;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```should_panic")]
pub fn set_lower_bound(&mut self, bound: Float) -> Result<(), CheckedTempError> {
self.bounds.set_lower(bound)?;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```should_panic")]
pub fn set_bounds(
&mut self,
lower_bound: Float,
upper_bound: Float,
) -> Result<(), CheckedTempError> {
self.bounds.set_lower(lower_bound)?;
self.bounds.set_upper(upper_bound)?;
Ok(())
}
#[cfg_attr(not(feature = "checked"), doc = "```ignore")]
#[cfg_attr(feature = "checked", doc = "```")]
pub fn get_bounds(&self) -> (Temperature, Temperature) {
let t: fn(Float) -> Temperature = match self.temp {
Temperature::Fahrenheit(_) => Temperature::Fahrenheit,
Temperature::Celsius(_) => Temperature::Celsius,
Temperature::Kelvin(_) => Temperature::Kelvin,
};
(t(self.bounds.lower), t(self.bounds.upper))
}
}
impl core::fmt::Display for CheckedTemperature {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.get_inner())
}
}
impl ufmt::uDebug for CheckedTemperature {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt_write::uWrite + ?Sized,
{
let unit = match self.temp {
Temperature::Fahrenheit(_) => "Fahrenheit",
Temperature::Celsius(_) => "Celsius",
Temperature::Kelvin(_) => "Kelvin",
};
#[cfg(feature = "f32")]
return ufmt::uwrite!(
f,
"Temperature::{}({})",
unit,
ufmt_float::uFmt_f32::Five(self.get_inner())
);
#[cfg(not(feature = "f32"))]
return ufmt::uwrite!(
f,
"Temperature::{}({})",
unit,
ufmt_float::uFmt_f64::Five(self.get_inner())
);
}
}
impl ufmt::uDisplay for CheckedTemperature {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: ufmt_write::uWrite + ?Sized,
{
#[cfg(feature = "f32")]
return ufmt::uwrite!(f, "{}", ufmt_float::uFmt_f32::Five(self.get_inner()));
#[cfg(not(feature = "f32"))]
return ufmt::uwrite!(f, "{}", ufmt_float::uFmt_f64::Five(self.get_inner()));
}
}