kittycad_unit_conversion_derive/
lib.rs1#[macro_use]
4extern crate quote;
5#[macro_use]
6extern crate syn;
7
8#[proc_macro_derive(UnitConversion)]
10pub fn derive_unit_conversions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11 derive(parse_macro_input!(input)).into()
12}
13
14fn derive(item: syn::DeriveInput) -> proc_macro2::TokenStream {
15 let struct_name = &item.ident;
16
17 let measurement_struct_name = format_ident!("{}", struct_name.to_string().replace("Unit", ""));
18
19 match &item.data {
21 syn::Data::Enum(_) => {}
22 _ => return quote!(),
24 }
25
26 let variants: Vec<&proc_macro2::Ident> = match &item.data {
28 syn::Data::Enum(data) => data.variants.iter().map(|v| &v.ident).collect(),
29 _ => unreachable!(),
30 };
31
32 let mut items = quote!();
33 for variant in variants.clone() {
34 for to_variant in variants.clone() {
36 if variant == to_variant {
37 items = quote! {
39 #items
40 (#struct_name::#variant, #struct_name::#to_variant) => {
41 input
42 }
43 };
44 } else {
45 let from_fn = format_ident!("from_{}", clean_fn_name(&variant.to_string()));
46 let to_fn = format_ident!("as_{}", clean_fn_name(&to_variant.to_string()));
47 items = quote! {
49 #items
50 (#struct_name::#variant, #struct_name::#to_variant) => {
51 let value = measurements::#measurement_struct_name::#from_fn(input);
52 value.#to_fn()
53 }
54 };
55 }
56 }
57 }
58
59 quote! {
60 impl #struct_name {
61 pub fn convert_to(&self, to: #struct_name, input: f64) -> f64 {
63 match (self, to) {
64 #items
65 }
66 }
67 }
68 }
69}
70fn clean_fn_name(name: &str) -> String {
72 inflections::case::to_snake_case(
73 &name
74 .replace("Electronvolts", "e_v")
75 .replace("Kilocalories", "Kcalories"),
76 )
77}