use std::borrow::Cow;
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt};
#[derive(Debug, Clone)]
pub struct BuilderField<'a> {
pub crate_root: &'a syn::Path,
pub field_ident: &'a syn::Ident,
pub field_type: BuilderFieldType<'a>,
pub field_visibility: Cow<'a, syn::Visibility>,
pub attrs: &'a [syn::Attribute],
}
impl<'a> ToTokens for BuilderField<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = self.field_ident;
let vis = &self.field_visibility;
let ty = &self.field_type.with_crate_root(self.crate_root);
let attrs = self.attrs;
tokens.append_all(quote!(
#(#attrs)* #vis #ident: #ty,
));
}
}
impl<'a> BuilderField<'a> {
pub fn default_initializer_tokens(&self) -> TokenStream {
let ident = self.field_ident;
let crate_root = self.crate_root;
quote! { #ident : #crate_root::export::core::default::Default::default(), }
}
}
#[derive(Debug, Clone)]
pub enum BuilderFieldType<'a> {
Optional(&'a syn::Type),
Precise(&'a syn::Type),
Phantom(&'a syn::Type),
}
impl<'a> BuilderFieldType<'a> {
pub fn setter_type_info(&'a self) -> (&'a syn::Type, bool) {
match self {
BuilderFieldType::Optional(ty) => (ty, true),
BuilderFieldType::Precise(ty) => (ty, false),
BuilderFieldType::Phantom(_ty) => panic!("phantom fields should never have setters"),
}
}
fn with_crate_root(&'a self, crate_root: &'a syn::Path) -> BuilderFieldTypeWithCrateRoot<'a> {
BuilderFieldTypeWithCrateRoot {
crate_root,
field_type: self,
}
}
}
struct BuilderFieldTypeWithCrateRoot<'a> {
crate_root: &'a syn::Path,
field_type: &'a BuilderFieldType<'a>,
}
impl<'a> ToTokens for BuilderFieldTypeWithCrateRoot<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let crate_root = self.crate_root;
match self.field_type {
BuilderFieldType::Optional(ty) => tokens.append_all(quote!(
#crate_root::export::core::option::Option<#ty>
)),
BuilderFieldType::Precise(ty) => ty.to_tokens(tokens),
BuilderFieldType::Phantom(ty) => tokens.append_all(quote!(
#crate_root::export::core::marker::PhantomData<#ty>
)),
}
}
}
#[cfg(test)] #[doc(hidden)]
#[macro_export]
macro_rules! default_builder_field {
() => {{
BuilderField {
crate_root: &parse_quote!(::db),
field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
field_type: BuilderFieldType::Optional(Box::leak(Box::new(parse_quote!(String)))),
field_visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)),
attrs: &[parse_quote!(#[some_attr])],
}
}};
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn setter_enabled() {
let field = default_builder_field!();
assert_eq!(
quote!(#field).to_string(),
quote!(
#[some_attr] pub foo: ::db::export::core::option::Option<String>,
)
.to_string()
);
}
#[test]
fn setter_disabled() {
let mut field = default_builder_field!();
field.field_visibility = Cow::Owned(syn::Visibility::Inherited);
field.field_type = match field.field_type {
BuilderFieldType::Optional(ty) => BuilderFieldType::Phantom(ty),
_ => panic!(),
};
assert_eq!(
quote!(#field).to_string(),
quote!(
#[some_attr]
foo: ::db::export::core::marker::PhantomData<String>,
)
.to_string()
);
}
#[test]
fn private_field() {
let private = Cow::Owned(syn::Visibility::Inherited);
let mut field = default_builder_field!();
field.field_visibility = private;
assert_eq!(
quote!(#field).to_string(),
quote!(
#[some_attr]
foo: ::db::export::core::option::Option<String>,
)
.to_string()
);
}
}