hifumi_macros/
lib.rs

1use anyhow::{anyhow, Result};
2use proc_macro::TokenStream;
3use quote::quote;
4use std::collections::BTreeMap;
5use syn::parse_macro_input;
6
7mod template;
8mod tools;
9mod utils;
10
11use template::{
12    generate_current_version_struct, generate_impl_froms, generate_old_version_structs,
13};
14use tools::{DeriveVersion, Migration};
15
16#[proc_macro_attribute]
17pub fn version(attr: TokenStream, input: TokenStream) -> TokenStream {
18    let attr: DeriveVersion = parse_macro_input!(attr);
19    let input: Migration = parse_macro_input!(input);
20
21    let final_struct_fields = input
22        .struct_data
23        .fields
24        .iter()
25        .map(|field| match field {
26            syn::Field {
27                ident: Some(ident),
28                ty,
29                ..
30            } => Ok((ident.clone(), ty.clone())),
31            _ => Err(anyhow!("Failed to get field ident")),
32        })
33        .collect::<Vec<Result<_>>>()
34        .into_iter()
35        .collect::<Result<Vec<_>>>()
36        .expect("Failed to get field ident")
37        .into_iter()
38        .collect::<BTreeMap<_, _>>();
39    let versions = {
40        let mut temp_version = attr.version.clone();
41        let mut ret = vec![];
42
43        while let Some(item) = input
44            .versions
45            .iter()
46            .find(|item| item.to.value() == temp_version)
47        {
48            ret.push((item.from.value(), item.changes.clone(), item.to.value()));
49            temp_version = item.from.value();
50        }
51        ret
52    };
53
54    let old_version_structs = generate_old_version_structs(
55        input.struct_data.ident.clone(),
56        attr.version.clone(),
57        final_struct_fields.clone(),
58        input
59            .extra_macros
60            .iter()
61            .map(|(key, value)| {
62                quote! {
63                    #[#key #value]
64                }
65            })
66            .collect::<Vec<_>>(),
67        versions.clone(),
68    )
69    .expect("Failed to generate old version structs");
70
71    // Confirm the order of the versions
72    let impl_versions = generate_impl_froms(
73        input.struct_data.ident.clone(),
74        attr.version.clone(),
75        final_struct_fields.clone(),
76        input.versions.clone(),
77    )
78    .expect("Failed to generate impl froms");
79
80    let current_version_struct = generate_current_version_struct(
81        attr.clone(),
82        input.clone(),
83        input.struct_data.ident.clone(),
84        attr.version.clone(),
85        final_struct_fields,
86        versions,
87    )
88    .expect("Failed to generate current version struct");
89
90    quote! {
91        #current_version_struct
92        #old_version_structs
93        #impl_versions
94    }
95    .into()
96}