use quote::{ToTokens, Tokens};
use syn;
use Bindings;
use Block;
use BuilderPattern;
use DEFAULT_STRUCT_NAME;
#[derive(Debug, Clone)]
pub struct Initializer<'a> {
pub field_ident: &'a syn::Ident,
pub setter_enabled: bool,
pub builder_pattern: BuilderPattern,
pub default_value: Option<Block>,
pub use_default_struct: bool,
pub bindings: Bindings,
}
impl<'a> ToTokens for Initializer<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
trace!("Deriving initializer for `{}`.", self.field_ident);
let struct_field = &self.field_ident;
if self.setter_enabled {
let match_some = self.match_some();
let match_none = self.match_none();
let builder_field = &*struct_field;
tokens.append_all(quote!(
#struct_field: match self.#builder_field {
#match_some,
#match_none,
},
));
} else {
let default = self.default();
tokens.append_all(quote!(
#struct_field: #default,
));
}
}
}
impl<'a> Initializer<'a> {
fn match_some(&'a self) -> MatchSome {
match self.builder_pattern {
BuilderPattern::Owned => MatchSome::Move,
BuilderPattern::Mutable | BuilderPattern::Immutable => {
if self.bindings.no_std {
MatchSome::CloneNoStd
} else {
MatchSome::Clone
}
}
}
}
fn match_none(&'a self) -> MatchNone<'a> {
match self.default_value {
Some(ref expr) => MatchNone::DefaultTo(expr),
None => {
if self.use_default_struct {
MatchNone::UseDefaultStructField(self.field_ident)
} else if self.bindings.no_std {
MatchNone::ReturnErrorNoStd(format!(
"`{}` must be initialized",
self.field_ident
))
} else {
MatchNone::ReturnError(format!("`{}` must be initialized", self.field_ident))
}
}
}
}
fn default(&'a self) -> Tokens {
match self.default_value {
Some(ref expr) => quote!(#expr),
None if self.use_default_struct => {
let struct_ident = syn::Ident::from(DEFAULT_STRUCT_NAME);
let field_ident = self.field_ident;
quote!(#struct_ident.#field_ident)
}
None => {
let default = self.bindings.default_trait();
quote!(#default::default())
}
}
}
}
enum MatchNone<'a> {
DefaultTo(&'a Block),
UseDefaultStructField(&'a syn::Ident),
ReturnError(String),
ReturnErrorNoStd(String),
}
impl<'a> ToTokens for MatchNone<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
MatchNone::DefaultTo(expr) => tokens.append_all(quote!(
None => #expr
)),
MatchNone::UseDefaultStructField(field_ident) => {
let struct_ident = syn::Ident::from(DEFAULT_STRUCT_NAME);
tokens.append_all(quote!(
None => #struct_ident.#field_ident
))
}
MatchNone::ReturnError(ref err) => tokens.append_all(quote!(
None => return ::std::result::Result::Err(::std::string::String::from(#err))
)),
MatchNone::ReturnErrorNoStd(ref err) => tokens.append_all(quote!(
None => return ::core::result::Result::Err(
::alloc::string::String::from(#err))
)),
}
}
}
enum MatchSome {
Move,
Clone,
CloneNoStd,
}
impl<'a> ToTokens for MatchSome {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
MatchSome::Move => tokens.append_all(quote!(
Some(value) => value
)),
MatchSome::Clone => tokens.append_all(quote!(
Some(ref value) => ::std::clone::Clone::clone(value)
)),
MatchSome::CloneNoStd => tokens.append_all(quote!(
Some(ref value) => ::core::clone::Clone::clone(value)
)),
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! default_initializer {
() => {
Initializer {
field_ident: &syn::Ident::from("foo"),
setter_enabled: true,
builder_pattern: BuilderPattern::Mutable,
default_value: None,
use_default_struct: false,
bindings: Default::default(),
}
};
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn immutable() {
let mut initializer = default_initializer!();
initializer.builder_pattern = BuilderPattern::Immutable;
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(ref value) => ::std::clone::Clone::clone(value),
None => return ::std::result::Result::Err(::std::string::String::from(
"`foo` must be initialized"
)),
},
)
);
}
#[test]
fn mutable() {
let mut initializer = default_initializer!();
initializer.builder_pattern = BuilderPattern::Mutable;
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(ref value) => ::std::clone::Clone::clone(value),
None => return ::std::result::Result::Err(::std::string::String::from(
"`foo` must be initialized"
)),
},
)
);
}
#[test]
fn owned() {
let mut initializer = default_initializer!();
initializer.builder_pattern = BuilderPattern::Owned;
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(value) => value,
None => return ::std::result::Result::Err(::std::string::String::from(
"`foo` must be initialized"
)),
},
)
);
}
#[test]
fn default_value() {
let mut initializer = default_initializer!();
initializer.default_value = Some("42".parse().unwrap());
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(ref value) => ::std::clone::Clone::clone(value),
None => { 42 },
},
)
);
}
#[test]
fn default_struct() {
let mut initializer = default_initializer!();
initializer.use_default_struct = true;
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(ref value) => ::std::clone::Clone::clone(value),
None => __default.foo,
},
)
);
}
#[test]
fn setter_disabled() {
let mut initializer = default_initializer!();
initializer.setter_enabled = false;
assert_eq!(
quote!(#initializer),
quote!(foo: ::std::default::Default::default(),)
);
}
#[test]
fn no_std() {
let mut initializer = default_initializer!();
initializer.bindings.no_std = true;
assert_eq!(
quote!(#initializer),
quote!(
foo: match self.foo {
Some(ref value) => ::core::clone::Clone::clone(value),
None => return ::core::result::Result::Err(::alloc::string::String::from(
"`foo` must be initialized"
)),
},
)
);
}
#[test]
fn no_std_setter_disabled() {
let mut initializer = default_initializer!();
initializer.bindings.no_std = true;
initializer.setter_enabled = false;
assert_eq!(
quote!(#initializer),
quote!(foo: ::core::default::Default::default(),)
);
}
}