use crate::data_result::DataResult; use crate::dynamic_ops::DynamicOps; use crate::lifecycle::Lifecycle; /// A trait specifying a builder to add key-value pairs in order to create a composite type. /// /// `Value` is the dynamic type for this builder. /// For a struct, some methods here can be implemented via the `impl_struct_builder` macro. pub trait StructBuilder { type Value; /// Adds a single key-value pair to this builder and returns the builder. #[must_use] fn add_key_value(self, key: Self::Value, value: Self::Value) -> Self; /// Adds a single key-'value result' pair to this builder and returns the builder. #[must_use] fn add_key_value_result(self, key: Self::Value, value: DataResult) -> Self; /// Adds a single 'key result'-'value result' pair to this builder and returns the builder. #[must_use] fn add_key_result_value_result( self, key: DataResult, value: DataResult, ) -> Self; /// Adds the error message from a provided `DataResult` (if any) to this builder and returns the builder. #[must_use] fn with_errors_from(self, result: &DataResult) -> Self; /// Adds a string key-value pair to this builder and returns the builder. #[must_use] fn add_string_key_value(self, key: &str, value: Self::Value) -> Self; /// Adds a string key-'value result' pair to this builder and returns the builder. #[must_use] fn add_string_key_value_result(self, key: &str, value: DataResult) -> Self; /// Sets the lifecycle of this builder and returns the builder. #[must_use] fn set_lifecycle(self, lifecycle: Lifecycle) -> Self; /// Maps the error from the internal builder to the function `f` and returns the builder. #[must_use] fn map_error(self, f: impl FnOnce(String) -> String) -> Self; /// Builds the map stored in this builder along with a prefix and returns the result. fn build(self, prefix: Self::Value) -> DataResult; /// Builds the map stored in this builder along with a `DataResult` prefix and returns the result. fn build_with_result_prefix(self, prefix: DataResult) -> DataResult where Self: Sized, { prefix.flat_map(|p| self.build(p)) } } /// A [`StructBuilder`] with a specified `Result` type for builders. pub trait ResultStructBuilder: StructBuilder { type Result; /// Builds the map stored in `builder` along with a prefix and returns the result. fn build_with_builder( self, builder: Self::Result, prefix: Self::Value, ) -> DataResult; } /// A subtrait of [`StructBuilder`] for appending string keys instead of dynamic type keys. /// The methods in `StructBuilder` can also be implemented via the `impl_string_struct_builder` macro. pub trait StringStructBuilder: ResultStructBuilder { /// Appends a string key-value pair to `builder`, mutating it. fn append(&self, key: &str, value: Self::Value, builder: Self::Result) -> Self::Result; } /// A subtrait of [`StructBuilder`] for appending dynamic keys. The methods in `StructBuilder` /// can also be implemented via the `impl_universal_struct_builder` macro. pub trait UniversalStructBuilder: ResultStructBuilder { /// Appends a key-value pair to `builder`, mutating it. fn append(&self, key: Self::Value, value: Self::Value, builder: Self::Result) -> Self::Result; } /// A macro to be placed inside an `impl` block of a struct implementing [`StructBuilder`]. /// /// Place this in a `impl StructBuilder for ...` block. /// This automatically implements the methods to add key-value pairs to the builder. /// Make sure to have a struct field of type [`DataResult`] of name `$builder`. #[macro_export] macro_rules! impl_struct_builder { ($builder:ident) => { fn set_lifecycle(mut self, lifecycle: Lifecycle) -> Self { self.$builder = self.$builder.with_lifecycle(lifecycle); self } fn map_error(mut self, f: impl FnOnce(String) -> String) -> Self { self.$builder = self.$builder.map_error(f); self } fn with_errors_from(mut self, result: &DataResult) -> Self { self.$builder = self.$builder.with_errors_from(result); self } fn build(self, prefix: Self::Value) -> DataResult { self.$builder .clone() .flat_map(|b| self.build_with_builder(b, prefix)) } }; } /// A macro to be placed inside an `impl` block of a struct implementing [`StringStructBuilder`]. /// /// Place this in a `impl StructBuilder for ...` block. /// This automatically implements the methods to add key-value pairs to the builder. #[macro_export] macro_rules! impl_string_struct_builder { (@internal $builder:ident) => { fn add_string_key_value(mut self, key: &str, value: Self::Value) -> Self { self.$builder = std::mem::take(&mut self.$builder).map(|r| self.append(key, value, r)); self } fn add_string_key_value_result(mut self, key: &str, value: DataResult) -> Self { self.$builder = std::mem::take(&mut self.$builder).apply_2_and_make_stable(|r, v| self.append(key, v, r), value); self } }; // For constant ops ($builder:ident, $ops:ident) => { impl_string_struct_builder!(@internal $builder); fn add_key_value(mut self, key: Self::Value, value: Self::Value) -> Self { self.$builder = $ops.get_string(&key).flat_map( |s| std::mem::take(&mut self.$builder).map(|r| self.append(&s, value, r)) ); self } fn add_key_value_result(mut self, key: Self::Value, value: DataResult) -> Self { self.$builder = $ops.get_string(&key).flat_map( |s| std::mem::take(&mut self.$builder).apply_2_and_make_stable(|r, v| self.append(&s, v, r), value) ); self } fn add_key_result_value_result( mut self, key: DataResult, value: DataResult, ) -> Self { self.$builder = key.flat_map(|v| $ops.get_string(&v)).flat_map(|s| { std::mem::take(&mut self.$builder).apply_2_and_make_stable(|r, v| self.append(&s, v, r), value) }); self } }; // For stored ops ($builder:ident, self. $ops:ident) => { impl_string_struct_builder!(@internal $builder); fn add_key_value(mut self, key: Self::Value, value: Self::Value) -> Self { self.$builder = self.$ops.get_string(&key).flat_map( |s| std::mem::take(&mut self.$builder).map(|r| self.append(&s, value, r)) ); self } fn add_key_value_result(mut self, key: Self::Value, value: DataResult) -> Self { self.$builder = self.$ops.get_string(&key).flat_map( |s| std::mem::take(&mut self.$builder).apply_2_and_make_stable(|r, v| self.append(&s, v, r), value) ); self } fn add_key_result_value_result( mut self, key: DataResult, value: DataResult, ) -> Self { self.$builder = key.flat_map(|v| self.$ops.get_string(&v)).flat_map(|s| { std::mem::take(&mut self.$builder).apply_2_and_make_stable(|r, v| self.append(&s, v, r), value) }); self } }; } /// A macro to be placed inside an `impl` block of a struct implementing `UniversalStructBuilder`. /// /// Place this in a `impl StructBuilder for ...` block. /// This automatically implements the methods to add key-value pairs to the builder. #[macro_export] macro_rules! impl_universal_struct_builder { (@internal $builder:ident) => { fn add_key_value(mut self, key: Self::Value, value: Self::Value) -> Self { self.$builder = std::mem::take(&mut self.$builder).map(|b| self.append(key, value, b)); self } fn add_key_value_result(mut self, key: Self::Value, value: DataResult) -> Self { self.$builder = std::mem::take(&mut self.$builder) .apply_2_and_make_stable(|b, v| self.append(key, v, b), value); self } fn add_key_result_value_result( mut self, key: DataResult, value: DataResult, ) -> Self { self.$builder = self .$builder .clone() .apply(key.apply_2_and_make_stable(|k, v| (|b| self.append(k, v, b)), value)); self } }; // For constant ops ($builder:ident, $ops:ident) => { impl_universal_struct_builder!(@internal $builder); fn add_string_key_value(self, key: &str, value: Self::Value) -> Self { self.add_key_value($ops.create_string(key), value) } fn add_string_key_value_result(self, key: &str, value: DataResult) -> Self { self.add_key_value_result($ops.create_string(key), value) } }; // For stored ops ($builder:ident, self. $ops:ident) => { impl_universal_struct_builder!(@internal $builder); fn add_string_key_value(self, key: &str, value: Self::Value) -> Self { let string = self.$ops.create_string(key); self.add_key_value(string, value) } fn add_string_key_value_result(self, key: &str, value: DataResult) -> Self { let string = self.$ops.create_string(key); self.add_key_value_result(string, value) } }; } pub struct MapBuilder + 'static> { builder: DataResult>, ops: &'static O, } impl> StructBuilder for MapBuilder { type Value = T; impl_struct_builder!(builder); impl_universal_struct_builder!(builder, self.ops); } impl> ResultStructBuilder for MapBuilder { type Result = Vec<(T, T)>; fn build_with_builder( self, builder: Self::Result, prefix: Self::Value, ) -> DataResult { self.ops.merge_entries_into_map(prefix, builder) } } impl> UniversalStructBuilder for MapBuilder { fn append( &self, key: Self::Value, value: Self::Value, mut builder: Self::Result, ) -> Self::Result { builder.push((key, value)); builder } }