use core::fmt;
use crate::{
error::{Error, Result},
unit,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Pressure(f64);
impl Pressure {
pub fn from_pa(pa: f64) -> Result<Self> {
if !pa.is_finite() {
return Err(Error::NotFinite);
}
Ok(Pressure(pa))
}
pub fn from_hpa(hpa: f64) -> Result<Self> {
Self::from_pa(hpa * unit::HPA)
}
pub fn from_mbar(mbar: f64) -> Result<Self> {
Self::from_pa(mbar * unit::MBAR)
}
pub fn from_bar(bar: f64) -> Result<Self> {
Self::from_pa(bar * unit::BAR)
}
pub fn from_kpa(kpa: f64) -> Result<Self> {
Self::from_pa(kpa * unit::KPA)
}
pub fn from_mpa(mpa: f64) -> Result<Self> {
Self::from_pa(mpa * unit::MPA)
}
pub fn from_torr(torr: f64) -> Result<Self> {
Self::from_pa(torr * unit::TORR)
}
pub fn from_atm(atm: f64) -> Result<Self> {
Self::from_pa(atm * unit::ATM)
}
#[must_use]
pub fn pa(self) -> f64 {
self.0
}
#[must_use]
pub fn hpa(self) -> f64 {
self.0 / unit::HPA
}
#[must_use]
pub fn mbar(self) -> f64 {
self.0 / unit::MBAR
}
#[must_use]
pub fn bar(self) -> f64 {
self.0 / unit::BAR
}
#[must_use]
pub fn kpa(self) -> f64 {
self.0 / unit::KPA
}
#[must_use]
pub fn mpa(self) -> f64 {
self.0 / unit::MPA
}
#[must_use]
pub fn torr(self) -> f64 {
self.0 / unit::TORR
}
#[must_use]
pub fn atm(self) -> f64 {
self.0 / unit::ATM
}
}
impl fmt::Display for Pressure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let decimals = f.precision().unwrap_or(1);
write!(f, "{:.decimals$} mbar", self.mbar())
}
}
impl approx::AbsDiffEq for Pressure {
type Epsilon = f64;
fn default_epsilon() -> Self::Epsilon {
1.0
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
(self.0 - other.0).abs() <= epsilon
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_non_finite() {
assert!(matches!(Pressure::from_pa(f64::NAN), Err(Error::NotFinite)));
}
#[test]
fn one_atm_round_trip() {
let p = Pressure::from_atm(1.0).unwrap();
assert!((p.pa() - 101_325.0).abs() < 1e-9);
assert!((p.hpa() - 1013.25).abs() < 1e-9);
assert!((p.mbar() - 1013.25).abs() < 1e-9);
assert!((p.torr() - 760.0).abs() < 1e-6);
}
#[test]
fn bar_and_kpa() {
let one_bar = Pressure::from_bar(1.0).unwrap();
assert!((one_bar.pa() - 1e5).abs() < 1e-9);
assert!((one_bar.kpa() - 100.0).abs() < 1e-9);
}
#[test]
fn mbar_equals_hpa() {
let p = Pressure::from_mbar(1013.25).unwrap();
assert!((p.mbar() - 1013.25).abs() < 1e-9);
assert!((p.hpa() - 1013.25).abs() < 1e-9);
}
#[test]
fn mpa_round_trip() {
let p = Pressure::from_mpa(1.0).unwrap();
assert!((p.mpa() - 1.0).abs() < 1e-12);
assert!((p.pa() - 1e6).abs() < 1e-6);
}
#[test]
fn torr_round_trip() {
let p = Pressure::from_torr(760.0).unwrap();
assert!((p.torr() - 760.0).abs() < 1e-9);
assert!((p.atm() - 1.0).abs() < 1e-9);
}
#[test]
fn kpa_round_trip() {
let p = Pressure::from_kpa(100.0).unwrap();
assert!((p.kpa() - 100.0).abs() < 1e-12);
assert!((p.bar() - 1.0).abs() < 1e-9);
}
#[test]
fn abs_diff_eq() {
use approx::AbsDiffEq;
let a = Pressure::from_pa(101_325.0).unwrap();
let b = Pressure::from_pa(101_325.5).unwrap();
assert!(a.abs_diff_eq(&b, Pressure::default_epsilon()));
let c = Pressure::from_pa(101_327.0).unwrap();
assert!(!a.abs_diff_eq(&c, Pressure::default_epsilon()));
}
#[test]
fn display_renders_mbar() {
let p = Pressure::from_hpa(1013.25).unwrap();
let s = format!("{p}");
assert!(s.contains("mbar"), "got: {s}");
}
}