pub mod doc_howto {}
pub mod doc_generated_code {}
pub mod doc_ref_attrs {}
pub mod doc_differences {}
pub mod doc_magic_types {}
use derive_deftly::define_derive_deftly;
#[doc(hidden)]
pub mod exports {
pub use super::{
ShouldBeCaughtAsSerdeSpecialCase, ShouldBeCaughtAsSpecialCase, ShouldNotBeUsed,
ShouldUseListBuilder, ShouldUseMapBuilder, assert_not_impl, bld_magic_check_type,
bld_magic_cvt, bld_magic_setter_arg_type, bld_magic_setter_cvt, bld_magic_setter_docs,
bld_magic_type, list_element, normalize_and_invoke, strip_option,
};
pub use crate::flatten::derive_deftly_template_Flattenable;
pub use crate::{
ConfigBuildError, define_list_builder_accessors, define_list_builder_helper,
define_map_builder,
extend_builder::{ExtendBuilder, ExtendStrategy},
impl_standard_builder,
load::Buildable as BuildableTrait,
load::Builder as BuilderTrait,
};
pub use derive_deftly::Deftly;
pub use figment;
pub use humantime_serde;
pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use serde_value;
pub use std::{
clone::Clone,
convert::{AsRef, Into, TryInto},
default::Default,
fmt::Debug,
option::Option,
result::Result,
};
pub use tracing;
pub use void;
}
pub mod prelude {
pub use super::derive_deftly_template_TorConfig;
}
#[doc(hidden)]
#[macro_export]
macro_rules! strip_option {
{ $($($(::)? std::)? option::)? Option $(::)? < $t:ty > } => { $t };
{ $t:ty } => { compile_error!{"'strip_option' only works on a type that is an Option<X>"} }
}
pub use strip_option;
#[doc(hidden)]
#[macro_export]
macro_rules! normalize_and_invoke {
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZero $(::)? < $t:ty > >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {$t} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZeroU8 >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {u8} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZeroU16 >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {u16} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZeroU32 >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {u32} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZeroU64 >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {u64} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? num:: )? NonZeroU128 >
} $($args:tt)*} => { $crate::derive::opt_nz_typemagic! { @$cmd {u128} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $( $($(::)? std::)? string:: )? String >
} $($args:tt)*} => { $crate::derive::opt_str_typemagic! { @$cmd $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? option:: )? Option $(::)?
< $t:ty >
} $($args:tt)*} => {$crate::derive::opt_other_typemagic!{@$cmd {$t} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZero $(::)? < $t:ty >} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {$t} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZeroU8} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {u8} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZeroU16} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {u16} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZeroU32} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {u32} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZeroU64} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {u64} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? num:: )? NonZeroU128} $($args:tt)*} => { $crate::derive::nonzero_typemagic!{ @$cmd {u128} $($args)* } };
{ @$cmd:ident {$( $($(::)? std::)? string:: )? String} $($args:tt)*} => { $crate::derive::string_typemagic! { @$cmd $($args)* } };
{ @$cmd:ident {$($t:tt)+} $($args:tt)*} => { $crate::derive::no_typemagic! { @$cmd {$($t)+} $($args)* } };
}
pub use normalize_and_invoke;
#[doc(hidden)]
#[macro_export]
macro_rules! no_typemagic {
{ @build_type {$t:ty} } => { $t };
{ @setter_arg_type {$t:ty}} => { $t };
{ @setter_cvt {$t:ty} {$e:expr} } => { $e };
{ @build_field {$t:ty} {$e:expr} {$fname:expr}} => { $e.clone() };
{ @check_type {$t:ty} } => {
$crate::derive::exports::assert_not_impl!(
[type_was_not_correctly_identified_as_a_TorConfig_special_case]
$t: $crate::derive::ShouldBeCaughtAsSpecialCase
);
};
{ @setter_docs {$t:ty} } => { "" };
}
pub use no_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! string_typemagic {
{ @build_type } => { String };
{ @setter_arg_type } => { impl $crate::setter_traits::StringOrStr };
{ @setter_cvt {$e:expr} } => { $e.to_string() };
{ @build_field {$e:expr} {$fname:expr}} => { $e.clone() };
{ @check_type } => {};
{ @setter_docs } => {
"\nFor convenience, this function accepts both `String` and `&str`.\n"
};
}
pub use string_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! nonzero_typemagic {
{ @build_type {$t:ty} } => { $t };
{ @setter_arg_type {$t:ty} } => { impl $crate::setter_traits::PossiblyBoundsChecked<$t> };
{ @setter_cvt {$t:ty} {$e:expr} } => { $e.to_unchecked() };
{ @build_field {$t:ty} {$e:expr} {$fname:expr}} => {
$e.map(|v|
v.try_into().map_err(|_| $crate::ConfigBuildError::Invalid {
field: $fname.to_string(),
problem: "value not allowed to be zero".to_string()
})).transpose()?
};
{ @check_type {$t:ty} } => {};
{ @setter_docs {$t:ty} } => {
concat!("\nFor convenience, this function accepts both `",
stringify!($t), "` and `NonZero<", stringify!($t), ">`.\n" )
};
}
pub use nonzero_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! opt_nz_typemagic {
{ @build_type {$t:ty} } => { Option<$t> };
{ @setter_arg_type {$t:ty} } => { impl $crate::setter_traits::OptionPossiblyBoundsChecked<$t> };
{ @setter_cvt {$t:ty} {$e:expr} } => { $e.to_option_unchecked() };
{ @build_field {$t:ty} {$e:expr} {$fname:expr}} => {
match $e {
Some(Some(v)) => match v.try_into() {
Ok(n) => Some(Some(n)),
Err(_) => return Err( $crate::ConfigBuildError::Invalid {
field: $fname.to_string(),
problem: "value not allowed to be zero".to_string()
})
}
Some(None) => Some(None),
None => None,
}
};
{ @check_type {$t:ty} } => {};
{ @setter_docs {$t:ty} } => {
concat!("\nFor convenience, this function accepts `",
stringify!($t), "`, `NonZero<", stringify!($t), ">`, `Option<", stringify!($t),
">`, and `Option<NonZero<", stringify!($t), ">`.\n")
};
}
pub use opt_nz_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! opt_str_typemagic {
{ @build_type } => { Option<String> };
{ @setter_arg_type } => { impl $crate::setter_traits::OptionStringOrStr };
{ @setter_cvt {$e:expr} } => { $e.to_option_string() };
{ @build_field {$e:expr} {$fname:expr}} => { $e.clone() };
{ @check_type } => {};
{ @setter_docs } => {
"\nFor convenience, this function accepts `String`, `&str`, \
`Option<String>`, and `Option<&str>`.\n"
};
}
pub use opt_str_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! opt_other_typemagic {
{ @build_type {$t:ty} } => { Option<$t> };
{ @setter_arg_type {$t:ty} } => { impl $crate::setter_traits::PossiblyOption<$t> };
{ @setter_cvt {$t:ty} {$e:expr} } => { $e.to_option() };
{ @build_field {$t:ty} {$e:expr} {$fname:expr}} => { $e.clone() };
{ @check_type {$t:ty} } => {
$crate::derive::exports::assert_not_impl!(
[type_was_not_correctly_identified_as_a_TorConfig_Option_special_case]
$t: $crate::derive::ShouldBeCaughtAsSpecialCase
);
};
{ @setter_docs {$t:ty} } => {
concat!("\nFor convenience, this function accepts both `", stringify!($t),
"` and `Option<", stringify!($T), ">`\n.")
};
}
pub use opt_other_typemagic;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_type {
{ $($t:tt)+ } => { $crate::derive::normalize_and_invoke!{ @build_type {$($t)+} } };
}
pub use bld_magic_type;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_setter_arg_type {
{ $($t:tt)+ } => { $crate::derive::normalize_and_invoke!{ @setter_arg_type {$($t)+} } };
}
pub use bld_magic_setter_arg_type;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_setter_cvt {
{ {$e:expr} {$($t:tt)+}} => { $crate::derive::normalize_and_invoke!{ @setter_cvt {$($t)+} {$e} } };
}
pub use bld_magic_setter_cvt;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_cvt {
{ {$e:expr} {$fname:expr} {$($t:tt)+}} => { $crate::derive::normalize_and_invoke!{ @build_field {$($t)+} {$e} {$fname} } };
}
pub use bld_magic_cvt;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_check_type {
{ {$($t:tt)+}} => { $crate::derive::normalize_and_invoke!{ @check_type {$($t)+} } };
}
pub use bld_magic_check_type;
#[doc(hidden)]
#[macro_export]
macro_rules! bld_magic_setter_docs {
{ {$($t:tt)+} } => { $crate::derive::normalize_and_invoke!{ @setter_docs {$($t)+} } };
}
pub use bld_magic_setter_docs;
mod seal {
pub trait SealSpecialCase {}
pub trait SealSerdeSpecialCase {}
pub trait SealUseListBuilder {}
pub trait SealUseMapBuilder {}
pub trait SealShouldNotBeUsed {}
}
macro_rules! impl_many {
{ $($ty:ty),+ : $trait:ty } =>
{
$( impl $trait for $ty {} )+
};
{ $($ty:ty),+ : $trait:ty , $($more:tt)+} =>
{
impl_many!{ $($ty),+ : $trait }
impl_many!{ $($ty),+ : $($more)+ }
};
}
pub trait ShouldBeCaughtAsSpecialCase: seal::SealSpecialCase {}
impl_many! {
std::num::NonZero<u8>, std::num::NonZero<u16>,
std::num::NonZero<u32>, std::num::NonZero<u64>,
std::num::NonZero<u128>, String
: seal::SealSpecialCase, ShouldBeCaughtAsSpecialCase
}
impl<T> seal::SealSpecialCase for Option<T> {}
impl<T> ShouldBeCaughtAsSpecialCase for Option<T> {}
pub trait ShouldBeCaughtAsSerdeSpecialCase: seal::SealSerdeSpecialCase {}
impl_many! {
std::time::Duration
: seal::SealSerdeSpecialCase, ShouldBeCaughtAsSerdeSpecialCase
}
pub trait ShouldUseListBuilder: seal::SealUseListBuilder {
type Element;
}
impl<T> seal::SealUseListBuilder for Vec<T> {}
impl<T> seal::SealUseListBuilder for std::collections::HashSet<T> {}
impl<T> seal::SealUseListBuilder for std::collections::BTreeSet<T> {}
impl<T> ShouldUseListBuilder for Vec<T> {
type Element = T;
}
impl<T> ShouldUseListBuilder for std::collections::HashSet<T> {
type Element = T;
}
impl<T> ShouldUseListBuilder for std::collections::BTreeSet<T> {
type Element = T;
}
#[doc(hidden)]
#[macro_export]
macro_rules! list_element {
{ $($($(::)? std::)? vec::)? Vec $(::)? < $t:ty > } => { $t };
{ $($($(::)? std::)? collections::)? HashSet $(::)? < $t:ty > } => { $t };
{ $($($(::)? std::)? collections::)? BTreeSet $(::)? < $t:ty > } => { $t };
{ $t:ty } => { compile_error!{"'list_builder' only works on Vec, HashSet, or BTreeSet."} }
}
pub use list_element;
pub trait ShouldUseMapBuilder: seal::SealUseMapBuilder {
type BuilderMap;
}
impl<T> seal::SealUseMapBuilder for std::collections::HashMap<String, T> where
T: crate::load::Buildable
{
}
impl<T> seal::SealUseMapBuilder for std::collections::BTreeMap<String, T> where
T: crate::load::Buildable
{
}
impl<T> ShouldUseMapBuilder for std::collections::HashMap<String, T>
where
T: crate::load::Buildable,
{
type BuilderMap = std::collections::HashMap<String, T::Builder>;
}
impl<T> ShouldUseMapBuilder for std::collections::BTreeMap<String, T>
where
T: crate::load::Buildable,
{
type BuilderMap = std::collections::BTreeMap<String, T::Builder>;
}
pub trait ShouldNotBeUsed: seal::SealShouldNotBeUsed {}
impl_many! {
Option<std::time::Duration>,
Option<Option<std::time::Duration>>
: seal::SealShouldNotBeUsed, ShouldNotBeUsed
}
macro_rules! should_not_be_used_in_collection {
{ $($t:ty),* $(,)?} => {
$(
impl_many!{
Vec<$t>,
std::collections::BTreeSet<$t>,
std::collections::HashSet<$t>
: seal::SealShouldNotBeUsed, ShouldNotBeUsed
}
impl<K> seal::SealShouldNotBeUsed for std::collections::HashMap<K,$t> {}
impl<K> ShouldNotBeUsed for std::collections::HashMap<K,$t> {}
impl<K> seal::SealShouldNotBeUsed for std::collections::BTreeMap<K,$t> {}
impl<K> ShouldNotBeUsed for std::collections::BTreeMap<K,$t> {}
)*
}
}
should_not_be_used_in_collection! {
std::time::Duration,
}
#[macro_export]
macro_rules! assert_not_impl {
{[$rule:ident] $t:ty : $trait:path } => {
const _ : () = {
#[allow(dead_code, non_camel_case_types)]
trait $rule<X> {
fn item();
}
impl $rule<()> for $t { fn item() {}}
struct Invalid;
impl<T : $trait + ?Sized> $rule<Invalid> for T { fn item() {} }
let _ = <$t as $rule<_>>::item;
};
}
}
pub use assert_not_impl;
define_derive_deftly! {
export TorConfig beta_deftly, for struct:
#[allow(unused_imports)]
use $crate::derive::exports as $<__tor_config_exports__ $tname>;
${define E {$crate::derive::exports}}
${define EX ${concat "__tor_config_exports__" $tname}}
${define FNAME { ${concat $fname} }}
${define BLD_NAME {
${tmeta(tor_config(build_fn(name))) as ident, default build}
} }
${defcond F_IS_DURATION
any(approx_equal({$ftype}, {Duration}),
approx_equal({$ftype}, {time::Duration}),
approx_equal({$ftype}, {std::time::Duration}),
approx_equal({$ftype}, {::std::time::Duration}))
}
${defcond F_SERDE_MAGIC any(F_IS_DURATION)}
${defcond SERDE
not(all(tmeta(tor_config(no_serialize_trait)),
tmeta(tor_config(no_deserialize_trait))))
}
${define BLD_MAGIC_ATTRIBUTES
${if fmeta(tor_config(no_magic)) {
} else if SERDE {
${select1
F_IS_DURATION {
${if SERDE {
#[serde(with = ${concat $EX "::humantime_serde::option"})]
}}
}
else {
${if F_SERDE_MAGIC {
${error "Type should receive magic handling with serde, but we failed to apply any."}
}}
}}
}}
}
${define F_LST_BLD_TYPE { $<
${fmeta(tor_config(list(listtype))) as ty,
default ${paste $tname ${upper_camel_case $fname} List}
} Builder>} }
${define F_MAP_TYPE { ${paste ${fmeta(tor_config(map(maptype))) as ty,
default ${paste $tname ${upper_camel_case $fname} Map}}
}}}
${define F_MAP_BLD_TYPE { $< $F_MAP_TYPE Builder > }}
${define BLD_MAGIC_FTYPE {
${if fmeta(tor_config(no_magic)) {
$ftype
} else {
$E::bld_magic_type!($ftype)
}}
}}
${define BLD_FTYPE {
${if fmeta(tor_config(field(ty))) {
${fmeta(tor_config(field(ty))) as ty}
} else if fmeta(tor_config(sub_builder)) {
$< $ftype Builder >
} else if fmeta(tor_config(list)) {
$F_LST_BLD_TYPE
} else if fmeta(tor_config(map)) {
$F_MAP_BLD_TYPE
} else {
$E::Option<$BLD_MAGIC_FTYPE>
}}
}}
${define ERR
${tmeta(tor_config(build_fn(error))) as ty, default {$E::ConfigBuildError}}}
${define IF_CFG {
${if fmeta(tor_config(cfg)) {
#[cfg( ${fmeta(tor_config(cfg)) as token_stream} )]
}}
}}
${define BLD_TVIS
${tmeta(tor_config(vis)) as token_stream, default $tvis}
}
${define BLD_FVIS
${fmeta(tor_config(field(vis))) as token_stream, default {} }
}
${defcond DD_FLATTENABLE_ON_BUILDER
all(not(tmeta(tor_config(no_deserialize_trait))),
not(tmeta(tor_config(no_flattenable_trait)))
)}
${define SETTER_VIS {
${fmeta(tor_config(setter(vis))) as token_stream, default $BLD_TVIS}
}}
$(
${if fmeta(tor_config(no_magic)) {
} else {
$E::bld_magic_check_type!({$ftype});
}}
${if all(not(F_SERDE_MAGIC), not(fmeta(tor_config(no_magic))) ) {
$E::assert_not_impl!(
[type_was_not_correctly_identified_as_a_TorConfig_serde_special_case]
$ftype: $E::ShouldBeCaughtAsSerdeSpecialCase
);
}}
)
$(
${when not(fmeta(tor_config(no_magic)))}
$E::assert_not_impl!(
[field_type_not_suitable_for_configuration]
$ftype: $E::ShouldNotBeUsed
);
)
$(
${when not(any(
fmeta(tor_config(sub_builder)),
fmeta(tor_config(no_sub_builder)),
fmeta(tor_config(no_magic)),
fmeta(tor_config(build)),
fmeta(tor_config(try_build)),
))}
$E::assert_not_impl!(
[missing_sub_builder_declaration_for_Buildable_field]
$ftype: $E::BuildableTrait
);
)
$(
${when not(any(
fmeta(tor_config(no_sub_builder)),
fmeta(tor_config(no_magic)),
fmeta(tor_config(list)),
))}
$E::assert_not_impl!(
[field_should_use_list_builder_or_opt_out]
$ftype: $E::ShouldUseListBuilder
);
)
$(
${when not(any(
fmeta(tor_config(no_sub_builder)),
fmeta(tor_config(no_magic)),
fmeta(tor_config(map)),
))}
$E::assert_not_impl!(
[field_should_use_map_builder_or_opt_out]
$ftype: $E::ShouldUseMapBuilder
);
)
#[doc = ${concat "A builder to create an instance of [`" $tname "`].\n"}]
#[derive($E::Default, $E::Clone, $E::Debug, $E::Deftly)]
${if not(tmeta(tor_config(no_deserialize_trait))) {
#[derive($E::Deserialize)]
}}
${if not(tmeta(tor_config(no_serialize_trait))) {
#[derive($E::Serialize)]
}}
${if DD_FLATTENABLE_ON_BUILDER {
#[derive_deftly($E::Flattenable)]
}}
${ if tmeta(tor_config(attr)) {
#[ ${tmeta(tor_config(attr)) as token_stream} ]
}}
#[allow(dead_code)]
$BLD_TVIS struct $<$tname Builder><$tdefgens>
where $twheres
{
$(
${when not(fmeta(tor_config(skip)))}
${if fmeta(tor_config(attr)) {
#[${fmeta(tor_config(attr)) as token_stream}]
}}
${if SERDE {
#[serde(default)]
}}
${ if fmeta(tor_config(serde)) {
#[ serde( ${fmeta(tor_config(serde)) as token_stream} )]
}}
#[doc = ${concat "In-progress value for " $fname ".\n\n"
"See [`" $tname "." $fname "`]("$tname "#structfield." $fname ")"}]
$BLD_MAGIC_ATTRIBUTES
$IF_CFG
$BLD_FVIS ${fdefine $fname} $BLD_FTYPE,
${if all(SERDE, fmeta(tor_config(cfg))) {
#[cfg(not(${fmeta(tor_config(cfg)) as token_stream} ))]
#[serde(default)]
${fdefine $fname} $E::Option<$E::serde_value::Value>,
}}
)
}
${define BUILD_LIST_ELEMENT {
${select1
fmeta(tor_config(list(element(build)))) {
|v| v.${fmeta(tor_config(list(element(build)))) as ident, default build}()
}
fmeta(tor_config(list(element(clone)))) {
|v| Ok(v.clone())
}
else {
${error "With list, must specify list(element(clone)) or list(element(build))"}
}}
}}
${define BLD_LIST_ELT_TYPE {
${select1
fmeta(tor_config(list(element(build)))) {
<<$ftype as $E::ShouldUseListBuilder>::Element as $E::BuildableTrait>::Builder
}
fmeta(tor_config(list(element(clone)))) {
$E::list_element!{ $ftype }
}
else {
${error "With list, must specify list(element(clone)) or list(element(build))"}
}}
}}
$(
${when fmeta(tor_config(list))}
$E::define_list_builder_helper! {
#[doc = ${concat "Builder for the `" $ftype "` type.\n\n"}]
$SETTER_VIS struct $F_LST_BLD_TYPE {
$BLD_FVIS $fname: [
$BLD_LIST_ELT_TYPE
],
}
built: $ftype = $fname;
default = ${fmeta(tor_config(default)) as expr};
item_build: $BUILD_LIST_ELEMENT;
}
)
$(
${when fmeta(tor_config(map))}
$E::define_map_builder! {
#[doc = ${concat "Builder for the `" $F_MAP_TYPE "` type.\n\n"}]
$SETTER_VIS struct $F_MAP_BLD_TYPE =>
$BLD_FVIS type $F_MAP_TYPE = {
map: $ftype,
builder_map: <$ftype as $E::ShouldUseMapBuilder>::BuilderMap,
}
defaults: ${fmeta(tor_config(default)) as expr};
}
)
${define SETTER_NAME { ${fmeta(tor_config(setter(name))) as ident, default $fname} }}
${define SETTER_INPUT_TYPE {
${select1
fmeta(tor_config(setter(into))) {
impl $E::Into<$ftype>
}
fmeta(tor_config(setter(try_into))) {
SetterArg
}
fmeta(tor_config(setter(strip_option))) {
$E::strip_option!{$ftype}
}
else {
${if fmeta(tor_config(no_magic)) {
$ftype
} else {
$E::bld_magic_setter_arg_type!{$ftype}
}
}}
}}}
${define SETTER_GENS {
${if fmeta(tor_config(setter(try_into))) {
<SetterArg : $E::TryInto<$ftype>>
} else {
}}
}}
${define SETTER_RETURN {
${if fmeta(tor_config(setter(try_into))) {
Ok(self)
} else {
self
}}
}}
${define SETTER_RETURN_TYPE {
${if fmeta(tor_config(setter(try_into))) {
$E::Result<&mut Self, SetterArg::Error>
} else {
&mut Self
}}
}}
${define DFLT_DOC {
${select1
fmeta(tor_config(sub_builder)) { "" }
fmeta(tor_config(build)) { "" }
fmeta(tor_config(try_build)) { "" }
fmeta(tor_config(default)) {
${concat "If no value is provided for `" $fname "`, "
"[`build`](Self::build) will use `"
${fmeta(default) as str, default "Default::default()"}
"`."
}}
fmeta(tor_config(no_default)) {
${concat "If no value is provided for `" $fname "`, "
"[`build`](Self::build) will fail with an error."}
}
else {
${error "Every field must have default, no_default, try_build, build, or sub_builder."}
}
}
}}
${define SET_FN {
#[doc = ${concat "Provide a value for `" $fname "`.\n\n"}]
#[doc = $DFLT_DOC]
${if not(fmeta(tor_config(no_magic))) {
#[doc = $E::bld_magic_setter_docs!{ { $ftype } } ]
}}
#[doc = ${concat "\n\n## " $fname "\n\n" }]
${fattrs doc}
$IF_CFG
$SETTER_VIS fn $SETTER_NAME $SETTER_GENS (&mut self, val: $SETTER_INPUT_TYPE) -> $SETTER_RETURN_TYPE {
${select1
fmeta(tor_config(setter(into))) {
self.$fname = Some(val.into());
}
fmeta(tor_config(setter(try_into))) {
self.$fname = Some(val.try_into()?);
}
fmeta(tor_config(setter(strip_option))) {
self.$fname = Some(Some(val));
}
else {
${if fmeta(tor_config(no_magic)) {
self.$fname = Some(val);
} else {
self.$fname = Some($E::bld_magic_setter_cvt!({val} {$ftype}));
}}
}
}
$SETTER_RETURN
}
}}
${define F_SUB_BUILDER_TYPE
${if fmeta(tor_config(map)) {
$F_MAP_BLD_TYPE
} else {
$<$ftype Builder>
}}
}
${define ACCESS_SUBBUILDER_FN {
#[doc = ${concat "Return a mutable reference to the inner builder for `" $fname "`.\n\n"
"## " $fname "\n\n"
}]
${fattrs doc}
$IF_CFG
$SETTER_VIS fn $fname(&mut self) -> &mut $F_SUB_BUILDER_TYPE {
&mut self.$fname
}
}}
#[allow(dead_code)]
impl<$tgens> $<$ttype Builder>
where $twheres {
$(
${if any(fmeta(tor_config(setter(skip))),
fmeta(tor_config(skip)),
fmeta(tor_config(list))) {
} else if any(fmeta(tor_config(sub_builder)),
fmeta(tor_config(map))) {
$ACCESS_SUBBUILDER_FN
} else {
$SET_FN
}}
)
}
$(
${when fmeta(tor_config(list))}
$E::define_list_builder_accessors!{
struct $<$tname Builder> {
$SETTER_VIS $fname : [
$BLD_LIST_ELT_TYPE
],
}
}
)
${define SUB_BUILDER_BUILD_FN {
${fmeta(tor_config(sub_builder(build_fn))) as path, default build}
}}
${define BLD_MAGIC_CVT {
${if fmeta(tor_config(no_magic)) {
self.$fname.clone()
} else {
$E::bld_magic_cvt!({self.$fname} {${concat $fname}} {$ftype})
}}
}}
${define BLD_MISSING_FIELD {
${tmeta(tor_config(missing_field)) as expr, default {
|name_of_missing_field: &str| $ERR::MissingField { field: name_of_missing_field.into() }
}}
}}
${define BUILD_FIELD {
${select1
any(fmeta(tor_config(sub_builder)),
fmeta(tor_config(list)),
fmeta(tor_config(map))) {
self.$fname.$SUB_BUILDER_BUILD_FN().map_err(|e| e.within($FNAME))?
}
all(fmeta(tor_config(default)), not(any(fmeta(tor_config(list)),
fmeta(tor_config(map))))) {
$BLD_MAGIC_CVT.unwrap_or_else(
|| ${fmeta(tor_config(default)) as expr, default {Default::default()}})
}
fmeta(tor_config(build)) {
(${fmeta(tor_config(build)) as expr})(self)
}
fmeta(tor_config(try_build)) {
(${fmeta(tor_config(try_build)) as expr})(self)?
}
fmeta(tor_config(no_default)) {
$BLD_MAGIC_CVT.ok_or_else(
|| { ($BLD_MISSING_FIELD)(stringify!($fname)) }
)?
}
else {
${error "Every field must have default, no_default, try_build, build, or sub_builder."}
}
}
}}
${define BLD_FN_VIS {
${tmeta(tor_config(build_fn(vis))) as token_stream, default $BLD_TVIS}
}}
#[allow(dead_code)]
impl<$tgens> $<$ttype Builder>
where $twheres {
$BLD_TVIS fn new() -> Self {
Self::default()
}
#[doc = ${concat
"Try to construct a new [`" $tname "`] from the fields set in this builder.\n\n"
"Return an error if any required field is missing, or is set to something invalid.\n"
}]
$BLD_FN_VIS fn $BLD_NAME(&self) -> $E::Result<$ttype, $ERR> {
${if tmeta(tor_config(pre_build)) {
let () = ${tmeta(tor_config(pre_build)) as path}(self)?;
}}
$(
${if fmeta(tor_config(cfg)) {
#[cfg(not( ${fmeta(tor_config(cfg)) as token_stream} ))]
if self.$fname.is_some() {
${if fmeta(tor_config(cfg_reject)) {
return Err($E::ConfigBuildError::NoCompileTimeSupport {
field: stringify!($fname).to_string(),
problem: ${concat "The program was not built "
${fmeta(tor_config(cfg_desc)) as str}
}.to_string()
});
} else {
$E::tracing::warn!(
${concat "Ignored configuration for '" $fname
"'. This option has no effect unless the program is built "
${fmeta(tor_config(cfg_desc)) as str} "'"}
)
}}
}
}}
)
let result = $tname {
$(
$IF_CFG
$fname: $BUILD_FIELD ,
${if fmeta(tor_config(cfg)) {
#[cfg(not( ${fmeta(tor_config(cfg)) as token_stream} ))]
$fname: $E::Default::default(),
}}
)
};
${if tmeta(tor_config(post_build)) {
let result = ${tmeta(tor_config(post_build)) as path}(result)?;
}}
Ok(result)
}
}
impl<$tgens> $ttype
where $twheres {
#[doc = ${concat "Return a new [`" $tname " Builder`] to construct an instance of this type."}]
#[allow(dead_code)]
$tvis fn builder() -> $<$ttype Builder> {
$<$ttype Builder>::default()
}
}
${if not(tmeta(tor_config(no_builder_trait))) {
impl<$tgens> $E::BuilderTrait for $<$ttype Builder>
where $twheres {
type Built = $ttype;
#[allow(clippy::needless_question_mark)]
fn build(&self) -> $E::Result<$ttype, $E::ConfigBuildError> {
Ok($<$ttype Builder>::$BLD_NAME(self)?)
}
}
}}
${if not(tmeta(tor_config(no_extendbuilder_trait))) {
impl<$tgens> $E::ExtendBuilder for $<$ttype Builder>
where $twheres {
#[allow(unused_variables)]
fn extend_from(&mut self, other: Self, strategy: $E::ExtendStrategy) {
${for fields {
${when not(fmeta(tor_config(skip)))}
${if fmeta(tor_config(cfg)) {
#[cfg(not( ${fmeta(tor_config(cfg)) as token_stream} ))]
if other.$fname.is_some() {
self.$fname = other.$fname;
}
#[cfg( ${fmeta(tor_config(cfg)) as token_stream} )]
}}
{
${if fmeta(tor_config(extend_with)) {
${fmeta(tor_config(extend_with)) as expr}(&mut self.$fname, other.$fname, strategy);
} else if fmeta(tor_config(extend_with_replace)) {
if let Some(other_val) = other.$fname {
self.$fname = Some(other_val);
}
} else if any(fmeta(tor_config(sub_builder)),
fmeta(tor_config(list)),
fmeta(tor_config(map))) {
$E::ExtendBuilder::extend_from(&mut self.$fname, other.$fname, strategy);
} else {
if let Some(other_val) = other.$fname {
self.$fname = Some(other_val);
}
}}
}
}}
}
}
}}
${if not(tmeta(tor_config(no_buildable_trait))) {
impl<$tgens> $E::BuildableTrait for $ttype
where $twheres {
type Builder = $<$ttype Builder>;
fn builder() -> $<$ttype Builder> {
$<$ttype Builder>::default()
}
}
}}
${if not(tmeta(tor_config(no_default_trait))) {
impl<$tgens> $E::Default for $ttype
where $twheres {
fn default() -> Self {
$<$ttype Builder>::default().$BLD_NAME().unwrap()
}
}
}}
${if not(any(
tmeta(tor_config(no_default_trait)),
tmeta(tor_config(no_deserialize_trait)),
tmeta(tor_config(no_test_default))))
{
#[cfg(test)]
mod $<test_ ${snake_case $tname} _builder> {
#[test]
fn test_impl_default() {
let def = super::$ttype::default();
let empty_config = $E::figment::Figment::new();
let builder: super::$<$ttype Builder> = empty_config.extract().unwrap();
let from_empty = builder.$BLD_NAME().unwrap();
assert_eq!(def, from_empty);
}
}
}}
}
pub use derive_deftly_template_TorConfig;
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use crate::ConfigBuildError;
use assert_matches::assert_matches;
use tracing_test::traced_test;
mod t {
use std::{
collections::{BTreeSet, HashMap},
num::{NonZero, NonZeroU8},
time::Duration,
};
use derive_deftly::Deftly;
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct Simple {
#[deftly(tor_config(default))]
pub(super) xyz: u32,
#[deftly(tor_config(default = "3"))]
pub(super) abc: u16,
#[deftly(tor_config(build = "|_self| 6 * 7"))]
pub(super) forty_two: u16,
#[deftly(tor_config(
try_build = "|_self| Ok::<_,crate::ConfigBuildError>(6 * 7 + 1)"
))]
pub(super) forty_three: u16,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(no_default_trait))]
pub(super) struct FieldNoDefault {
#[deftly(tor_config(default))]
pub(super) has_default: u32,
#[deftly(tor_config(no_default))]
pub(super) has_no_default: u32,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(no_default_trait))]
pub(super) struct Sub {
#[deftly(tor_config(sub_builder))]
pub(super) simple: Simple,
#[deftly(tor_config(sub_builder))]
pub(super) fnd: FieldNoDefault,
#[deftly(tor_config(default))]
pub(super) s: String,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct Magic {
#[deftly(tor_config(default = "nz(7)"))]
pub(super) nzu8: NonZeroU8,
#[deftly(tor_config(default = "nz(123)"))]
pub(super) nzu8_2: NonZero<u8>,
#[deftly(tor_config(default))]
pub(super) dur: Duration,
#[deftly(tor_config(default))]
pub(super) s: String,
}
fn nz(x: u8) -> NonZeroU8 {
x.try_into().unwrap()
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct CfgEnabled {
#[deftly(tor_config(
default,
cfg = "all()",
cfg_desc = "with eschaton immenentization"
))]
pub(super) flower_power: u32,
#[deftly(tor_config(
default,
cfg = "all()",
cfg_reject,
cfg_desc = "with eschaton immenentization"
))]
pub(super) flower_power_err: u32,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct CfgDisabled {
#[deftly(tor_config(
default,
cfg = "any()",
cfg_desc = "with resublimated thiotimoline"
))]
pub(super) time_travel: u32,
#[deftly(tor_config(
default,
cfg = "any()",
cfg_reject,
cfg_desc = "with resublimated thiotimoline"
))]
pub(super) time_travel_err: u32,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(
pre_build = "Self::check_odd",
post_build = "CfgValidating::check_even"
))]
pub(super) struct CfgValidating {
#[deftly(tor_config(default = "1"))]
pub(super) odd: u32,
#[deftly(tor_config(default))]
pub(super) even: u32,
}
impl CfgValidatingBuilder {
fn check_odd(&self) -> Result<(), crate::ConfigBuildError> {
if let Some(v) = self.odd {
if v & 1 != 1 {
return Err(crate::ConfigBuildError::Invalid {
field: "odd".to_string(),
problem: "Not odd".to_string(),
});
}
}
Ok(())
}
}
impl CfgValidating {
fn check_even(self) -> Result<Self, crate::ConfigBuildError> {
if self.even & 1 != 0 {
return Err(crate::ConfigBuildError::Invalid {
field: "even".to_string(),
problem: "Not even".to_string(),
});
}
Ok(self)
}
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(
no_serialize_trait,
no_test_default,
no_extendbuilder_trait,
no_flattenable_trait
))]
pub(super) struct CfgGeneric<T, U>
where
T: Clone + std::fmt::Debug + PartialEq + Default,
U: Clone + std::fmt::Debug + PartialEq + Default,
{
#[deftly(tor_config(default, no_magic))]
pub(super) t: T,
#[deftly(tor_config(default, no_magic))]
pub(super) u: U,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct OptionsCfg {
#[deftly(tor_config(default))]
pub(super) a: Option<u32>,
#[deftly(tor_config(default = "Some(123)"))]
pub(super) b: Option<u32>,
#[deftly(tor_config(default = "Some(nz(42))"))]
pub(super) nz: Option<NonZeroU8>,
#[deftly(tor_config(default))]
pub(super) s: Option<String>,
#[deftly(tor_config(default))]
pub(super) other: Option<(u32, u32)>,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(attr = "derive(Eq,Ord,PartialOrd,PartialEq)"))]
pub(super) struct AttribsCfg {
#[deftly(tor_config(default))]
#[deftly(tor_config(attr = r#"serde(alias = "fun_with_numbers")"#))]
#[deftly(tor_config(serde = r#"alias = "its_fun_to_count""#))]
pub(super) a: u32,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct SettersCfg {
#[deftly(tor_config(default, setter(skip)))]
pub(super) a: u32,
#[deftly(tor_config(default, setter(into)))]
pub(super) b: u32,
#[deftly(tor_config(default, setter(try_into)))]
pub(super) c: u32,
#[deftly(tor_config(default, setter(strip_option)))]
pub(super) d: Option<u32>,
#[deftly(tor_config(default, setter(name = "set_the_e")))]
pub(super) e: u32,
}
impl SettersCfgBuilder {
pub(super) fn a(&mut self, val: u32) {
self.a = Some(val * 2);
}
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(no_serialize_trait, no_deserialize_trait))]
pub(super) struct NoSerdeCfg {
#[deftly(tor_config(default))]
pub(super) ip: u128,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
#[deftly(tor_config(build_fn(name = "build_this")))]
pub(super) struct RenameBuild {
#[deftly(tor_config(default))]
pub(super) member: u16,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct RenameSubBuild {
#[deftly(tor_config(sub_builder(build_fn = "build_this")))]
pub(super) inner: RenameBuild,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct FullyCustom {
#[deftly(tor_config(
setter(skip),
field(ty = "(u32, u32)", vis = "pub(super)"),
build = r#"|this: &Self| format!("{} {}", this.value.0, this.value.1)"#,
extend_with = r#"|mine: &mut (u32,u32), theirs: (u32,u32), _| *mine = theirs"#,
))]
pub(super) value: String,
}
impl FullyCustomBuilder {
pub(super) fn try_the_thing(&mut self, x: u64) {
self.value = ((x >> 32) as u32, x as u32);
}
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct FieldSkipCfg {
#[deftly(tor_config(skip, build = r#"|_this: &Self| 25"#))]
pub(super) value: u32,
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct ListsCfg {
#[deftly(tor_config(list(element(clone)), default = "vec![7]"))]
pub(super) integers: Vec<u32>,
#[deftly(tor_config(
list(listtype = "StringSet", element(clone)),
default = "cats()"
))]
pub(super) cats: BTreeSet<String>,
#[deftly(tor_config(list(element(build)), default = "vec![]"))]
pub(super) simple: Vec<Simple>,
}
fn cats() -> Vec<String> {
["Damiano", "Moonbeam", "Checkers", "Enigma"]
.iter()
.map(|s| s.to_string())
.collect()
}
#[derive(Deftly, Clone, Debug, PartialEq)]
#[derive_deftly(TorConfig)]
pub(super) struct MapCfg {
#[deftly(tor_config(map, default = "default_map()"))]
pub(super) map: HashMap<String, Simple>,
}
fn default_map() -> HashMap<String, SimpleBuilder> {
let mut b = SimpleBuilder::new();
b.abc(32);
b.xyz(123);
let mut m = HashMap::new();
m.insert("pangolin".to_string(), b);
m
}
}
#[test]
fn test_simple_defaults() {
let b = t::SimpleBuilder::new();
let x = b.build().unwrap();
assert_eq!(x.xyz, 0);
assert_eq!(x.abc, 3);
assert_eq!(x.forty_two, 42);
assert_eq!(x.forty_three, 43);
}
#[test]
fn test_simple_setters() {
let mut b = t::SimpleBuilder::new();
let x = b.abc(7).xyz(77).forty_two(777).build().unwrap();
assert_eq!(x.xyz, 77);
assert_eq!(x.abc, 7);
assert_eq!(x.forty_two, 42);
assert_eq!(x.forty_three, 43);
}
#[test]
fn test_simple_serde() {
let v = r#"
xyz = 7
"#;
let b: t::SimpleBuilder = toml::from_str(v).unwrap();
let x = b.build().unwrap();
assert_eq!(x.xyz, 7);
assert_eq!(x.abc, 3);
assert_eq!(x.forty_two, 42);
assert_eq!(x.forty_three, 43);
}
#[test]
fn test_field_no_default() {
let e = t::FieldNoDefaultBuilder::new().build().unwrap_err();
assert_matches!(
e,
ConfigBuildError::MissingField {
field
} if field == "has_no_default"
);
let v = t::FieldNoDefaultBuilder::new()
.has_no_default(42)
.build()
.unwrap();
assert_eq!(v.has_default, 0);
assert_eq!(v.has_no_default, 42);
}
#[test]
fn test_subbuilder() {
let e = t::SubBuilder::new().build().unwrap_err();
assert_matches!(
e,
ConfigBuildError::MissingField {
field
} if field == "fnd.has_no_default"
);
let mut b = t::SubBuilder::new();
b.fnd().has_no_default(5).has_default(66);
b.simple().abc(123);
b.s("Hello");
let v = b.build().unwrap();
assert_eq!(v.fnd.has_no_default, 5);
assert_eq!(v.fnd.has_default, 66);
assert_eq!(v.simple.abc, 123);
assert_eq!(v.simple.xyz, 0);
assert_eq!(v.s, "Hello");
}
#[test]
fn test_subbuilder_serde() {
let v = r#"
s = "hello world"
[fnd]
has_no_default = 1234
"#;
let b: t::SubBuilder = toml::from_str(v).unwrap();
let x = b.build().unwrap();
assert_eq!(x.fnd.has_no_default, 1234);
assert_eq!(x.fnd.has_default, 0);
assert_eq!(x.s, "hello world");
}
#[test]
fn test_magic_nz() {
let mut b = t::Magic::builder();
b.nzu8(123);
b.nzu8_2(1);
let v = b.build().unwrap();
assert_eq!(v.nzu8.get(), 123);
assert_eq!(v.nzu8_2.get(), 1);
let e = t::MagicBuilder::new().nzu8(0).build().unwrap_err();
let ConfigBuildError::Invalid { field, problem } = e else {
panic!("Error not as expected ({e:?})");
};
assert_eq!(field, "nzu8");
assert_eq!(problem, "value not allowed to be zero");
let e = t::MagicBuilder::new().nzu8_2(0).build().unwrap_err();
let ConfigBuildError::Invalid { field, problem } = e else {
panic!("Error not as expected ({e:?})");
};
assert_eq!(field, "nzu8_2");
assert_eq!(problem, "value not allowed to be zero");
}
#[test]
fn test_magic_string() {
let mut b = t::Magic::builder();
b.s("hello"); let v = b.build().unwrap();
assert_eq!(v.s, "hello");
#[allow(clippy::unnecessary_to_owned)]
b.s("world".to_string());
let v = b.build().unwrap();
assert_eq!(v.s, "world");
}
#[test]
fn test_magic_duration() {
let v = r#"
dur = "1 hour"
"#;
let b: t::MagicBuilder = toml::from_str(v).unwrap();
let x = b.build().unwrap();
assert_eq!(x.dur, std::time::Duration::new(60 * 60, 0));
}
#[test]
#[traced_test]
fn test_cfg_enabled() {
let s = r#"
flower_power = 12
flower_power_err = 14
"#;
let b: t::CfgEnabledBuilder = toml::from_str(s).unwrap();
let v = b.build().unwrap();
assert_eq!(v.flower_power, 12);
assert_eq!(v.flower_power_err, 14);
assert!(!logs_contain("no effect"));
}
#[test]
#[traced_test]
fn test_cfg_disabled() {
let s = r#"
time_travel = "hello world"
"#;
let b: t::CfgDisabledBuilder = toml::from_str(s).unwrap();
let v = b.build().unwrap();
assert_eq!(v.time_travel, 0);
assert!(logs_contain(
"Ignored configuration for 'time_travel'. \
This option has no effect unless the program is built with resublimated thiotimoline"
));
let s = r#"
time_travel_err = "hello world"
"#;
let b: t::CfgDisabledBuilder = toml::from_str(s).unwrap();
let e = b.build().unwrap_err();
assert_matches!(e, ConfigBuildError::NoCompileTimeSupport { .. });
}
#[test]
fn test_validating() {
let err_notodd = t::CfgValidating::builder().odd(6).build().unwrap_err();
assert_eq!(
err_notodd.to_string(),
"Value of odd was incorrect: Not odd"
);
let err_noteven = t::CfgValidating::builder().even(5).build().unwrap_err();
assert_eq!(
err_noteven.to_string(),
"Value of even was incorrect: Not even"
);
let v = t::CfgValidating::builder().odd(5).even(6).build().unwrap();
assert_eq!(v.odd, 5);
assert_eq!(v.even, 6);
}
#[test]
fn test_generic() {
let mut b = t::CfgGeneric::<String, Vec<u32>>::builder();
b.t("This is a test".to_string());
b.u(vec![1, 2, 3]);
let v = b.build().unwrap();
assert_eq!(v.t, "This is a test");
assert_eq!(&v.u, &[1, 2, 3]);
}
#[test]
fn test_options_setters() {
let mut b = t::OptionsCfg::builder();
b.a(32);
b.s("hello");
b.other((1, 2));
b.nz(12);
let c = b.build().unwrap();
assert_eq!(c.a, Some(32));
assert_eq!(c.b, Some(123));
assert_eq!(c.s, Some("hello".to_string()));
assert_eq!(c.other, Some((1, 2)));
assert_eq!(c.nz, Some(12.try_into().unwrap()));
b.a(Some(12));
b.b(None);
b.s(Some("world"));
b.other(Some((11, 22)));
b.nz(Some(std::num::NonZeroU8::new(15).unwrap()));
let c = b.build().unwrap();
assert_eq!(c.a, Some(12));
assert_eq!(c.b, None);
assert_eq!(c.s, Some("world".to_string()));
assert_eq!(c.other, Some((11, 22)));
assert_eq!(c.nz, Some(15.try_into().unwrap()));
}
#[test]
fn test_attributes() {
let s1 = "fun_with_numbers = 1982374";
let s2 = "its_fun_to_count = 1982375";
let b1: t::AttribsCfgBuilder = toml::from_str(s1).unwrap();
let b2: t::AttribsCfgBuilder = toml::from_str(s2).unwrap();
assert_eq!(b1, b1);
assert_ne!(b1, b2);
assert!(b1 < b2);
}
#[test]
fn test_setter_meta() {
let mut b = t::SettersCfgBuilder::new();
b.a(7);
b.b(5_u8);
assert!(b.c(1_u64 << 40).is_err());
b.c(100_u64).unwrap();
b.d(19);
b.set_the_e(22);
let v = b.build().unwrap();
assert_eq!(v.a, 7 * 2);
assert_eq!(v.b, 5_u32);
assert_eq!(v.c, 100_u32);
assert_eq!(v.d, Some(19));
assert_eq!(v.e, 22);
}
#[test]
fn test_build_fn_rename() {
let mut b = t::RenameBuild::builder();
let v = b.member(6).build_this().unwrap();
assert_eq!(v.member, 6);
let mut b = t::RenameSubBuild::builder();
b.inner().member(5);
let v = b.build().unwrap();
assert_eq!(v.inner.member, 5);
}
#[test]
fn test_custom() {
let mut b = t::FullyCustomBuilder::new();
b.try_the_thing(0xF00B00B512345678);
assert_eq!(b.value, (0xF00B00B5, 0x12345678));
let v = b.build().unwrap();
assert_eq!(v.value, "4027252917 305419896");
}
#[test]
fn test_field_skip() {
let c = t::FieldSkipCfg::builder().build().unwrap();
assert_eq!(c.value, 25);
}
#[test]
fn test_list_builder() {
let mut b = t::ListsCfgBuilder::new();
b.set_integers(vec![12, 6, 3]);
let c = b.build().unwrap();
assert_eq!(&c.integers[..], &[12, 6, 3]);
assert!(c.cats.contains("Moonbeam"));
assert!(c.cats.contains("Damiano"));
assert!(c.simple.is_empty());
let b = t::ListsCfgBuilder::new();
let c = b.build().unwrap();
assert_eq!(&c.integers[..], &[7]);
let mut b = t::ListsCfgBuilder::new();
b.integers().push(22);
b.cats().remove(0);
b.cats().push("Frida".to_string());
b.simple().push(t::SimpleBuilder::new());
let c = b.build().unwrap();
assert_eq!(&c.integers[..], &[7, 22]);
assert!(c.cats.contains("Frida"));
assert!(!c.cats.contains("Damiano"));
assert_eq!(c.simple.len(), 1);
}
#[test]
fn test_list_builder_serde() {
let s1 = r#"
integers = [ 1,2,3 ]
"#;
let s2 = r#"
cats = [ "Prof. Jiggly", "Jorts" ]
"#;
let s3 = r#"
cats = [ "Jenny" ]
[[simple]]
xyz = 9
abc = 12
[[simple]]
xyz = 16
"#;
let b1: t::ListsCfgBuilder = toml::from_str(s1).unwrap();
let b2: t::ListsCfgBuilder = toml::from_str(s2).unwrap();
let b3: t::ListsCfgBuilder = toml::from_str(s3).unwrap();
let c1 = b1.build().unwrap();
let c2 = b2.build().unwrap();
let c3 = b3.build().unwrap();
assert_eq!(&c1.integers[..], &[1, 2, 3]);
assert!(c1.cats.contains("Checkers"));
assert!(!c1.cats.contains("Jorts"));
assert_eq!(&c2.integers[..], &[7]);
assert_eq!(c2.cats.len(), 2);
assert!(c2.cats.contains("Jorts"));
assert!(!c2.cats.contains("Checkers"));
assert_eq!(c3.cats.len(), 1);
assert_eq!(c3.simple.len(), 2);
assert_eq!(c3.simple[0].xyz, 9);
assert_eq!(c3.simple[0].abc, 12);
assert_eq!(c3.simple[1].xyz, 16);
assert_eq!(c3.simple[1].abc, 3);
}
#[test]
fn test_map_builder() {
let mut b = t::MapCfg::builder();
{
let mut sb = t::Simple::builder();
sb.xyz(11);
b.map().insert("Hello".to_string(), sb);
}
{
let mut sb = t::Simple::builder();
sb.abc(33);
b.map().insert("World".to_string(), sb);
}
let c = b.build().unwrap();
assert_eq!(c.map.len(), 3);
assert_eq!(c.map.get("Hello").unwrap().xyz, 11);
assert_eq!(c.map.get("Hello").unwrap().abc, 3);
assert_eq!(c.map.get("World").unwrap().xyz, 0);
assert_eq!(c.map.get("World").unwrap().abc, 33);
assert_eq!(c.map.get("pangolin").unwrap().xyz, 123);
assert_eq!(c.map.get("pangolin").unwrap().abc, 32);
}
}