use quote::quote;
use crate::derive::parse::{ClapInferredDefault, FieldAttrs};
use super::cli::option_type_tokens;
fn require_named_field(field: &syn::Field) -> Result<&syn::Ident, proc_macro2::TokenStream> {
field.ident.as_ref().ok_or_else(|| {
syn::Error::new_spanned(field, "OrthoConfig defaults structs require named fields")
.to_compile_error()
})
}
pub(crate) fn build_default_struct_fields(
fields: &[syn::Field],
field_attrs: &[FieldAttrs],
) -> Vec<proc_macro2::TokenStream> {
fields
.iter()
.zip(field_attrs.iter())
.filter(|(_, attrs)| !attrs.is_subcommand)
.map(|(f, _)| {
let name = match require_named_field(f) {
Ok(ident) => ident,
Err(err) => return err,
};
let ty = option_type_tokens(&f.ty);
quote! {
#[serde(skip_serializing_if = "Option::is_none")]
pub #name: #ty
}
})
.collect()
}
pub(crate) fn build_default_struct_init(
fields: &[syn::Field],
field_attrs: &[FieldAttrs],
) -> Vec<proc_macro2::TokenStream> {
fields
.iter()
.zip(field_attrs.iter())
.filter(|(_, attr)| !attr.is_subcommand)
.map(|(f, attr)| {
let name = match require_named_field(f) {
Ok(ident) => ident,
Err(err) => return err,
};
let default_expr = attr.default.as_ref().map_or_else(
|| inferred_default_expr(attr.inferred_clap_default.as_ref()),
|expr| Some(quote! { #expr }),
);
default_expr.map_or_else(
|| quote! { #name: None },
|expr| quote! { #name: Some(#expr) },
)
})
.collect()
}
fn inferred_default_expr(
inferred: Option<&ClapInferredDefault>,
) -> Option<proc_macro2::TokenStream> {
inferred.map(|default| match default {
ClapInferredDefault::Value(expr) => {
quote! { #expr }
}
ClapInferredDefault::ValueT(expr) => quote! { #expr },
ClapInferredDefault::ValuesT(expr) => quote! {
::std::iter::IntoIterator::into_iter(#expr)
.collect::<::std::vec::Vec<_>>()
},
})
}