diff --git a/Cargo.toml b/Cargo.toml index 60fd6ae..8645f17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,12 @@ members = [ # Device types "node", "commander", + # Meta + "generate-quantity" ] [workspace.package] -version = "0.3.5" +version = "0.3.6" edition = "2021" repository = "https://git.bfpower.io/BFPOWER/physical" readme = "README.md" @@ -89,6 +91,8 @@ version = "2.0.*" features = ["extra-traits", "parsing"] [workspace.dependencies.quote] version = "1.0.*" +[workspace.dependencies.proc-macro2] +version = "1.0.*" [workspace.dependencies.trybuild] version = "1.0.*" @@ -114,6 +118,8 @@ lm35 = [] pid = [] stm32 = [] +[dependencies.generate-quantity] +path = "generate-quantity" [dependencies.num-traits] workspace = true [dependencies.derive_more] diff --git a/generate-quantity/Cargo.toml b/generate-quantity/Cargo.toml new file mode 100644 index 0000000..1fedb42 --- /dev/null +++ b/generate-quantity/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "generate-quantity" +description = "Macros for generating physical quantity type" +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[lib] +proc-macro = true + +[dependencies.quote] +workspace = true +[dependencies.syn] +workspace = true +[dependencies.proc-macro2] +workspace = true \ No newline at end of file diff --git a/generate-quantity/src/lib.rs b/generate-quantity/src/lib.rs new file mode 100644 index 0000000..2494e15 --- /dev/null +++ b/generate-quantity/src/lib.rs @@ -0,0 +1,101 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{parse_macro_input, Ident, LitStr, Token}; + +const NUMBER_TYPES: &[&str] = &[ + "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "u128", "i128", "usize", "isize", "f32", + "f64", +]; + +// Define the input structure for the macro +struct QuantityInput { + struct_name: Ident, + symbol: LitStr, +} + +// Implement the parsing for the input structure +impl Parse for QuantityInput { + fn parse(input: ParseStream) -> syn::Result { + let struct_name: Ident = input.parse()?; + input.parse::()?; + let symbol: LitStr = input.parse()?; + Ok(QuantityInput { + struct_name, + symbol, + }) + } +} + +/// The following imports must be in scope for this macro to work +/// ``` +/// use physical::quantity::{Quantity, Value}; +/// use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; +/// use generate_quantity::quantity_type; +/// ``` +#[proc_macro] +pub fn quantity_type(input: TokenStream) -> TokenStream { + // Parse the input tokens into the QuantityInput structure + let QuantityInput { + struct_name, + symbol, + } = parse_macro_input!(input as QuantityInput); + + //----- Conversion impls ---------------------------------- + let mut impls: Vec = Vec::new(); + for ¤t_type in NUMBER_TYPES { + // Generate conversion methods for all primitive types except the current_type + let conversions = NUMBER_TYPES + .iter() + .filter(|&&t| t != current_type) + .map(|&target_type| { + let method_name = Ident::new(&format!("as_{}", target_type), Span::call_site()); + let target_type = Ident::new(target_type, Span::call_site()); + + quote! { + /// Directly [as] cast this quantities value while maintaining the type of the quantity. + #[inline(always)] + pub fn #method_name(self) -> #struct_name<#target_type> { + #struct_name(self.0 as #target_type) + } + } + }) + .collect::>(); + + let current_type = Ident::new(current_type, Span::call_site()); + // Generate the impl block for the struct with the current_type + let expanded = quote! { + impl #struct_name<#current_type> { + #(#conversions)* + } + }; + + impls.push(expanded); + } + + //----- Output Code ---------------------------------- + let expanded = quote! { + #[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] + #[display(fmt = "{} {}", _0, "Self::symbol()")] + #[repr(transparent)] + pub struct #struct_name(pub V); + + impl Quantity for #struct_name { + #[inline(always)] + fn value(self) -> V { + self.0 + } + + #[inline(always)] + fn symbol() -> &'static str { + #symbol + } + } + + #(#impls)* + }; + + expanded.into() +} diff --git a/src/quantity/irradiance.rs b/src/quantity/irradiance.rs index 297942a..20a041a 100644 --- a/src/quantity/irradiance.rs +++ b/src/quantity/irradiance.rs @@ -1,22 +1,6 @@ -use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; use crate::quantity::{Quantity, Value}; +use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; +use generate_quantity::quantity_type; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Watts per Square Meter ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct WattsPerSquareMeter(pub V); - -impl Quantity for WattsPerSquareMeter { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "W/m²" - } -} \ No newline at end of file +//----- Watter per Square Meter ---------------------------------- +quantity_type! {WattsPerSquareMeter, "W/m²"} diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs index 76fe2ed..20448a2 100644 --- a/src/quantity/mod.rs +++ b/src/quantity/mod.rs @@ -49,7 +49,7 @@ pub trait Value: Num + Copy + PartialOrd + FromPrimitive + NumCast + Display { fn kelvins(self) -> Kelvins { Kelvins(self) } - + #[inline(always)] fn deci_kelvins(self) -> DeciKelvins { DeciKelvins(self) @@ -59,7 +59,7 @@ pub trait Value: Num + Copy + PartialOrd + FromPrimitive + NumCast + Display { fn degrees_celsius(self) -> DegreesCelsius { DegreesCelsius(self) } - + #[inline(always)] fn deci_degrees_celsius(self) -> DeciDegreesCelsius { DeciDegreesCelsius(self) @@ -161,6 +161,12 @@ mod defmt_impl { } } + impl> Format for Fmt { + fn format(&self, fmt: Formatter) { + write!(fmt, "{} {}", self.quantity.value(), Q::symbol()); + } + } + impl> Format for Fmt { fn format(&self, fmt: Formatter) { write!(fmt, "{} {}", self.quantity.value(), Q::symbol()); @@ -191,6 +197,12 @@ mod defmt_impl { } } + impl> Format for Fmt { + fn format(&self, fmt: Formatter) { + write!(fmt, "{} {}", self.quantity.value(), Q::symbol()); + } + } + impl> Format for Fmt { fn format(&self, fmt: Formatter) { write!(fmt, "{} {}", self.quantity.value(), Q::symbol()); diff --git a/src/quantity/resistance.rs b/src/quantity/resistance.rs index 01b0bee..ecbbd9d 100644 --- a/src/quantity/resistance.rs +++ b/src/quantity/resistance.rs @@ -1,22 +1,6 @@ use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; +use generate_quantity::quantity_type; use crate::quantity::{Quantity, Value}; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Ohms ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct Ohms(pub V); - -impl Quantity for Ohms { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "Ω" - } -} +//----- Ohms ---------------------------------- +quantity_type! {Ohms, "Ω"} diff --git a/src/quantity/temperature.rs b/src/quantity/temperature.rs index 3a8aafd..57dfd98 100644 --- a/src/quantity/temperature.rs +++ b/src/quantity/temperature.rs @@ -1,28 +1,11 @@ use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; -use num_traits::float::FloatCore; +use generate_quantity::quantity_type; use crate::quantity::{DECI, Quantity, Value}; const KELVIN_CELSIUS_OFFSET: f64 = 273.15; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Kelvins ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct Kelvins(pub V); - -impl Quantity for Kelvins { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "K" - } -} +//----- Kelvins ---------------------------------- +quantity_type! {Kelvins, "K"} impl Kelvins { #[inline] @@ -39,25 +22,8 @@ impl Kelvins { } } -// --------------------------------------------------------------------------------------------------------------------- -// ----- Deci Kelvins ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct DeciKelvins(pub V); - -impl Quantity for DeciKelvins { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "dK" - } -} +//----- Deci Kelvins ---------------------------------- +quantity_type! {DeciKelvins, "dK"} impl DeciKelvins { #[inline] @@ -67,25 +33,8 @@ impl DeciKelvins { } } -// --------------------------------------------------------------------------------------------------------------------- -// ----- Degrees Celsius ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct DegreesCelsius(pub V); - -impl Quantity for DegreesCelsius { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "℃" - } -} +//----- Degrees Celsius ---------------------------------- +quantity_type! {DegreesCelsius, "℃"} impl DegreesCelsius { #[inline] @@ -102,25 +51,8 @@ impl DegreesCelsius { } } -// --------------------------------------------------------------------------------------------------------------------- -// ----- Deci Degrees Celsius ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct DeciDegreesCelsius(pub V); - -impl Quantity for DeciDegreesCelsius { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "d℃" - } -} +//----- Deci Degrees Celsius ---------------------------------- +quantity_type! {DeciDegreesCelsius, "d℃"} impl DeciDegreesCelsius { #[inline] diff --git a/src/quantity/voltage.rs b/src/quantity/voltage.rs index 500c32f..f49fbfa 100644 --- a/src/quantity/voltage.rs +++ b/src/quantity/voltage.rs @@ -1,27 +1,11 @@ use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; +use generate_quantity::quantity_type; use crate::quantity::{Quantity, Value}; const VOLT_MV_RATIO: u16 = 1_000; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Volts ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct Volts(pub V); - -impl Quantity for Volts { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "V" - } -} +//----- Volts ---------------------------------- +quantity_type! {Volts, "V"} impl Volts { #[inline] @@ -31,25 +15,8 @@ impl Volts { } } -// --------------------------------------------------------------------------------------------------------------------- -// ----- MilliVolts ------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct MilliVolts(pub V); - -impl Quantity for MilliVolts { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "mV" - } -} +//----- Milli Volts ---------------------------------- +quantity_type! {MilliVolts, "mV"} impl MilliVolts { pub fn to_volts(self) -> Volts { diff --git a/src/quantity/volume_rate.rs b/src/quantity/volume_rate.rs index 04cc14a..9646b80 100644 --- a/src/quantity/volume_rate.rs +++ b/src/quantity/volume_rate.rs @@ -1,22 +1,6 @@ -use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; use crate::quantity::{Quantity, Value}; +use derive_more::{Add, AddAssign, Display, Sub, SubAssign}; +use generate_quantity::quantity_type; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Liters per Minute------------------------ -// --------------------------------------------------------------------------------------------------------------------- -#[derive(Copy, Clone, PartialEq, PartialOrd, Add, AddAssign, Sub, SubAssign, Display)] -#[display(fmt = "{} {}", _0, "Self::symbol()")] -#[repr(transparent)] -pub struct LitersPerMinute(pub V); - -impl Quantity for LitersPerMinute { - #[inline(always)] - fn value(self) -> V { - self.0 - } - - #[inline(always)] - fn symbol() -> &'static str { - "L/min" - } -} \ No newline at end of file +//----- Liters per Minute ---------------------------------- +quantity_type! {LitersPerMinute, "L/min"}