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 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}