36 个版本 (10 个稳定版)
新增 2.8.0 | 2024 年 8 月 16 日 |
---|---|
2.7.0 | 2024 年 7 月 13 日 |
2.6.0 | 2024 年 6 月 29 日 |
1.0.0 | 2024 年 5 月 14 日 |
0.1.4 | 2021 年 11 月 30 日 |
在 开发工具 中排名 #62
每月 3,186 次下载
在 79 个 Crates 中使用(直接使用 7 个)
250KB
2.5K SLoC
模块 :: 前身
支持嵌套构建器和特定集合子构建器的灵活的 Builder 模式实现。
Builder 模式允许您分步构建对象,只需使用您需要的步骤。未明确设置的任何字段将接收默认值。通过实现此模式,您可以避免在构造函数中传递大量参数。
此 crate 提供了针对常见 Rust 集合的专用子构建器,以流畅和直观的方式构建复杂的数据结构。此外,它还提供了定义和在其他构建器中重用构建器作为子构建器的功能。
Former 的工作原理
- 派生:通过在结构体上派生
Former
,您可以自动为每个字段生成构建器方法。 - 流畅接口:每个字段的构建器方法允许设置该字段的值,并返回对构建器的可变引用,从而实现方法链。
- 可选字段:可以轻松处理可选字段,而无需将它们显式设置为
None
。 - 最终化:使用
.form()
方法完成构建过程并返回构建的结构体实例。 - 子构建:如果一个字段有自己的构建器定义或包含有构建器定义的项目容器,则它可以作为子构建器使用。
这种方法抽象掉了手动为每个结构体实现构建器的需求,使代码更易于阅读和维护。
比较
旧版crate和抽象的Builder模式概念有一个共同的目标:逐步构建复杂对象,确保它们始终处于有效状态并隐藏内部结构。两者都使用流畅的接口来设置字段,并支持默认值,以便于未显式设置的字段。它们还有一个最终化方法来返回构建好的对象(Former中的.form(),传统Builder中的build())。
然而,Former crate通过宏自动化构建方法的生成来扩展了传统的Builder模式。这消除了在抽象概念中通常需要的手动实现。此外,Former支持嵌套构建器和子Former,用于复杂的数据结构,允许更复杂的对象构建。
高级功能,如自定义设置器、子Former重用、特定存储字段、突变者和上下文管理,进一步区分了Former与传统方法,后者通常侧重于没有这些功能的简单用例。此外,尽管传统的Builder模式通常包括一个导演类来管理构建过程,但Former不负责这一方面。
示例:简单示例
提供的代码片段展示了Former的一个基本用例,它用于应用Builder模式逐步构建复杂对象,确保它们始终处于有效状态并隐藏内部结构。
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
use former::Former;
// Use attribute debug to print expanded code.
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct UserProfile
{
age : i32,
username : String,
bio_optional : Option< String >, // Fields could be optional
}
let profile = UserProfile::former()
.age( 30 )
.username( "JohnDoe".to_string() )
.bio_optional( "Software Developer".to_string() ) // Optionally provide a bio
.form();
dbg!( &profile );
// Expected output:
// &profile = UserProfile {
// age: 30,
// username: "JohnDoe",
// bio_optional: Some("Software Developer"),
// }
# }
上面的代码将被扩展成这样
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
// Use attribute debug to print expanded code.
#[ derive( Debug, PartialEq ) ]
pub struct UserProfile
{
age : i32,
username : String,
bio_optional : Option< String >, // Fields could be optional
}
impl UserProfile
where
{
#[ inline( always ) ]
pub fn former() -> UserProfileFormer<
UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed >
>
{
UserProfileFormer::< UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed > >::
new_coercing(former::ReturnPreformed)
}
}
// = entity to
impl< Definition > former::EntityToFormer< Definition > for UserProfile
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >,
{
type Former = UserProfileFormer< Definition >;
}
impl former::EntityToStorage for UserProfile
where
{
type Storage = UserProfileFormerStorage;
}
impl< Context, Formed, End > former::EntityToDefinition< Context, Formed, End > for UserProfile
where
End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed > >,
{
type Definition = UserProfileFormerDefinition< Context, Formed, End >;
type Types = UserProfileFormerDefinitionTypes< Context, Formed >;
}
// = definition
#[derive(Debug)]
pub struct UserProfileFormerDefinitionTypes< Context = (), Formed = UserProfile, >
where
{
_phantom : core::marker::PhantomData< (*const Context, *const Formed) >,
}
impl< Context, Formed, > ::core::default::Default for UserProfileFormerDefinitionTypes< Context, Formed, >
where
{
fn default() -> Self
{
Self
{
_phantom : core::marker::PhantomData,
}
}
}
impl< Context, Formed, > former::FormerDefinitionTypes for UserProfileFormerDefinitionTypes< Context, Formed, >
where
{
type Storage = UserProfileFormerStorage;
type Formed = Formed;
type Context = Context;
}
#[derive(Debug)]
pub struct UserProfileFormerDefinition< Context = (), Formed = UserProfile, End = former::ReturnPreformed, >
where
{
_phantom : core::marker::PhantomData< (*const Context, *const Formed, *const End) >,
}
impl< Context, Formed, End, > ::core::default::Default for UserProfileFormerDefinition< Context, Formed, End, >
where
{
fn default() -> Self
{
Self
{
_phantom : core::marker::PhantomData,
}
}
}
impl< Context, Formed, End, > former::FormerDefinition for UserProfileFormerDefinition< Context, Formed, End, >
where
End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed, > >,
{
type Types = UserProfileFormerDefinitionTypes< Context, Formed, >;
type End = End;
type Storage = UserProfileFormerStorage;
type Formed = Formed;
type Context = Context;
}
impl< Context, Formed, > former::FormerMutator for UserProfileFormerDefinitionTypes< Context, Formed, >
where
{}
// = storage
pub struct UserProfileFormerStorage
where
{
pub age : ::core::option::Option< i32 >,
pub username : ::core::option::Option< String >,
pub bio_optional : Option< String >,
}
impl ::core::default::Default for UserProfileFormerStorage
where
{
#[ inline( always ) ]
fn default() -> Self
{
Self
{
age : ::core::option::Option::None,
username : ::core::option::Option::None,
bio_optional : ::core::option::Option::None,
}
}
}
impl former::Storage for UserProfileFormerStorage
where
{
type Preformed = UserProfile;
}
impl former::StoragePreform for UserProfileFormerStorage
where
{
fn preform(mut self) -> Self::Preformed
{
let age = if self.age.is_some()
{
self.age.take().unwrap()
}
else
{
{
trait MaybeDefault< T >
{
fn maybe_default(self : &Self) -> T
{
panic!("Field 'age' isn't initialized")
}
}
impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T >
{}
impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T >
where T : ::core::default::Default,
{
fn maybe_default(self : &Self) -> T
{
T::default()
}
}
(&::core::marker::PhantomData::< i32 >).maybe_default()
}
};
let username = if self.username.is_some()
{
self.username.take().unwrap()
}
else
{
{
trait MaybeDefault< T >
{
fn maybe_default(self : &Self) -> T
{
panic!("Field 'username' isn't initialized")
}
}
impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T >
{}
impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T >
where T : ::core::default::Default,
{
fn maybe_default(self : &Self) -> T
{
T::default()
}
}
(&::core::marker::PhantomData::< String >).maybe_default()
}
};
let bio_optional = if self.bio_optional.is_some()
{
::core::option::Option::Some(self.bio_optional.take().unwrap())
}
else
{
::core::option::Option::None
};
let result = UserProfile::<>
{
age,
username,
bio_optional,
};
return result;
}
}
pub struct UserProfileFormer< Definition = UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed >, >
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >,
{
pub storage : Definition::Storage,
pub context : core::option::Option< Definition::Context >,
pub on_end : core::option::Option< Definition::End >,
}
impl< Definition, > UserProfileFormer< Definition, >
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = UserProfileFormerStorage >,
{
#[ inline( always ) ]
pub fn new(on_end : Definition::End) -> Self
{
Self::begin_coercing(None, None, on_end)
}
#[ inline( always ) ]
pub fn new_coercing< IntoEnd >(end : IntoEnd) -> Self
where IntoEnd : Into< Definition::End >,
{
Self::begin_coercing(None, None, end,)
}
#[ inline( always ) ]
pub fn begin(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : <Definition as former::FormerDefinition>::End,) -> Self
{
if storage.is_none()
{
storage = Some(::core::default::Default::default());
}
Self
{
storage : storage.unwrap(),
context : context,
on_end : ::core::option::Option::Some(on_end),
}
}
#[ inline( always ) ]
pub fn begin_coercing< IntoEnd >(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : IntoEnd,) -> Self
where IntoEnd : ::core::convert::Into< <Definition as former::FormerDefinition>::End >,
{
if storage.is_none()
{
storage = Some(::core::default::Default::default());
}
Self
{
storage : storage.unwrap(),
context : context,
on_end : ::core::option::Option::Some(::core::convert::Into::into(on_end)),
}
}
#[ inline( always ) ]
pub fn form(self) -> <Definition::Types as former::FormerDefinitionTypes>::Formed
{
self.end()
}
#[ inline( always ) ]
pub fn end(mut self) -> <Definition::Types as former::FormerDefinitionTypes>::Formed
{
let on_end = self.on_end.take().unwrap();
let mut context = self.context.take();
<Definition::Types as former::FormerMutator>::form_mutation(&mut self.storage, &mut context);
former::FormingEnd::<Definition::Types>::call(&on_end, self.storage, context)
}
#[ inline( always ) ]
pub fn age< Src >(mut self, src : Src) -> Self
where Src : ::core::convert::Into< i32 >,
{
debug_assert!(self.storage.age.is_none());
self.storage.age = ::core::option::Option::Some(::core::convert::Into::into( src ));
self
}
#[ inline( always ) ]
pub fn username< Src >(mut self, src : Src) -> Self
where Src : ::core::convert::Into< String >,
{
debug_assert!(self.storage.username.is_none());
self.storage.username = ::core::option::Option::Some(::core::convert::Into::into( src ));
self
}
#[ inline( always ) ]
pub fn bio_optional< Src >(mut self, src : Src) -> Self
where Src : ::core::convert::Into< String >,
{
debug_assert!(self.storage.bio_optional.is_none());
self.storage.bio_optional = ::core::option::Option::Some(::core::convert::Into::into( src ));
self
}
}
impl< Definition, > UserProfileFormer< Definition, >
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile >,
{
pub fn preform(self) -> <Definition::Types as former::FormerDefinitionTypes>::Formed
{
former::StoragePreform::preform(self.storage)
}
}
impl< Definition, > UserProfileFormer< Definition, >
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile, >,
{
#[ inline( always ) ]
pub fn perform(self) -> Definition::Formed
{
let result = self.form();
return result;
}
}
impl< Definition > former::FormerBegin< Definition > for UserProfileFormer< Definition, >
where
Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >,
{
#[ inline( always ) ]
fn former_begin(storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : Definition::End,) -> Self
{
debug_assert!(storage.is_none());
Self::begin(None, context, on_end)
}
}
// = as subformer
pub type UserProfileAsSubformer< Superformer, End > =
UserProfileFormer< UserProfileFormerDefinition< Superformer, Superformer, End, >, >;
pub trait UserProfileAsSubformerEnd< SuperFormer >
where
Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >, {}
impl< SuperFormer, T > UserProfileAsSubformerEnd< SuperFormer > for T
where
Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >,
{}
// = end
let profile = UserProfile::former()
.age( 30 )
.username( "JohnDoe".to_string() )
.bio_optional( "Software Developer".to_string() ) // Optionally provide a bio
.form();
dbg!( &profile );
// Expected output:
//
// &profile = UserProfile {
// age: 30,
// username: "JohnDoe",
// bio_optional: Some("Software Developer"),
// }
# }
尝试运行 cargo run --example former_trivial
。
查看代码.
示例:自定义和替代设置器
借助Former
,可以定义单个字段的多个版本设置器,提供在设置器方法中包含自定义逻辑的灵活性。当需要预处理数据或在对字段赋值之前强制执行特定约束时,此功能特别有用。自定义设置器应具有独特名称,以区分由Former
生成的默认设置器,从而在保持代码清晰的同时实现特定的行为。
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
use former::Former;
/// Structure with a custom setter.
#[ derive( Debug, Former ) ]
pub struct StructWithCustomSetters
{
word : String,
}
impl StructWithCustomSettersFormer
{
// Custom alternative setter for `word`
pub fn word_exclaimed( mut self, value : impl Into< String > ) -> Self
{
debug_assert!( self.storage.word.is_none() );
self.storage.word = Some( format!( "{}!", value.into() ) );
self
}
}
let example = StructWithCustomSetters::former()
.word( "Hello" )
.form();
assert_eq!( example.word, "Hello".to_string() );
let example = StructWithCustomSetters::former()
.word_exclaimed( "Hello" )
.form();
assert_eq!( example.word, "Hello!".to_string() );
# }
在上面的示例中展示了自定义的替代设置器word_exclaimed
,它在将输入字符串存储之前,将其追加感叹号。这种方法允许在不妨碍Builder模式简单性的同时,进行额外的数据处理或验证。
尝试运行 cargo run --example former_custom_setter
。
查看代码.
示例:自定义设置器覆盖
但也可以完全覆盖设置器并从头开始编写。为此,使用属性[ setter( false ) ]
来禁用设置器。
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
use former::Former;
/// Structure with a custom setter.
#[ derive( Debug, Former ) ]
pub struct StructWithCustomSetters
{
// Use `debug` to gennerate sketch of setter.
#[ scalar( setter = false ) ]
word : String,
}
impl< Definition > StructWithCustomSettersFormer< Definition >
where
Definition : former::FormerDefinition< Storage = StructWithCustomSettersFormerStorage >,
{
// Custom alternative setter for `word`
#[ inline ]
pub fn word< Src >( mut self, src : Src ) -> Self
where
Src : ::core::convert::Into< String >,
{
debug_assert!( self.storage.word.is_none() );
self.storage.word = Some( format!( "{}!", src.into() ) );
self
}
}
let example = StructWithCustomSetters::former()
.word( "Hello" )
.form();
assert_eq!( example.word, "Hello!".to_string() );
dbg!( example );
//> StructWithCustomSetters {
//> word: "Hello!",
//> }
# }
在上面的示例中,默认的word
设置器被禁用,并定义了一个自定义设置器,它会自动将感叹号追加到字符串。此方法允许完全控制数据赋值过程,包括任何必要的逻辑或验证步骤。
尝试运行 cargo run --example former_custom_setter_overriden
。
查看代码.
示例:自定义默认值
《Former》库通过允许通过 default
属性指定字段的自定义默认值来增强结构体的初始化。此功能不仅提供了一种在不依赖 Default
特性的情况下设置结构体字段初始值的方法,而且还增加了处理字段类型未实现 Default
或需要非标准默认值的情况的灵活性。
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
use former::Former;
/// Structure with default attributes.
#[ derive( Debug, PartialEq, Former ) ]
pub struct ExampleStruct
{
#[ former( default = 5 ) ]
number : i32,
#[ former( default = "Hello, Former!".to_string() ) ]
greeting : String,
#[ former( default = vec![ 10, 20, 30 ] ) ]
numbers : Vec< i32 >,
}
let instance = ExampleStruct::former().form();
let expected = ExampleStruct
{
number : 5,
greeting : "Hello, Former!".to_string(),
numbers : vec![ 10, 20, 30 ],
};
assert_eq!( instance, expected );
dbg!( &instance );
// > &instance = ExampleStruct {
// > number: 5,
// > greeting: "Hello, Former!",
// > numbers: [
// > 10,
// > 20,
// > 30,
// > ],
// > }
# }
上面的代码片段展示了 Former
库使用自定义默认值初始化结构体字段的能力。
number
字段初始化为5
。greeting
字段的默认值为问候消息 "Hello, Former!"。numbers
字段以包含整数10
、20
和30
的向量开始。
这种方法大大简化了结构体的构建,尤其是在需要复杂类型或超出 Default
特性能力之外的默认值时。通过利用 default
属性,开发者可以确保他们的结构体安全、可预测地初始化,从而提高代码的清晰性和可维护性。
尝试运行 cargo run --example former_custom_defaults
。
查看代码.
存储和Former的概念
存储是临时存储结构,它在对象构建过程中持有对象的中间状态。
存储的目的
- 中间状态持有:存储作为正在形成对象的所有部分设置属性和数据的临时存储库。这种功能在对象完成依赖于多个、可能复杂的配置阶段的情况下是必不可少的。
- 解耦配置与实例化:存储将配置状态的积累与最终对象的实际创建分开。这种分离促进了更干净、更易于维护的代码,允许开发者以任何顺序应用配置并更有效地管理中间状态,而不影响最终对象的完整性。
存储不仅仅是被动收集;它是包括Former本身、上下文和回调(通常称为 FormingEnd
)在内的更大生态系统的一个积极组成部分。
- Former作为积极管理者:Former负责管理存储,利用它来跟踪对象演变的配置。它通过处理中间状态并准备对象最终形式来协调形成过程。
- 上下文灵活性:与Former关联的上下文为Former添加了额外的灵活性,允许Former根据对象形成的更广泛环境调整其行为。这在形成过程涉及对象本身之外的条件或状态时特别有用。
- FormingEnd回调:
FormingEnd
回调是一个动态组件,它定义了形成过程的最终步骤。它可以基于最终调整修改存储,验证对象的就绪状态,或将对象集成到更大的结构中,例如将其作为子Former嵌入到另一个结构中。
这些元素协同工作,确保形成过程不仅关于逐步构建一个对象,而且还关于将其无缝集成到更大的、更复杂的结构或系统中。
子Former的概念
子表单是用于在表单中构建嵌套或基于集合的数据结构(如向量、哈希图和哈希集合)的专业构建器。它们通过提供流畅的接口,可以将接口无缝集成到父结构的整体构建器模式中,从而简化了向这些结构添加元素的过程。这种方法可以干净直观地初始化复杂的数据结构,提高代码的可读性和可维护性。
子表单类型/设置器
了解不同类型设置器或子表单之间的区别对于在对象构造中有效地使用构建器模式至关重要。每种类型的设置器都是为了满足构建复杂、结构化数据实体时的特定需求而设计的。
-
标量设置器:处理实体中标量值或简单字段的直接赋值。这些设置器管理基本数据类型或单个字段,不涉及嵌套表单或复杂结构。
-
子表集合设置器:通过返回一个提供配置整个集合接口的表单,促进对整个集合的管理。此设置器对于将统一配置或验证应用于集合中的所有元素(如子代的
HashMap
)非常有用。 -
子表条目设置器:允许对集合中的元素进行单独的构建。它为每个元素返回一个表单,可以在集合中详细配置和添加复杂元素,例如在父的
HashMap
中管理Child
实体。 -
子表标量设置器:类似于子表条目设置器,但专为具有表单实现的标量字段设计。此设置器不收集实例到集合中,因为没有集合,只有标量字段。当标量字段本身需要通过其专用表单进行配置或修改时,使用它。
这些设置器确保开发人员可以精确高效地设置属性、管理集合,并在其应用程序中配置复杂结构。
示例:向量的集合设置器
此示例演示了如何以结构化的方式使用集合设置器通过 Former
配置 Vec
。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
#[ derive( Debug, PartialEq, former::Former ) ]
pub struct StructWithVec
{
#[ subform_collection ]
vec : Vec< &'static str >,
}
let instance = StructWithVec::former()
.vec()
.add( "apple" )
.add( "banana" )
.end()
.form();
assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } );
dbg!( instance );
# }
尝试运行 cargo run --example former_collection_vector
。
查看代码.
示例:哈希图的集合设置器
此示例演示了如何有效地使用集合设置器通过 Former
配置 HashMap
。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
use collection_tools::{ HashMap, hmap };
#[ derive( Debug, PartialEq, former::Former ) ]
pub struct StructWithMap
{
#[ subform_collection ]
map : HashMap< &'static str, &'static str >,
}
let instance = StructWithMap::former()
.map()
.add( ( "a", "b" ) )
.add( ( "c", "d" ) )
.end()
.form()
;
assert_eq!( instance, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } );
dbg!( instance );
# }
尝试运行 cargo run --example former_collection_hashmap
。
查看代码.
示例:哈希集合的集合设置器
此示例演示了通过子表单构建 collection_tools::HashSet
的使用。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
{
use collection_tools::{ HashSet, hset };
#[ derive( Debug, PartialEq, former::Former ) ]
pub struct StructWithSet
{
#[ subform_collection ]
set : HashSet< &'static str >,
}
let instance = StructWithSet::former()
.set()
.add( "apple" )
.add( "banana" )
.end()
.form();
assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] });
dbg!( instance );
# }
尝试运行 cargo run --example former_collection_hashset
。
查看代码.
示例:自定义标量设置器
此示例演示了使用 Former
实现标量设置器的示例。与之前示例中展示的更复杂的子表单和集合设置器不同,此示例侧重于直接在父实体中设置标量值的直接方法。父结构 Parent
管理一个 HashMap
,其中包含 Child
实体的映射,标量设置器用于直接设置整个 HashMap
。
在 ParentFormer
中定义的 child
函数是一个自定义子表设置器,发挥着至关重要的作用。它独特地使用 ChildFormer
来通过父类构建模式添加和配置名为子类的子元素。这种方法展示了集成子表器以管理集合特定元素(在这种情况下为每个子实体)的强大技术。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
use collection_tools::HashMap;
use former::Former;
// Child struct with Former derived for builder pattern support
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Child
{
name : String,
description : String,
}
// Parent struct to hold children
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Parent
{
// Use `debug` to gennerate sketch of setter.
#[ scalar( setter = false ) ]
children : HashMap< String, Child >,
}
impl< Definition > ParentFormer< Definition >
where
Definition : former::FormerDefinition< Storage = ParentFormerStorage >,
{
#[ inline ]
pub fn children< Src >( mut self, src : Src ) -> Self
where
Src : ::core::convert::Into< HashMap< String, Child > >,
{
debug_assert!( self.storage.children.is_none() );
self.storage.children = ::core::option::Option::Some( ::core::convert::Into::into( src ) );
self
}
}
let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() };
let exit = Child { name : "exit".to_string(), description : "just exit".to_string() };
let mut children = HashMap::new();
children.insert( echo.name.clone(), echo );
children.insert( exit.name.clone(), exit );
let ca = Parent::former()
.children( children )
.form();
dbg!( &ca );
// > &ca = Parent {
// > child: {
// > "echo": Child {
// > name: "echo",
// > description: "prints all subjects and properties",
// > },
// > "exit": Child {
// > name: "exit",
// > description: "just exit",
// > },
// > },
// > }
# }
在这个例子中,Parent
结构体作为多个 Child
结构体的集合,每个都由一个唯一的子名称标识。《ParentFormer》实现了一个自定义方法 child
,它作为子表器用于向《Parent》中添加《Child》实例。
- 子定义:每个《Child》由一个《name》和一个《description》组成,我们通过《Former》派生它,以使用构建器模式轻松设置这些属性。
- 父定义:它使用《HashMap》存储《Child》对象集合。《setter`》属性被用来禁用默认设置器,并定义了一个自定义方法《child`》来方便添加具有特定属性的子。
- 自定义子表器集成:《ParentFormer》中的《child`》方法通过一个闭包初始化一个《ChildFormer`》,在完成时将《Child`》集成到《Parent`》的《child`》映射中。
尝试运行 cargo run --example former_custom_scalar_setter
。
查看代码.
示例:自定义子表标量设置器
使用《Former》实现自定义子表标量设置器的示例。
此示例侧重于子表标量设置器的使用,以管理父结构体内部复杂的标量类型。与处理集合的更通用子表设置器不同,此设置器专门配置具有自己的《Former》的标量字段,允许在嵌套构建器模式中进行详细配置。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former" ) ) ) ]
# fn main()
# {}
#
# // Ensures the example only compiles when the appropriate features are enabled.
# #[ cfg( all( feature = "enabled", feature = "derive_former" ) ) ]
# fn main()
# {
use former::Former;
// Child struct with Former derived for builder pattern support
#[ derive( Debug, PartialEq, Former ) ]
// Optional: Use `#[debug]` to expand and debug generated code.
// #[debug]
pub struct Child
{
name : String,
description : String,
}
// Parent struct designed to hold a single Child instance using subform scalar
#[ derive( Debug, PartialEq, Former ) ]
// Optional: Use `#[debug]` to expand and debug generated code.
// #[debug]
pub struct Parent
{
// The `subform_scalar` attribute is used to specify that the 'child' field has its own former
// and can be individually configured via a subform setter. This is not a collection but a single scalar entity.
#[ subform_scalar( setter = false ) ]
child : Child,
}
/// Extends `ParentFormer` to include a method that initializes and configures a subformer for the 'child' field.
/// This function demonstrates the dynamic addition of a named child, leveraging a subformer to specify detailed properties.
impl< Definition > ParentFormer< Definition >
where
Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >,
{
#[ inline( always ) ]
pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > >
{
self._child_subform_scalar::< ChildFormer< _ >, _, >().name( name )
}
}
// Creating an instance of `Parent` using the builder pattern to configure `Child`
let ca = Parent::former()
.child( "echo" ) // starts the configuration of the `child` subformer
.description( "prints all subjects and properties" ) // sets additional properties for the `Child`
.end() // finalize the child configuration
.form(); // finalize the Parent configuration
dbg!( &ca ); // Outputs the structured data for review
// Expected output:
//> Parent {
//> child: Child {
//> name: "echo",
//> description: "prints all subjects and properties",
//> },
//> }
# }
示例:自定义子表集合设置器
此示例展示了使用集合设置器管理复杂嵌套数据结构的方法,重点在于围绕集合《HashMap》构建的父-子关系。与使用子表设置器逐个添加元素的传统构建器模式不同,此示例使用集合设置器来管理整个子集合。
在 ParentFormer
中定义的 child
函数是一个自定义子表设置器,发挥着至关重要的作用。它独特地使用 ChildFormer
来通过父类构建模式添加和配置名为子类的子元素。这种方法展示了集成子表器以管理集合特定元素(在这种情况下为每个子实体)的强大技术。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
// Ensure the example only compiles when the appropriate features are enabled.
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
use collection_tools::HashMap;
use former::Former;
// Child struct with Former derived for builder pattern support
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Child
{
name : String,
description : String,
}
// Parent struct to hold children
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Parent
{
// Use `debug` to gennerate sketch of setter.
#[ scalar( setter = false ) ]
children : HashMap< String, Child >,
}
impl< Definition > ParentFormer< Definition >
where
Definition : former::FormerDefinition< Storage = ParentFormerStorage >,
{
#[ inline ]
pub fn children< Src >( mut self, src : Src ) -> Self
where
Src : ::core::convert::Into< HashMap< String, Child > >,
{
debug_assert!( self.storage.children.is_none() );
self.storage.children = ::core::option::Option::Some( ::core::convert::Into::into( src ) );
self
}
}
let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() };
let exit = Child { name : "exit".to_string(), description : "just exit".to_string() };
let mut children = HashMap::new();
children.insert( echo.name.clone(), echo );
children.insert( exit.name.clone(), exit );
let ca = Parent::former()
.children( children )
.form();
dbg!( &ca );
// > &ca = Parent {
// > child: {
// > "echo": Child {
// > name: "echo",
// > description: "prints all subjects and properties",
// > },
// > "exit": Child {
// > name: "exit",
// > description: "just exit",
// > },
// > },
// > }
# }
尝试运行 cargo run --example former_custom_subform_collection
。
查看代码.
示例:自定义子表条目设置器
此示例说明了使用《Former》实现嵌套构建器模式的方法,强调了父-子关系。在此,《Parent`》结构体利用《ChildFormer`》作为自定义子表器来动态管理其《child`》字段——一个《HashMap`》。《HashMap`》中的每个子实体都通过《ChildFormer`》进行唯一标识和配置。
在 ParentFormer
中定义的 child
函数是一个自定义子表设置器,发挥着至关重要的作用。它独特地使用 ChildFormer
来通过父类构建模式添加和配置名为子类的子元素。这种方法展示了集成子表器以管理集合特定元素(在这种情况下为每个子实体)的强大技术。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# // Ensure the example only compiles when the appropriate features are enabled.
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
use collection_tools::HashMap;
use former::Former;
// Child struct with Former derived for builder pattern support
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Child
{
name : String,
description : String,
}
// Parent struct to hold children
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]
pub struct Parent
{
// Use `debug` to gennerate sketch of setter.
#[ subform_entry( setter = false ) ]
child : HashMap< String, Child >,
}
/// Initializes and configures a subformer for adding named child entities. This method leverages an internal function
/// to create and return a configured subformer instance. It allows for the dynamic addition of children with specific names,
/// integrating them into the formation process of the parent entity.
///
impl< Definition > ParentFormer< Definition >
where
Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >,
{
#[ inline( always ) ]
pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > >
{
self._child_subform_entry::< ChildFormer< _ >, _, >()
.name( name )
}
}
// Required to define how `value` is converted into pair `( key, value )`
impl former::ValToEntry< HashMap< String, Child > > for Child
{
type Entry = ( String, Child );
#[ inline( always ) ]
fn val_to_entry( self ) -> Self::Entry
{
( self.name.clone(), self )
}
}
let ca = Parent::former()
.child( "echo" )
.description( "prints all subjects and properties" ) // sets additional properties using custom subformer
.end()
.child( "exit" )
.description( "just exit" ) // Sets additional properties using using custom subformer
.end()
.form();
dbg!( &ca );
// > &ca = Parent {
// > child: {
// > "echo": Child {
// > name: "echo",
// > description: "prints all subjects and properties",
// > },
// > "exit": Child {
// > name: "exit",
// > description: "just exit",
// > },
// > },
// > }
# }
尝试运行 cargo run --example former_custom_subform_entry
。
查看代码.
通用集合接口
有一系列特质被设计用来抽象和增强形成过程中集合数据结构的函数。这些特质对于管理集合操作(如添加、修改、在集合如向量、哈希表等不同表示形式之间转换)的复杂性至关重要。当与 former
宏中的 collection
属性结合使用时,这些特质特别有用,它自动实现这些特质,为复杂数据结构创建健壮且灵活的构建模式。
Collection
- 定义集合的基本功能,管理条目和值,建立形成过程中任何自定义集合实现所需的基本操作。EntryToVal
- 促进将集合条目转换为它们的值表示,这对于将集合元素更抽象地作为值处理的操作至关重要。ValToEntry
- 提供EntryToVal
的反向功能,将值转换回条目,这对于根据值数据添加或修改集合中条目的操作至关重要。CollectionAdd
- 为插入条目到集合中添加功能,考虑集合特定的规则,如重复处理和顺序保留,增强形成场景中集合的可使用性。CollectionAssign
- 扩展集合功能以替换所有现有条目,允许批量更新或完全重置集合内容,这在动态数据环境中特别有用。
自定义集合构建器
集合接口在包中定义,并为向量、哈希表等集合实现,但如果你想使用非标准集合,你可以为集合实现集合接口。这个例子演示了如何做到这一点。
尝试运行 cargo run --example former_custom_collection
。
查看代码.
突变器的概念
提供了一种在形成过程完成后对上下文和存储进行修改的机制。
FormerMutator
特质允许在最终形成操作完成之前对实体的内部状态(上下文和存储)实施自定义突变逻辑。这种突变发生在调用 FormingEnd
回调之前。
突变器的用例
- 对正在形成的数据进行最后时刻的更改。
- 根据存储的最终状态设置或修改依赖于存储或上下文的属性。
- 存储特定字段,这些字段在形成的结构中不存在。
存储特定字段
存储特定字段是仅在形成过程中存在的中间字段。这些字段在最终形成的结构中不存在,但在复杂的形成操作(如条件突变、临时状态跟踪或累计)中起着关键作用。
这些字段用于管理中间数据或状态,这些数据或状态有助于最终对象的构建,但在对象模式中不一定有直接表示。例如,计数器、标志或临时计算结果,这些结果决定了对象的最终状态。
《FormerMutator
》特质简化了自定义变异逻辑的实现。它作用于最终形成操作完成之前的内部状态(上下文和存储),就在调用《FormingEnd
》回调之前。这个特质对于根据存储中累积的状态进行最后调整或计算至关重要。
变异器与《FormingEnd
》的比较
与负责在父构建器中集成和最终确定字段形成过程的《FormingEnd
》不同,《form_mutation
`直接关联到实体本身。此方法旨在独立于构建过程是否在超构建器上下文中进行,或者结构是独立或嵌套字段。这使得《form_mutation
`适合进行实体特定的转换,这些转换不应干扰《FormingEnd
`管理的层次结构形成逻辑。
示例:变异器和存储字段
此示例说明了如何使用《FormerMutator
`》特质来实现自定义变异,并展示了形成过程中存储特定字段的概要。
在此示例中,字段《a
`和《b
`仅在存储中定义,并在自定义变异器中使用,以丰富或修改形成实体的字段《c
`。这种方法允许形成逻辑更加丰富和灵活,可以根据存储中持有的中间状态进行适应。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
use former::Former;
#[ derive( Debug, PartialEq, Former ) ]
#[ storage_fields( a : i32, b : Option< String > ) ]
#[ mutator( custom ) ]
pub struct Struct1
{
c : String,
}
// = former mutator
impl< Context, Formed > former::FormerMutator
for Struct1FormerDefinitionTypes< Context, Formed >
{
/// Mutates the context and storage of the entity just before the formation process completes.
#[ inline ]
fn form_mutation( storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > )
{
storage.a.get_or_insert_with( Default::default );
storage.b.get_or_insert_with( Default::default );
storage.c = Some( format!( "{:?} - {}", storage.a.unwrap(), storage.b.as_ref().unwrap() ) );
}
}
let got = Struct1::former().a( 13 ).b( "abc" ).c( "def" ).form();
let exp = Struct1
{
c : "13 - abc".to_string(),
};
assert_eq!( got, exp );
dbg!( got );
// > got = Struct1 {
// > c : "13 - abc",
// > }
# }
尝试运行《cargo run --example former_custom_mutator
`。
查看代码.
定义的概念
定义用于有效地封装和管理泛型参数,避免逐个传递每个参数。
两个关键定义特质
《
:FormerDefinitionTypes
`- 此特质概述了形成过程中涉及的基本组件,包括存储类型、创建的表单和使用的上下文。它侧重于涉及的类型,而不是形成过程的终止。
《
:FormerDefinition
`- 在《
FormerDefinitionTypes
`》的基础上,此特质引入了《FormingEnd
`回调,将形成类型与明确的结束联系起来。它指定了形成过程应该如何结束,这可能涉及验证、转换或集成到更大的结构中。 - 包含《
End
`类型参数指定了形成过程的结束条件,有效地将存储中持有的临时状态与其最终形式联系起来。
- 在《
形成特质系统概述
形成过程使用几个核心特质,每个特质在实体创建的生命周期中都有特定的作用。这些特质确保实体按照结构化模式有方法地构建,提高了可维护性和可扩展性。以下是对这些关键特质的总结:
- 《
EntityToDefinition
`》:将实体与其相应的形成定义联系起来,该定义规定了它们的构建过程。 - 《
EntityToFormer
`》:将实体与负责其逐步构建的构建器联系起来。 - 《
EntityToStorage
`》:指定了在实体形成过程中暂时持有其实体状态的存储结构。 - 《
FormerDefinition
`,《FormerDefinitionTypes
`》:定义了形成过程的基本属性和结束条件,确保实体根据预定的规则和逻辑形成。 - 《
Storage
`》:为形成过程中使用的存储类型建立基本接口,确保每个都可以初始化为默认状态。 StoragePreform
:描述了存储从可变、中间状态转换为实体最终不可变状态的过程,这对于准确推断形成过程至关重要。FormerMutator
:允许在形成过程完成之前对存储和上下文执行自定义变异逻辑,确保可以在最后一刻进行调整。FormingEnd
:指定形成过程结束时的关闭动作,可以转换或验证实体的最终状态。FormingEndClosure
:提供了一种灵活的机制,通过闭包动态处理形成过程的结束,这对于复杂场景非常有用。FormerBegin
:启动子形成过程,管理实体在存储和上下文设置方面的形成过程。
这些特性共同促进了一种强大而灵活的构建器模式,支持复杂对象的创建和配置场景。
示例:自定义定义
定义自定义形成定义和自定义形成逻辑,并将它们应用到集合中。
该示例展示了如何将元素累积到集合中,然后使用自定义的FormingEnd
实现将它们转换成一个单一的结果。这种模式在形成过程涉及输入元素的聚合或转换为不同类型或形式时非常有用。
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
// Define a struct `Sum` that will act as a custom former definition.
struct Sum;
// Implement `FormerDefinitionTypes` for `Sum`.
// This trait defines the types used during the forming process.
impl former::FormerDefinitionTypes for Sum
{
type Storage = Vec<i32>; // Collection for the integers.
type Formed = i32; // The final type after forming, which is a single integer.
type Context = (); // No additional context is used in this example.
}
// Implement `FormerMutator` for `Sum`.
// This trait could include custom mutation logic applied during the forming process, but it's empty in this example.
impl former::FormerMutator for Sum
{
}
// Implement `FormerDefinition` for `Sum`.
// This trait links the custom types to the former.
impl former::FormerDefinition for Sum
{
type Types = Sum; // Associate the `FormerDefinitionTypes` with `Sum`.
type End = Sum; // Use `Sum` itself as the end handler.
type Storage = Vec<i32>; // Specify the storage type.
type Formed = i32; // Specify the final formed type.
type Context = (); // Specify the context type, not used here.
}
// Implement `FormingEnd` for `Sum`.
// This trait handles the final step of the forming process.
impl former::FormingEnd<Sum> for Sum
{
fn call
(
&self,
storage: < Sum as former::FormerDefinitionTypes >::Storage,
_context: Option< < Sum as former::FormerDefinitionTypes >::Context>
)
-> < Sum as former::FormerDefinitionTypes >::Formed
{
// Sum all integers in the storage vector.
storage.iter().sum()
}
}
// Use the custom `Former` to sum a list of integers.
let got = former::CollectionFormer::<i32, Sum>::new(Sum)
.add( 1 ) // Add an integer to the storage.
.add( 2 ) // Add another integer.
.add( 10 ) // Add another integer.
.form(); // Perform the form operation, which triggers the summing logic.
let exp = 13; // Expected result after summing 1, 2, and 10.
assert_eq!(got, exp); // Assert the result is as expected.
dbg!(got); // Debug print the result to verify the output.
// > got = 13
# }
示例索引
添加到您的项目中
cargo add former
从存储库中尝试
git clone https://github.com/Wandalen/wTools
cd wTools
cd examples/former_trivial
cargo run
依赖关系
~1.4–2.2MB
~43K SLoC