133 lines
4.3 KiB
Rust
133 lines
4.3 KiB
Rust
extern crate proc_macro;
|
|
use proc_macro::TokenStream;
|
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
use quote::quote;
|
|
use std::ops::Deref;
|
|
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<Self> {
|
|
let struct_name: Ident = input.parse()?;
|
|
input.parse::<Token![,]>()?;
|
|
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);
|
|
|
|
//----- Value Extension ----------------------------------
|
|
let mut val_ext_name = String::new();
|
|
let struct_name_str = struct_name.to_string();
|
|
let mut struct_name_chars = struct_name_str.chars();
|
|
let first = struct_name_chars
|
|
.next()
|
|
.expect("Struct name cannot be 0 length");
|
|
val_ext_name.push(first.to_ascii_lowercase());
|
|
for character in struct_name_chars {
|
|
if character.is_uppercase() {
|
|
val_ext_name.push('_');
|
|
val_ext_name.push(character.to_ascii_lowercase());
|
|
} else {
|
|
val_ext_name.push(character);
|
|
}
|
|
}
|
|
let val_ext_fn_name = Ident::new(val_ext_name.deref(), struct_name.span());
|
|
let val_ext_trait_name =
|
|
Ident::new(format!("{struct_name_str}Val").deref(), struct_name.span());
|
|
|
|
//----- Conversion impls ----------------------------------
|
|
let mut conversion_impls: Vec<TokenStream2> = 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::<Vec<TokenStream2>>();
|
|
|
|
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)*
|
|
}
|
|
};
|
|
|
|
conversion_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<V: Value>(pub V);
|
|
|
|
impl<V: Value> Quantity<V> for #struct_name<V> {
|
|
#[inline(always)]
|
|
fn value(self) -> V {
|
|
self.0
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn symbol() -> &'static str {
|
|
#symbol
|
|
}
|
|
}
|
|
|
|
pub trait #val_ext_trait_name : Value {
|
|
/// Create a quantity in the given unit from this [Value]
|
|
#[inline(always)]
|
|
fn #val_ext_fn_name(self) -> #struct_name<Self> {
|
|
#struct_name(self)
|
|
}
|
|
}
|
|
|
|
impl <V: Value> #val_ext_trait_name for V {}
|
|
|
|
#(#conversion_impls)*
|
|
};
|
|
|
|
expanded.into()
|
|
}
|