use core::fmt;
use crate::conversion::Conversion;
use crate::error::{Error, Result};
use crate::kind::Kind;
use crate::ratio::Ratio;
use crate::unit::UnitExpr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Quantity {
value: Ratio,
unit: UnitExpr,
}
impl Quantity {
#[must_use]
pub const fn new(value: Ratio, unit: UnitExpr) -> Self {
Self { value, unit }
}
#[must_use]
pub const fn value(&self) -> Ratio {
self.value
}
#[must_use]
pub const fn unit(&self) -> &UnitExpr {
&self.unit
}
pub fn to_f64(&self) -> Result<f64> {
self.value.to_f64()
}
pub fn to_unit(&self, target: &UnitExpr) -> Result<Self> {
let conv = Conversion::between(&self.unit, target)?;
Ok(Self {
value: conv.apply_exact(self.value)?,
unit: target.clone(),
})
}
fn guard_scalar(&self, rhs: &Self, operation: &'static str) -> Result<()> {
for (q, other) in [(self, rhs), (rhs, self)] {
if q.unit.kind() == Kind::AbsoluteTemperature && other.unit.is_unity() {
return Err(Error::KindMismatch {
from: Kind::AbsoluteTemperature,
to: Kind::Plain,
operation,
suggestions: q
.unit
.difference_symbol()
.map(String::from)
.into_iter()
.collect(),
});
}
}
Ok(())
}
pub fn checked_mul(&self, rhs: &Self) -> Result<Self> {
self.guard_scalar(rhs, "multiplication")?;
Ok(Self {
value: self.value.checked_mul(rhs.value)?,
unit: self.unit.mul(&rhs.unit)?,
})
}
pub fn checked_div(&self, rhs: &Self) -> Result<Self> {
self.guard_scalar(rhs, "division")?;
Ok(Self {
value: self.value.checked_div(rhs.value)?,
unit: self.unit.div(&rhs.unit)?,
})
}
}
impl fmt::Display for Quantity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.value.to_decimal(12) {
Ok((s, true)) => write!(f, "{s} {}", self.unit),
Ok((s, false)) => write!(f, "~{s} {}", self.unit),
Err(_) => write!(f, "{} {}", self.value, self.unit),
}
}
}