1use logid_core::log_id::LogLevel;
2use proc_macro::TokenStream;
3use quote::{quote, quote_spanned};
4use syn::{parse_macro_input, DeriveInput};
5
6#[proc_macro_derive(ErrLogId)]
7pub fn derive_err_log_id(input: TokenStream) -> TokenStream {
8 derive_log_id(input, LogLevel::Error)
9}
10
11#[proc_macro_derive(WarnLogId)]
12pub fn derive_warn_log_id(input: TokenStream) -> TokenStream {
13 derive_log_id(input, LogLevel::Warn)
14}
15
16#[proc_macro_derive(InfoLogId)]
17pub fn derive_info_log_id(input: TokenStream) -> TokenStream {
18 derive_log_id(input, LogLevel::Info)
19}
20
21#[proc_macro_derive(DbgLogId)]
22pub fn derive_dbg_log_id(input: TokenStream) -> TokenStream {
23 derive_log_id(input, LogLevel::Debug)
24}
25
26#[proc_macro_derive(TraceLogId)]
27pub fn derive_trace_log_id(input: TokenStream) -> TokenStream {
28 derive_log_id(input, LogLevel::Trace)
29}
30
31fn derive_log_id(input: TokenStream, log_level: LogLevel) -> TokenStream {
32 let input = parse_macro_input!(input as DeriveInput);
33 let ident_name = input.ident;
34
35 let log_token = log_level_as_tokenstream(log_level);
36
37 match input.data {
38 syn::Data::Enum(enum_data) => {
39 let span = ident_name.span();
40
41 let mut field_identifiers = Vec::new();
42
43 for variant in enum_data.variants {
44 let field_name = variant.ident;
45 let full_field_name = match variant.fields {
46 syn::Fields::Named(_) => {
47 quote_spanned! {span=>
48 #ident_name::#field_name{..}
49 }
50 }
51 syn::Fields::Unnamed(_) => {
52 quote_spanned! {span=>
53 #ident_name::#field_name(_)
54 }
55 }
56 _ => quote_spanned! {span=>
57 #ident_name::#field_name
58 },
59 };
60 let full_field_name_str =
61 syn::LitStr::new(&full_field_name.to_string().replace(' ', ""), span);
62 field_identifiers.push(quote_spanned! {span=>
63 #full_field_name => #full_field_name_str,
64 });
65 }
66
67 let from_enum = quote_spanned! {span=>
68 impl From<#ident_name> for logid::log_id::LogId {
69 fn from(value: #ident_name) -> Self {
70 let field_name = match value {
71 #(#field_identifiers)*
72 };
73
74 logid::log_id::LogId::new(
75 module_path!(),
76 field_name,
77 #log_token,
78 )
79 }
80 }
81 };
82
83 TokenStream::from(from_enum)
84 }
85 syn::Data::Struct(struct_data) => {
86 let span = struct_data.struct_token.span;
87 from_struct_or_union(ident_name, log_token, span)
88 }
89 syn::Data::Union(union_data) => {
90 let span = union_data.union_token.span;
91 from_struct_or_union(ident_name, log_token, span)
92 }
93 }
94}
95
96fn from_struct_or_union(
97 ident_name: proc_macro2::Ident,
98 log_token: proc_macro2::TokenStream,
99 span: proc_macro2::Span,
100) -> TokenStream {
101 let ident_name_str = syn::LitStr::new(&ident_name.to_string(), span);
102
103 let from = quote_spanned! {span=>
104 impl From<#ident_name> for logid::log_id::LogId {
105 fn from(value: #ident_name) -> Self {
106 logid::log_id::LogId::new(
107 module_path!(),
108 #ident_name_str,
109 #log_token,
110 )
111 }
112 }
113 };
114
115 TokenStream::from(from)
116}
117
118#[proc_macro_derive(FromLogId)]
119pub fn derive_from_log_id(input: TokenStream) -> TokenStream {
120 let input = parse_macro_input!(input as DeriveInput);
121 let enum_name = input.ident;
122
123 match input.data {
124 syn::Data::Enum(enum_data) => {
125 let span = enum_name.span();
126
127 let mut from_fields = Vec::new();
128
129 for variant in enum_data.variants {
130 let field_name = variant.ident;
131 let full_field_name = quote_spanned! {span=>
132 #enum_name::#field_name
133 };
134 let full_field_name_str =
135 syn::LitStr::new(&full_field_name.to_string().replace(' ', ""), span);
136 from_fields.push(quote_spanned! {span=>
137 v if v == #full_field_name_str => #full_field_name,
138 });
139 }
140
141 let from_log_id = quote_spanned! {span=>
142 impl From<logid::log_id::LogId> for #enum_name {
143 fn from(value: logid::log_id::LogId) -> Self {
144 if value.get_module_path() != module_path!() {
145
146 return Self::default();
147 }
148
149 match value.get_identifier() {
150 #(#from_fields)*
151 _ => Self::default(),
152 }
153 }
154 }
155
156 impl From<logid::logging::intermediary_event::IntermediaryLogEvent> for #enum_name {
157 fn from(value: logid::logging::intermediary_event::IntermediaryLogEvent) -> Self {
158 value.finalize().into_event_id().into()
159 }
160 }
161 };
162
163 TokenStream::from(from_log_id)
164 }
165 _ => panic!("Derive `FromLogId` is only implemented for enumerations where no item has fields (e.g. SomeEnum::Item(Field) is **not** allowed)."),
166 }
167}
168
169fn log_level_as_tokenstream(level: LogLevel) -> proc_macro2::TokenStream {
170 match level {
171 LogLevel::Error => quote! { logid::log_id::LogLevel::Error },
172 LogLevel::Warn => quote! { logid::log_id::LogLevel::Warn },
173 LogLevel::Info => quote! { logid::log_id::LogLevel::Info },
174 LogLevel::Debug => quote! { logid::log_id::LogLevel::Debug },
175 LogLevel::Trace => quote! { logid::log_id::LogLevel::Trace },
176 }
177}