use crate::block::Block;
use crate::initializer::Initializer;
use crate::options::{Include, Parser, Variable};
use crate::{bindings, DEFAULT_STRUCT_NAME};
use darling::ToTokens;
use proc_macro2::{Span, TokenStream};
use quote::TokenStreamExt;
use syn::punctuated::Punctuated;
use syn::Path;
pub struct Builder<'a> {
pub ident: syn::Ident,
pub generics: Option<&'a syn::Generics>,
pub visibility: syn::Visibility,
pub fields: Vec<TokenStream>,
pub functions: Vec<TokenStream>,
pub doc_comment: Option<syn::Attribute>,
pub includes: Vec<Include>,
pub parser: &'a Parser,
pub vars: Vec<Variable>,
pub pre_source_hook: Option<Path>,
}
impl<'a> Builder<'a> {
pub fn push_field<T: ToTokens>(&mut self, f: &T) -> &mut Self {
self.fields.push(quote!(#f));
self
}
pub fn push_method<T: ToTokens>(&mut self, f: &T) -> &mut Self {
self.functions.push(quote!(#f));
self
}
fn compute_impl_bounds(&self) -> syn::Generics {
if let Some(type_gen) = self.generics {
type_gen.clone()
} else {
Default::default()
}
}
}
#[derive(Debug)]
pub struct BuildMethod<'a> {
pub ident: &'a syn::Ident,
pub visibility: syn::Visibility,
pub target_ty: &'a syn::Ident,
pub target_ty_generics: Option<syn::TypeGenerics<'a>>,
pub initializers: Vec<TokenStream>,
pub doc_comment: Option<syn::Attribute>,
pub default_struct: Option<Block>,
pub validate_fn: Option<&'a syn::Path>,
}
impl<'a> FromObject<'a> {
pub fn push_initializer(&mut self, init: Initializer) -> &mut Self {
self.initializers.push(quote!(#init));
self
}
}
#[derive(Debug, Clone)]
pub struct IntoBuilder<'a> {
pub ident: syn::Ident,
pub visibility: syn::Visibility,
pub target_ty: &'a syn::Ident,
pub generics: Option<&'a syn::Generics>,
}
pub struct FromObject<'a> {
pub target_ty: syn::Ident,
pub generics: Option<&'a syn::Generics>,
pub initializers: Vec<TokenStream>,
pub default_struct: Option<Block>,
}
impl<'a> ToTokens for FromObject<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let target_ty = &self.target_ty;
let target_ty_generics = &self.generics;
let initializers = &self.initializers;
let result = bindings::result_ty();
let error_ty = bindings::ucl_object_error();
let try_from = bindings::from_object_trait();
let obj_ref_ty = bindings::ucl_object_ref_ty();
let obj_ty = bindings::ucl_object_ty();
let borrow = bindings::borrow_trait();
tokens.append_all(quote!(
impl #try_from<&#obj_ref_ty> for #target_ty #target_ty_generics {
fn try_from(root: &#obj_ref_ty) -> #result<Self, #error_ty> {
Ok(#target_ty {
#(#initializers)*
})
}
}
impl #try_from<#obj_ref_ty> for #target_ty #target_ty_generics {
fn try_from(source: #obj_ref_ty) -> #result<Self, #error_ty> {
#try_from::try_from(&source)
}
}
impl #try_from<#obj_ty> for #target_ty #target_ty_generics {
fn try_from(source: #obj_ty) -> #result<Self, #error_ty> {
let obj: &#obj_ref_ty = #borrow::borrow(&source);
#try_from::try_from(obj)
}
}
))
}
}
impl<'a> ToTokens for BuildMethod<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = self.ident;
let vis = &self.visibility;
let target_ty = &self.target_ty;
let target_ty_generics = &self.target_ty_generics;
let default_struct = self.default_struct.as_ref().map(|default_expr| {
let ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
quote!(let #ident: #target_ty #target_ty_generics = #default_expr;)
});
let result = bindings::result_ty();
let boxed_error = bindings::boxed_error();
let ucl_error_ty = bindings::ucl_parser_error();
let ucl_obj_error_ty = bindings::ucl_object_error();
let from_obj = bindings::from_object_trait();
tokens.append_all(quote!(
#[doc = "Build target struct or return first encountered error."]
#vis fn #ident(mut self) -> #result<#target_ty #target_ty_generics, #boxed_error> {
#default_struct
let root = self.__parser.get_object().map_err(|e: #ucl_error_ty| e.boxed() as #boxed_error)?;
#from_obj::try_from(root).map_err(|e: #ucl_obj_error_ty| e.boxed() as #boxed_error)
}
))
}
}
impl<'a> ToTokens for Builder<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let builder_vis = &self.visibility;
let builder_ident = &self.ident;
let bounded_generics = self.compute_impl_bounds();
let (impl_generics, _, _) = bounded_generics.split_for_impl();
let (struct_generics, ty_generics, where_clause) = self
.generics
.map(syn::Generics::split_for_impl)
.map(|(i, t, w)| (Some(i), Some(t), Some(w)))
.unwrap_or((None, None, None));
let builder_fields = &self.fields;
let functions = &self.functions;
let derived_traits = {
let traits: Punctuated<&Path, Token![,]> = Default::default();
quote!(#traits)
};
let includes: Vec<TokenStream> =
self.includes.iter().map(|e| e.to_token_stream()).collect();
let vars: Vec<TokenStream> = self.vars.iter().map(ToTokens::to_token_stream).collect();
let builder_doc_comment = &self.doc_comment;
let result_ty = bindings::result_ty();
let ucl_error_ty = bindings::ucl_parser_error();
let parser = self.parser;
let pre_source_hook = if let Some(ref hook) = self.pre_source_hook {
quote! {
#hook(&mut parser)?;
}
} else {
quote!()
};
tokens.append_all(quote!(
#[derive(#derived_traits)]
#builder_doc_comment
#builder_vis struct #builder_ident #struct_generics #where_clause {
#(#builder_fields)*
}
#[allow(dead_code)]
impl #impl_generics #builder_ident #ty_generics #where_clause {
#(#functions)*
#builder_vis fn new() -> #result_ty<Self #ty_generics #where_clause, #ucl_error_ty> {
#parser
#(#vars)*
#pre_source_hook
#(#includes)*
Ok(
Self {
__parser: parser
}
)
}
}
));
}
}
impl<'a> ToTokens for IntoBuilder<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let builder_vis = &self.visibility;
let builder_ident = &self.ident;
let target = &self.target_ty;
let result_ty = bindings::result_ty();
let ucl_error_ty = bindings::ucl_parser_error();
let (_struct_generics, ty_generics, where_clause) = self
.generics
.map(syn::Generics::split_for_impl)
.map(|(i, t, w)| (Some(i), Some(t), Some(w)))
.unwrap_or((None, None, None));
tokens.append_all(quote!(
impl #target {
#builder_vis fn builder() -> #result_ty<#builder_ident #ty_generics #where_clause, #ucl_error_ty> {
#builder_ident::new()
}
}
));
}
}