aptos_log_derive_link/
lib.rs1use proc_macro::TokenStream;
5use proc_macro2::Ident;
6use quote::{format_ident, quote};
7use syn::{
8 parse_macro_input, AngleBracketedGenericArguments, Attribute, Data, DataStruct, DeriveInput,
9 Fields, FieldsNamed, GenericArgument, Meta, MetaList, NestedMeta, Path, PathArguments,
10 PathSegment, Type, TypePath,
11};
12
13#[proc_macro_derive(Schema, attributes(schema))]
14pub fn derive(input: TokenStream) -> TokenStream {
15 let input = parse_macro_input!(input as DeriveInput);
17 let name = input.ident;
18 let fields = match input.data {
19 Data::Struct(DataStruct {
20 fields: Fields::Named(FieldsNamed { named, .. }),
21 ..
22 }) => named,
23 _ => panic!("derive(Schema) only supports structs with named fields"),
24 };
25 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
26
27 let fields: Vec<StructField> = fields
28 .iter()
29 .map(|f| {
30 let ty = f.ty.clone();
31 let value_type = extract_attr(&f.attrs);
32
33 let inner_ty = extract_internal_type(&ty).cloned();
34 StructField {
35 value_type,
36 ident: f.ident.clone(),
37 ty: f.ty.clone(),
38 inner_ty,
39 }
40 })
41 .collect();
42
43 let setters = fields.iter().map(|f| {
44 let ident = &f.ident;
45
46 if let Some(ty) = &f.inner_ty {
47 quote! {
48 pub fn #ident(mut self, #ident: #ty) -> Self {
49 self.#ident = ::std::option::Option::Some(#ident);
50 self
51 }
52 }
53 } else {
54 let ty = &f.ty;
55 quote! {
56 pub fn #ident(mut self, #ident: #ty) -> Self {
57 self.#ident = #ident;
58 self
59 }
60 }
61 }
62 });
63
64 let visitor = format_ident!("visitor");
66 let from_serde = quote! { ::aptos_logger::Value::from_serde };
67 let from_display = quote! { ::aptos_logger::Value::from_display };
68 let from_debug = quote! { ::aptos_logger::Value::from_debug };
69 let key_new = quote! { ::aptos_logger::Key::new };
70 let visits = fields.iter().map(|f| {
71 let ident = f.ident.as_ref().unwrap();
72 let ident_str = ident.to_string();
73
74 let from_fn = match f.value_type {
75 Some(ValueType::Display) => &from_display,
76 Some(ValueType::Debug) => &from_debug,
77 Some(ValueType::Serde) | None => &from_serde,
78 };
79 if f.inner_ty.is_some() {
80 quote! {
81 if let Some(#ident) = &self.#ident {
82 #visitor.visit_pair(#key_new(#ident_str), #from_fn(#ident));
83 }
84 }
85 } else {
86 quote! {
87 #visitor.visit_pair(#key_new(#ident_str), #from_fn(&self.#ident));
88 }
89 }
90 });
91
92 let expanded = quote! {
94 impl #impl_generics #name #ty_generics #where_clause {
95 #(#setters)*
96 }
97
98 impl #impl_generics ::aptos_logger::Schema for #name #ty_generics #where_clause {
99 fn visit(&self, visitor: &mut dyn ::aptos_logger::Visitor) {
100 #(#visits)*
101 }
102 }
103 };
104
105 TokenStream::from(expanded)
107}
108
109#[derive(Debug)]
110enum ValueType {
111 Debug,
112 Display,
113 Serde,
114}
115
116#[derive(Debug)]
117struct StructField {
118 value_type: Option<ValueType>,
119 ident: Option<Ident>,
120 ty: Type,
121 inner_ty: Option<Type>,
123}
124
125fn extract_internal_type(ty: &Type) -> Option<&Type> {
126 if let Type::Path(TypePath {
127 qself: None,
128 path: Path { segments, .. },
129 }) = ty
130 {
131 if let Some(PathSegment { ident, arguments }) = segments.first() {
132 if ident == "Option" {
134 if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
135 args, ..
136 }) = arguments
137 {
138 if let Some(GenericArgument::Type(ty)) = args.first() {
139 return Some(ty);
140 }
141 }
142 }
143 }
144 }
145
146 None
147}
148
149fn extract_attr(attrs: &[Attribute]) -> Option<ValueType> {
150 for attr in attrs {
151 if let Meta::List(MetaList { path, nested, .. }) = attr.parse_meta().unwrap() {
152 for segment in path.segments {
153 if segment.ident == "schema" {
155 for meta in &nested {
156 let path = if let NestedMeta::Meta(Meta::Path(path)) = meta {
157 path
158 } else {
159 panic!("unsupported schema attribute");
160 };
161
162 match path.segments.first().unwrap().ident.to_string().as_ref() {
163 "debug" => return Some(ValueType::Debug),
164 "display" => return Some(ValueType::Display),
165 "serde" => return Some(ValueType::Serde),
166 _ => panic!("unsupported schema attribute"),
167 }
168 }
169 }
170 }
171 }
172 }
173
174 None
175}