diman_unit_system 0.2.0

Internal procedural macros for diman.
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;

use crate::{
    storage_types::{FloatType, VectorType},
    types::{Defs, Unit},
};

impl Defs {
    pub fn unit_constructors(&self) -> TokenStream {
        self.units.iter().map(|unit| {
            let dimension = self.get_dimension_expr(&unit.dimension);
            let quantity_type = &self.quantity_type;
            let unit_name = &unit.name;
            let factor = &unit.factor;
            let conversion_method_name = format_ident!("in_{}", unit_name);
            let vector_impls: TokenStream = self
                .vector_types()
                .iter()
                .map(|vector_type| self.vector_unit_constructor(vector_type, unit, &dimension))
                .collect();
            let float_impls: TokenStream = self
                .float_types()
                .iter()
                .map(|float_type| self.float_unit_constructor(float_type, unit, &dimension))
                .collect();
            quote! {
                impl<S> #quantity_type<S, {#dimension}> where S: std::ops::Div<f64, Output = S> {
                    pub fn #conversion_method_name(self) -> S {
                        self.0 / #factor
                    }
                }
                #float_impls
                #vector_impls
            }
        }).collect()
    }

    fn float_unit_constructor(
        &self,
        float_type: &FloatType,
        unit: &Unit,
        quantity_dimension: &TokenStream,
    ) -> TokenStream {
        let Defs { quantity_type, .. } = &self;
        let Unit {
            name: unit_name,
            factor,
            ..
        } = unit;
        let name = &float_type.name;
        let span = self.dimension_type.span();
        quote_spanned! {
            span =>
            impl #quantity_type<#name, {#quantity_dimension}> {
                pub fn #unit_name(val: #name) -> #quantity_type<#name, {#quantity_dimension}> {
                    #quantity_type::<#name, {#quantity_dimension}>(val * (#factor as #name))
                }
            }
        }
    }

    fn vector_unit_constructor(
        &self,
        vector_type: &VectorType,
        unit: &Unit,
        quantity_dimension: &TokenStream,
    ) -> TokenStream {
        let Defs { quantity_type, .. } = &self;
        let Unit {
            name: unit_name,
            factor,
            ..
        } = unit;
        let VectorType {
            name,
            float_type,
            num_dims,
            ..
        } = &vector_type;
        let float_type = &float_type.name;
        let fn_args = match num_dims {
            2 => quote! { x: #float_type, y: #float_type },
            3 => quote! { x: #float_type, y: #float_type, z: #float_type },
            _ => unreachable!(),
        };
        let call_args = match num_dims {
            2 => quote! { x, y },
            3 => quote! { x, y, z },
            _ => unreachable!(),
        };
        quote! {
            impl #quantity_type<#name, {#quantity_dimension}> {
                pub fn #unit_name(#fn_args) -> #quantity_type<#name, {#quantity_dimension}> {
                    #quantity_type::<#name, {#quantity_dimension}>(#name::new(#call_args) * (#factor as #float_type))
                }
            }
        }
    }
}