danwi 0.4.2

Zero-cost dimensional analysis library with SI units, compile-time checking, and no_std support
Documentation
//! The `units!` macro used the way a downstream crate would use it.

use danwi::prelude::*;

mod aux {
    use danwi::dimension::{Length, Mass, ThermodynamicTemperature};

    danwi::units! {
        foot: Length { symbol: ft, scale: 3_048 / 10_000 },
        pound: Mass { symbol: lb, scale: 45_359_237 / 100_000_000 },
        /// Scale and offset combined: x_K = 5/9 * x_F + 45967/180.
        fahrenheit: ThermodynamicTemperature {
            symbol: degF,
            display: "°F",
            scale: 5 / 9,
            offset: 45_967 / 180,
        },
        /// Contrived unit with a non-decimal scale and full prefixes.
        stick: Length { symbol: stk, scale: 2, prefixes: all },
    }
}

use aux::{constants::*, ext::F64QuantityExt as _};

fn assert_close(a: f64, b: f64) {
    assert!((a - b).abs() <= 1e-12 * b.abs().max(1.0), "{a} != {b}");
}

#[test]
fn custom_units_mix_with_si() {
    assert_close(1.0.foot().to(mm), 304.8);
    assert_close((1.0.foot() + 1.0.m()).value(), 1.3048);
    assert_close(1.0.pound().to(g), 453.59237);
}

#[test]
fn scale_and_offset_combined() {
    assert_close(32.0.fahrenheit().to(K), 273.15);
    assert_close(212.0.fahrenheit().to(K), 373.15);
    assert_close(98.6.fahrenheit().to(degC), 37.0);
    assert_close(50.0.fahrenheit().to(degF), 50.0);
    assert_eq!(
        format!("{:.0}", 32.0.fahrenheit().display_as(degF)),
        "32 °F"
    );
}

#[test]
fn prefixes_compose_with_custom_scales() {
    // Kilo<Stick> must reduce to 2000/1 at the type level.
    assert_eq!((1.0_f64 * kstk).value(), 2_000.0);
    assert_eq!(1.0.kilostick().to(stk), 1_000.0);
    assert_eq!(format!("{}", 1.0.kstk().display_as(kstk)), "1 kstk");
}

#[test]
fn generated_types_and_constants() {
    let reach: aux::types::f64::Foot = 6.0 * ft;
    assert_close(reach.value(), 1.8288);
    assert_eq!(reach, 6.0.foot());
}