diman_unit_system 0.2.0

Internal procedural macros for diman.
Documentation
use proc_macro2::TokenStream;
use quote::quote;

use crate::types::Defs;

impl Defs {
    pub fn units_array(&self) -> TokenStream {
        let units: TokenStream = self
            .units
            .iter()
            .filter_map(|unit| {
                let dim = self.get_dimension_expr(&unit.dimension);
                let factor = unit.factor;
                let symbol = unit.symbol.as_ref()?;
                Some(quote! {
                    (#dim, #symbol, #factor),
                })
            })
            .collect();
        quote! { [ #units ] }
    }

    pub fn debug_trait(&self) -> TokenStream {
        let Defs {
            quantity_type,
            dimension_type,
            ..
        } = &self;
        let units = self.units_array();
        quote! {
            impl<const D: #dimension_type, S: diman::DebugStorageType + std::fmt::Display> std::fmt::Debug for #quantity_type<S, D> {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    let closeness = |value: f64, unit_factor: f64| {
                        if value == 0.0 {
                            1.0
                        } else {
                            (value / unit_factor).abs().ln().abs()
                        }
                    };
                    let val = self.0.representative_value();
                    let units = #units;
                    let (unit_name, unit_value) = units
                        .iter()
                        .filter(|(d, _, _)| d == &D)
                        .min_by(|(_, _, x), (_, _, y)| {
                            closeness(val, *x)
                                .partial_cmp(&closeness(val, *y))
                                .unwrap_or(std::cmp::Ordering::Equal)
                        })
                        .map(|(_, name, val)| (name, val))
                        .unwrap_or((&"unknown unit", &1.0));
                    (self.0.div_f64(*unit_value))
                        .fmt(f)
                        .and_then(|_| write!(f, " {}", unit_name))
                }
            }
        }
    }
}