1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/// Provides a generic interface for setting a component of a certain type on an object.
///
/// This trait abstracts the action of setting or replacing a component, where a component
/// can be any part or attribute of an object, such as a field value. It is designed to be
/// generic over the type of the component being set (`T`) and the type that can be converted
/// into the component (`IntoT`). This design allows for flexible implementations that can
/// accept various types that can then be converted into the required component type.
///
/// # Type Parameters
///
/// - `T`: The type of the component to be set on the implementing object. This type represents
///   the final form of the component as it should be stored or represented in the object.
/// - `IntoT`: The type that can be converted into `T`. This allows the `assign` method to accept
///   different types that are capable of being transformed into the required component type `T`,
///   providing greater flexibility in setting the component.
///
/// # Examples
///
/// Implementing `Assign` to set a name string on a struct:
///
/// ```rust
/// use former_types::Assign; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly
///
/// struct MyStruct {
///   name: String,
/// }
///
/// impl< IntoT : Into< String > > Assign< String, IntoT > for MyStruct
/// {
///   fn assign( &mut self, component : IntoT )
///   {
///     self.name = component.into();
///   }
/// }
///
/// let mut obj = MyStruct { name : String::new() };
/// obj.assign( "New Name" );
/// assert_eq!( obj.name, "New Name" );
/// ```
#[ cfg( any( feature = "types_component_assign" ) ) ]
pub trait Assign< T, IntoT >
where
  IntoT : Into< T >,
{
  /// Sets or replaces the component on the object with the given value.
  ///
  /// This method takes ownership of the given value (`component`), which is of type `IntoT`.
  /// `component` is then converted into type `T` and set as the component of the object.
  fn assign( &mut self, component : IntoT );
}

/// Extension trait to provide a method for setting a component on an `Option<Self>`
/// if the `Option` is currently `None`. If the `Option` is `Some`, the method will
/// delegate to the `Assign` trait's `assign` method.
///
/// # Type Parameters
///
/// - `T`: The type of the component to be set on the implementing object. This type represents
///   the final form of the component as it should be stored or represented in the object.
///
/// # Examples
///
/// Using `option_assign` to set a component on an `Option`:
///
/// ```rust
/// use former_types::{ Assign, OptionExt }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly
///
/// struct MyStruct
/// {
///   name : String,
/// }
///
/// impl< IntoT : Into< MyStruct > > Assign< MyStruct, IntoT > for MyStruct
/// {
///   fn assign( &mut self, component : IntoT )
///   {
///     self.name = component.into().name;
///   }
/// }
///
/// let mut opt_struct: Option< MyStruct > = None;
/// opt_struct.option_assign( MyStruct { name: "New Name".to_string() } );
/// assert_eq!( opt_struct.unwrap().name, "New Name" );
/// ```
#[ cfg( any( feature = "types_component_assign" ) ) ]
pub trait OptionExt< T > : sealed::Sealed
where
  T : Sized + Assign< T, T >,
{
  /// Sets the component on the `Option` if it is `None`.
  ///
  /// If the `Option` is `Some`, the `assign` method is called to update the existing value.
  ///
  /// # Parameters
  ///
  /// - `src`: The value to assign to the `Option`.
  fn option_assign( & mut self, src : T );
}

#[ cfg( any( feature = "types_component_assign" ) ) ]
impl< T > OptionExt< T > for Option< T >
where
  T : Sized + Assign< T, T >,
{
  #[ inline( always ) ]
  fn option_assign( & mut self, src : T )
  {
    match self
    {
      Some( self_ref ) => Assign::assign( self_ref, Into::< T >::into( src ) ),
      None => * self = Some( src ),
    }
  }
}

#[ cfg( any( feature = "types_component_assign" ) ) ]
mod sealed
{
  pub trait Sealed {}
  impl< T > Sealed for Option< T >
  where
    T : Sized + super::Assign< T, T >,
  {}
}

/// The `AssignWithType` trait provides a mechanism to set a component on an object,
/// utilizing the type information explicitly. This trait extends the functionality of `Assign`
/// by allowing implementers to specify the component's type at the method call site,
/// enhancing expressiveness in code that manipulates object states.
///
/// # Type Parameters
///
/// - `T`: The type of the component to be set on the implementing object. This specifies
///   the exact type expected by the object as its component.
/// - `IntoT`: A type that can be converted into `T`, providing flexibility in the types of values
///   that can be used to set the component.
///
/// # Examples
///
/// Implementing `AssignWithType` to set a username on a struct:
///
/// ```rust
/// use former_types::{ Assign, AssignWithType }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly
///
/// struct UserProfile
/// {
///   username : String,
/// }
///
/// impl< IntoT : Into< String > > Assign< String, IntoT > for UserProfile
/// {
///   fn assign( &mut self, component : IntoT )
///   {
///     self.username = component.into();
///   }
/// }
///
/// let mut user_profile = UserProfile { username : String::new() };
/// user_profile.assign_with_type::< String, _ >("john_doe");
///
/// assert_eq!( user_profile.username, "john_doe" );
/// ```
#[ cfg( any( feature = "types_component_assign" ) ) ]
pub trait AssignWithType
{
  /// Sets the value of a component by its type.
  ///
  /// This method allows an implementer of `AssignWithType` to set a component on `self`
  /// where the component's type is `T`, and the input value is of type `IntoT`, which can be
  /// converted into `T`. This method bridges the gap between dynamic type usage and static type
  /// enforcement, providing a flexible yet type-safe interface for modifying object states.
  ///
  /// # Parameters
  ///
  /// - `component`: The value to assign to the component.
  ///
  /// # Type Parameters
  ///
  /// - `T`: The type of the component to be set on the implementing object.
  /// - `IntoT`: A type that can be converted into `T`.
  fn assign_with_type< T, IntoT >( & mut self, component : IntoT )
  where
    IntoT : Into< T >,
    Self : Assign< T, IntoT >;
}

#[ cfg( any( feature = "types_component_assign" ) ) ]
impl< S > AssignWithType for S
{
  #[ inline( always ) ]
  fn assign_with_type< T, IntoT >( & mut self, component : IntoT )
  where
    IntoT : Into< T >,
    Self : Assign< T, IntoT >,
  {
    Assign::< T, IntoT >::assign( self, component );
  }
}