#![allow(clippy::option_if_let_else)] use std::convert::identity; use convert_case::{Case, Casing}; use darling::{ ast::{self, Data}, util::{Flag, Override, PathList}, Error, FromDeriveInput, FromField, FromMeta, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{parse_quote, spanned::Spanned}; #[derive(Debug, FromField)] #[darling(attributes(config))] #[darling(forward_attrs(doc))] pub struct ConfigField { ident: Option<syn::Ident>, vis: syn::Visibility, ty: syn::Type, attrs: Vec<syn::Attribute>, min: Option<Override<syn::Expr>>, max: Option<Override<syn::Expr>>, default: Option<Override<syn::Expr>>, typ: Option<syn::Type>, get: Option<syn::Path>, set: Option<syn::Path>, no_serde: Flag, #[darling(skip)] description: Option<syn::Expr>, #[darling(skip)] parent_ident: Option<syn::Ident>, } impl ConfigField { pub(crate) fn needs_newtype(&self) -> bool { self.helper_keys().len() > 1 || self.typ.is_some() } pub(crate) fn helper(&self, attrs: &[syn::Attribute]) -> (Option<TokenStream>, syn::Type) { if self.needs_newtype() { let default = self.helper_default(); let derives = attrs .iter() .find(|attr| attr.path().is_ident("derive")) .map(|attr| { let mut derives = (*PathList::from_meta(&attr.meta).unwrap()).clone(); derives.retain(|derive| { derive.segments.last().map(|s| { let derive = s.ident.to_string(); derive.contains("Tree") || derive.contains("Serialize") || derive.contains("Deserialize") || (default.is_some() && derive.contains("Default")) }) == Some(false) }); quote! {#[derive(#(#derives,)*)]} }); let new_type_ident = self.helper_ident(); let vis = &self.vis; let ty = &self.ty; let new = self.helper_new(); let serde = self.helper_serde(); let tree = self.helper_tree(); ( Some(quote! { #derives #vis struct #new_type_ident(#ty); #new #default #serde #tree }), parse_quote!(#new_type_ident), ) } else { (None, self.ty.clone()) } } pub(crate) fn helper_ident(&self) -> syn::Ident { format_ident!( "__{}{}", self.parent_ident .as_ref() .unwrap_or(&syn::Ident::new("Struct", Span::mixed_site())), self.ident .as_ref() .map_or("Field".to_owned(), |v| v.to_string().to_case(Case::Pascal)) ) } pub(crate) fn has_custom_limits(&self) -> bool { self.typ.is_none() && !((self.min.is_none() || self.min == Some(Override::Inherit)) && (self.max.is_none() || self.max == Some(Override::Inherit))) } pub(crate) fn helper_new(&self) -> TokenStream { let ident = self.helper_ident(); let ty = &self.ty; if self.has_custom_limits() { let min = self.min.as_ref().and_then(|v| v.as_ref().explicit()); let max = self.max.as_ref().and_then(|v| v.as_ref().explicit()); quote! { impl #ident { pub fn new(value: #ty) -> Option<Self> { if (#min..=#max).contains(&value) { Some(Self(value)) } else { None } } #[allow(dead_code)] pub const unsafe fn new_unchecked(value: #ty) -> Self { Self(value) } } impl ::core::ops::Deref for #ident { type Target = #ty; fn deref(&self) -> &Self::Target { &self.0 } } } } else { quote! { impl #ident { pub const fn new(value: #ty) -> Self { Self(value) } } impl ::core::ops::Deref for #ident { type Target = #ty; fn deref(&self) -> &Self::Target { &self.0 } } impl ::core::ops::DerefMut for #ident { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } } } } pub(crate) fn helper_default(&self) -> Option<TokenStream> { if self.typ.is_some() { return None; } self.default.as_ref().map(|default| { let ident = self.helper_ident(); let default_default = parse_quote!(::core::default::Default::default()); let default = default.as_ref().unwrap_or(&default_default); quote! { impl ::core::default::Default for #ident { fn default() -> Self { Self(#default) } } } }) } pub(crate) fn helper_serde(&self) -> Option<TokenStream> { if self.no_serde.is_present() { return None; } let ident = self.helper_ident(); let conversion = if self.has_custom_limits() { quote! { Self::new(value).ok_or_else(|| { <D::Error as ::serde::de::Error>::custom("checking value bounds") }) } } else { quote! { Ok(Self::new(value)) } }; let ty = &self.ty; Some(quote! { impl ::serde::Serialize for #ident { fn serialize<S>(&self, serializer: S) -> ::core::result::Result::<S::Ok, S::Error> where S: ::serde::Serializer, { self.0.serialize(serializer) } } impl<'de> ::serde::Deserialize<'de> for #ident { fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error> where D: ::serde::Deserializer<'de>, { type T = #ty; let value = T::deserialize(deserializer)?; #conversion } } }) } pub(crate) fn helper_tree(&self) -> TokenStream { let mut tokens = self.helper_tree_key(); tokens.extend(self.helper_tree_serialize()); tokens.extend(self.helper_tree_deserialize()); tokens.extend(self.helper_tree_any()); tokens } pub(crate) fn helper_keys(&self) -> Vec<(&'static str, syn::Expr)> { macro_rules! field_to_key { ($field: ident, $default: expr) => { self.$field.as_ref().map(|value| { let ty = self.typ.as_ref().unwrap_or(&self.ty); (stringify!($field), value.clone().explicit().map(|val| { parse_quote!({ fn type_ascribe(val: #ty) -> #ty {val} type_ascribe(#val) }) }).unwrap_or_else(|| { parse_quote!(#ty::$default) })) }) }; ($field: ident) => { self.$field.as_ref().map(|value| { (stringify!($field), value.clone()) }) } } [ Some(("value", parse_quote!(self))), field_to_key!(default, default()), field_to_key!(min, MIN), field_to_key!(max, MAX), field_to_key!(description), ] .into_iter() .flatten() .collect::<Vec<_>>() } pub(crate) fn helper_tree_key(&self) -> TokenStream { let ident = self.helper_ident(); let keys = self.helper_keys(); let num_keys = keys.len(); let keys = keys.iter().map(|(name, _)| name); let max_length = keys.clone().map(|v| v.len()).max(); quote! { impl ::miniconf::KeyLookup for #ident { const LEN: usize = #num_keys; const NAMES: &'static [&'static str] = &[#(#keys,)*]; fn name_to_index(value: &str) -> Option<usize> { Self::NAMES.iter().position(|name| *name == value) } } impl ::miniconf::TreeKey<1> for #ident { fn metadata() -> ::miniconf::Metadata { let mut metadata = ::miniconf::Metadata::default(); metadata.max_depth = 1; metadata.count = #num_keys; metadata.max_length = #max_length; metadata } fn traverse_by_key<K, F, E>(mut keys: K, mut func: F) -> ::core::result::Result<usize, ::miniconf::Error<E>> where K: ::miniconf::Keys, // Writing this to return an iterator instead of using a callback // would have worse performance (O(n^2) instead of O(n) for matching) F: FnMut(usize, Option<&'static str>, usize) -> ::core::result::Result<(), E>, { let ::core::result::Result::Ok(key) = keys.next::<Self>() else { return ::core::result::Result::Ok(0) }; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?; let name = <Self as ::miniconf::KeyLookup>::NAMES .get(index) .ok_or(::miniconf::Traversal::NotFound(1))?; func(index, Some(name), #num_keys).map_err(|err| ::miniconf::Error::Inner(1, err))?; ::core::result::Result::Ok(1) } } } } pub(crate) fn helper_tree_serialize(&self) -> TokenStream { let matches = self .helper_keys() .iter() .enumerate() .skip(1) .map(|(i, (_, expr))| { quote! { #i => ::serde::Serialize::serialize(&#expr, ser).map_err(|err| ::miniconf::Error::Inner(0, err)), } }) .collect::<Vec<_>>(); let ident = self.helper_ident(); let get = self .get .as_ref() .map_or_else(|| quote! {self}, |getter| quote! {#getter(&self.0)}); quote! { impl ::miniconf::TreeSerialize<1> for #ident { fn serialize_by_key<K, S>( &self, mut keys: K, ser: S, ) -> ::core::result::Result<usize, ::miniconf::Error<S::Error>> where K: ::miniconf::Keys, S: ::serde::Serializer, { let ::core::result::Result::Ok(key) = keys.next::<Self>() else { return ::serde::Serialize::serialize(&#get, ser).map_err(|err| ::miniconf::Error::Inner(0, err)).map(|_| 0); }; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(0))?; if !keys.finalize() { return ::core::result::Result::Err(::miniconf::Traversal::TooLong(0).into()); } match index { 0 => ::serde::Serialize::serialize(&#get, ser).map_err(|err| ::miniconf::Error::Inner(0, err)), #(#matches)* _ => unreachable!(), }?; Ok(0) } } } } pub(crate) fn helper_tree_deserialize(&self) -> TokenStream { let ident = self.helper_ident(); let num_keys = self.helper_keys().len(); let set = self.set.as_ref().map_or_else( || quote!((|val| {*self = val; core::result::Result::<_, ::miniconf::Traversal>::Ok(())})), |set| quote!((|val| #set(&mut self.0, val).map_err(|e| ::miniconf::Traversal::Invalid(0, e)))), ); let typ = self .typ .as_ref() .map_or_else(|| quote!(Self), |typ| quote!(#typ)); let match_range = if num_keys > 1 { Some( quote!(1..=#num_keys => ::core::result::Result::Err(::miniconf::Traversal::Access(0, "Cannot write limits").into()),), ) } else { None }; quote! { impl<'de> ::miniconf::TreeDeserialize<'de, 1> for #ident { fn deserialize_by_key<K, D>( &mut self, mut keys: K, de: D, ) -> ::core::result::Result<usize, ::miniconf::Error<D::Error>> where K: ::miniconf::Keys, D: ::serde::Deserializer<'de>, { let ::core::result::Result::Ok(key) = keys.next::<Self>() else { #set(<#typ as ::serde::Deserialize>::deserialize(de).map_err(|err| ::miniconf::Error::Inner(0, err))?)?; return ::core::result::Result::Ok(0); }; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?; if !keys.finalize() { return ::core::result::Result::Err(::miniconf::Traversal::TooLong(1).into()); } match index { 0 => { #set(<#typ as ::serde::Deserialize>::deserialize(de).map_err(|err| ::miniconf::Error::Inner(0, err))?)?; Ok(0) } #match_range _ => unreachable!(), } } } } } pub(crate) fn helper_tree_any(&self) -> TokenStream { let ident = self.helper_ident(); let num_keys = self.helper_keys().len(); let ref_mut = if self.has_custom_limits() { quote! {::core::result::Result::Err(::miniconf::Traversal::Access(0, "field has custom limits"))} } else { quote!(::core::result::Result::Ok(&mut *self)) }; let match_range = if num_keys > 1 { Some( quote!(1..#num_keys => ::core::result::Result::Err(::miniconf::Traversal::Access(1, "cannot return reference to local variable")),), ) } else { None }; quote! { impl ::miniconf::TreeAny<1> for #ident { fn ref_any_by_key<K>(&self, mut keys: K) -> ::core::result::Result<&dyn ::core::any::Any, ::miniconf::Traversal> where K: ::miniconf::Keys, { let ::core::result::Result::Ok(key) = keys.next::<Self>() else { return ::core::result::Result::Ok(&*self); }; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?; if !keys.finalize() { return ::core::result::Result::Err(::miniconf::Traversal::TooLong(1)); } match index { 0 => ::core::result::Result::Ok(&*self), #match_range _ => unreachable!(), } } fn mut_any_by_key<K>(&mut self, mut keys: K) -> ::core::result::Result<&mut dyn ::core::any::Any, ::miniconf::Traversal> where K: ::miniconf::Keys, { let ::core::result::Result::Ok(key) = keys.next::<Self>() else { return #ref_mut; }; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?; if !keys.finalize() { return ::core::result::Result::Err(::miniconf::Traversal::TooLong(1)); } match index { 0 => #ref_mut, #match_range _ => unreachable!(), } } } } } } #[derive(Debug, FromDeriveInput)] #[darling(forward_attrs(derive))] #[darling(supports(struct_any))] pub struct Config { ident: syn::Ident, // generics: syn::Generics, // vis: syn::Visibility, data: ast::Data<(), ConfigField>, pub attrs: Vec<syn::Attribute>, } impl Config { pub(crate) fn parse(input: &syn::DeriveInput) -> Result<Self, Error> { let mut config = Self::from_derive_input(input)?; let ident = config.ident.clone(); let fields = config.fields_mut(); for field in fields.iter_mut() { field.description = field .attrs .iter() .find(|attr| attr.path().is_ident("doc")) .map(|attr| { let description = syn::LitStr::from_meta(&attr.meta).unwrap().value(); let description = description.trim(); parse_quote!(#description) }); field.parent_ident = Some(ident.clone()); let get_set_typ = [ field.typ.is_some(), field.get.is_some(), field.set.is_some(), ]; if get_set_typ.iter().copied().any(identity) && !get_set_typ.into_iter().all(identity) { return Err( Error::custom("need all or none of 'typ', 'get', 'set'").with_span( &field.typ.as_ref().map_or_else( || { field.get.as_ref().map_or_else( || field.set.as_ref().unwrap().span(), Spanned::span, ) }, Spanned::span, ), ), ); } } Ok(config) } pub(crate) fn fields(&self) -> &Vec<ConfigField> { let Data::Struct(fields) = &self.data else { unreachable!() }; &fields.fields } pub(crate) fn fields_mut(&mut self) -> &mut Vec<ConfigField> { let Data::Struct(fields) = &mut self.data else { unreachable!() }; &mut fields.fields } }