macroconf/src/parser.rs

518 lines
19 KiB
Rust

#![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
}
}