This commit is contained in:
Mitchell Marino 2024-03-26 13:30:14 -05:00
parent 857dcec00f
commit a256e58672
5 changed files with 51 additions and 37 deletions

View File

@ -7,26 +7,15 @@ pub mod submit;
pub mod text_area; pub mod text_area;
pub mod text_input; pub mod text_input;
pub trait ValidationFn<FD: FormData>: Fn(&FD) -> Result<(), String> + 'static {} pub trait ValidationFn<FDT>: Fn(&FDT) -> Result<(), String> + 'static {}
pub trait ParseFn<FD: FormData, C: ControlData>: pub trait ParseFn<CT, FDT>: Fn(&CT) -> Result<FDT, String> + 'static {}
Fn(C::ReturnType, &mut FD) -> Result<(), String> + 'static pub trait FieldFn<FD, FDT>: Fn(&mut FD) -> FDT + 'static {}
{
}
// implement the trait for all valid types // implement the trait for all valid types
impl<FD, T> ValidationFn<FD> for T impl<FDT, T> ValidationFn<FDT> for T where T: Fn(&FDT) -> Result<(), String> + 'static {}
where
FD: FormData,
T: Fn(&FD) -> Result<(), String> + 'static,
{
}
// implement the trait for all valid types // implement the trait for all valid types
impl<FD, C, T> ParseFn<FD, C> for T impl<CR, FDT, F> ParseFn<CR, FDT> for F where F: Fn(&CR) -> Result<FDT, String> + 'static {}
where // implement the trait for all valid types
FD: FormData, impl<FD, FDT, F> FieldFn<FD, FDT> for F where F: Fn(&mut FD) -> FDT + 'static {}
C: ControlData,
T: Fn(C::ReturnType, &mut FD) -> Result<(), String> + 'static,
{
}
pub trait VanityControlData: 'static { pub trait VanityControlData: 'static {
fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View; fn build_control<FS: FormStyle>(fs: &FS, control: ControlRenderData<FS, Self>) -> View;
@ -69,19 +58,23 @@ impl<FS: FormStyle, C: VanityControlData> VanityControlBuilder<FS, C> {
} }
} }
pub struct ControlBuilder<FD: FormData, FS: FormStyle, C: ControlData> { pub struct ControlBuilder<FD: FormData, FS: FormStyle, C: ControlData, FDT> {
pub(crate) parse_fn: Box<dyn ParseFn<FD, C>>, pub(crate) field_fn: Option<Box<dyn FieldFn<FD, FDT>>>,
pub(crate) validation_fn: Box<dyn ValidationFn<FD>>, pub(crate) parse_fn: Option<Box<dyn ParseFn<C::ReturnType, FDT>>>,
pub(crate) unparse_fn: Option<Box<dyn ParseFn<FDT, C::ReturnType>>>,
pub(crate) validation_fn: Option<Box<dyn ValidationFn<FD>>>,
pub(crate) style_attributes: Vec<FS::StylingAttributes>, pub(crate) style_attributes: Vec<FS::StylingAttributes>,
pub(crate) data: C, pub(crate) data: C,
} }
impl<FD: FormData, FS: FormStyle, C: ControlData> ControlBuilder<FD, FS, C> { impl<FD: FormData, FS: FormStyle, C: ControlData, FDT> ControlBuilder<FD, FS, C, FDT> {
pub(crate) fn new(data: C) -> Self { pub(crate) fn new(data: C) -> Self {
ControlBuilder { ControlBuilder {
data, data,
parse_fn: Box::new(|_, _| Ok(())), field_fn: None,
validation_fn: Box::new(|_| Ok(())), parse_fn: None,
unparse_fn: None,
validation_fn: None,
style_attributes: Vec::new(), style_attributes: Vec::new(),
} }
} }
@ -103,6 +96,20 @@ impl<FD: FormData, FS: FormStyle, C: ControlData> ControlBuilder<FD, FS, C> {
) )
} }
// TODO: add method that automatically does the parse and unparse using
// TryInto<C::ReturnValue> and TryFrom<C::ReturnVlaue>
pub fn field_with(
mut self,
field_fn: impl FieldFn<FD, FDT>,
parse_fn: impl ParseFn<C::ReturnType, FDT>,
unparse_fn: impl ParseFn<FDT, C::ReturnType>,
) -> Self {
self.field_fn = Box::new(field_fn);
self.parse_fn = Box::new(parse_fn);
self.unparse_fn = Box::new(unparse_fn);
self
}
pub fn parse_fn(mut self, parse_fn: impl ParseFn<FD, C>) -> Self { pub fn parse_fn(mut self, parse_fn: impl ParseFn<FD, C>) -> Self {
self.parse_fn = Box::new(parse_fn) as Box<dyn ParseFn<FD, C>>; self.parse_fn = Box::new(parse_fn) as Box<dyn ParseFn<FD, C>>;
self self
@ -112,7 +119,7 @@ impl<FD: FormData, FS: FormStyle, C: ControlData> ControlBuilder<FD, FS, C> {
mut self, mut self,
validation_fn: impl Fn(&FD) -> Result<(), String> + 'static, validation_fn: impl Fn(&FD) -> Result<(), String> + 'static,
) -> Self { ) -> Self {
self.validation_fn = Box::new(validation_fn) as Box<dyn ValidationFn<FD>>; self.validation_fn = Some(Box::new(validation_fn)) as _;
self self
} }

View File

@ -25,15 +25,17 @@ impl ControlData for SelectData {
} }
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> { impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn select( pub fn select<FDT>(
self, self,
builder: impl Fn(ControlBuilder<FD, FS, SelectData>) -> ControlBuilder<FD, FS, SelectData>, builder: impl Fn(
ControlBuilder<FD, FS, SelectData, FDT>,
) -> ControlBuilder<FD, FS, SelectData, FDT>,
) -> Self { ) -> Self {
self.new_control(builder) self.new_control(builder)
} }
} }
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, SelectData> { impl<FD: FormData, FS: FormStyle, FDT> ControlBuilder<FD, FS, SelectData, FDT> {
pub fn options(mut self, options: Vec<String>) -> Self { pub fn options(mut self, options: Vec<String>) -> Self {
self.data.options = options; self.data.options = options;
self self

View File

@ -24,15 +24,17 @@ impl ControlData for TextAreaData {
} }
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> { impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn text_area( pub fn text_area<FDT>(
self, self,
builder: impl Fn(ControlBuilder<FD, FS, TextAreaData>) -> ControlBuilder<FD, FS, TextAreaData>, builder: impl Fn(
ControlBuilder<FD, FS, TextAreaData, FDT>,
) -> ControlBuilder<FD, FS, TextAreaData, FDT>,
) -> Self { ) -> Self {
self.new_control(builder) self.new_control(builder)
} }
} }
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, TextAreaData> { impl<FD: FormData, FS: FormStyle, FDT> ControlBuilder<FD, FS, TextAreaData, FDT> {
pub fn placeholder(mut self, placeholder: impl ToString) -> Self { pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
self.data.placeholder = Some(placeholder.to_string()); self.data.placeholder = Some(placeholder.to_string());
self self

View File

@ -40,15 +40,17 @@ impl ControlData for TextInputData {
} }
impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> { impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
pub fn text_input( pub fn text_input<FDT>(
self, self,
builder: impl Fn(ControlBuilder<FD, FS, TextInputData>) -> ControlBuilder<FD, FS, TextInputData>, builder: impl Fn(
ControlBuilder<FD, FS, TextInputData, FDT>,
) -> ControlBuilder<FD, FS, TextInputData, FDT>,
) -> Self { ) -> Self {
self.new_control(builder) self.new_control(builder)
} }
} }
impl<FD: FormData, FS: FormStyle> ControlBuilder<FD, FS, TextInputData> { impl<FD: FormData, FS: FormStyle, FDT> ControlBuilder<FD, FS, TextInputData, FDT> {
pub fn placeholder(mut self, placeholder: impl ToString) -> Self { pub fn placeholder(mut self, placeholder: impl ToString) -> Self {
self.data.placeholder = Some(placeholder.to_string()); self.data.placeholder = Some(placeholder.to_string());
self self

View File

@ -145,9 +145,9 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
self self
} }
pub(crate) fn new_control<C: ControlData + Default>( pub(crate) fn new_control<C: ControlData + Default, FDT>(
mut self, mut self,
builder: impl Fn(ControlBuilder<FD, FS, C>) -> ControlBuilder<FD, FS, C>, builder: impl Fn(ControlBuilder<FD, FS, C, FDT>) -> ControlBuilder<FD, FS, C, FDT>,
) -> Self { ) -> Self {
let control_builder = ControlBuilder::new(C::default()); let control_builder = ControlBuilder::new(C::default());
let control = builder(control_builder); let control = builder(control_builder);
@ -165,7 +165,7 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
full_builder.views.push(view); full_builder.views.push(view);
} }
fn add_control<C: ControlData>(&mut self, control: ControlBuilder<FD, FS, C>) { fn add_control<C: ControlData, FDT>(&mut self, control: ControlBuilder<FD, FS, C, FDT>) {
let full_builder = match &mut self.inner { let full_builder = match &mut self.inner {
FormBuilderInner::ValidationBuilder { validations } => { FormBuilderInner::ValidationBuilder { validations } => {
validations.push(control.validation_fn); validations.push(control.validation_fn);
@ -199,6 +199,7 @@ impl<FD: FormData, FS: FormStyle> FormBuilder<FD, FS> {
FormBuilderInner::FullBuilder(full_builder) => Form { FormBuilderInner::FullBuilder(full_builder) => Form {
fd: full_builder.fd_get, fd: full_builder.fd_get,
validations: full_builder.validations, validations: full_builder.validations,
// TODO: wrap in the style's form wrapper
view: full_builder.views.into_view(), view: full_builder.views.into_view(),
}, },
FormBuilderInner::ValidationBuilder { validations } => Form { FormBuilderInner::ValidationBuilder { validations } => Form {