ridstack-form 0.1.0

End-to-End Type-safe form handling for Dioxus applications
/// This file contains all the traits which are required to build a type safe form.
/// Normally you won't require this as everything is handled automatically by the `macro`
/// But then also you may required this for very advanced use cases.
use dioxus::prelude::*;
use std::{fmt::Debug, hash::Hash, pin::Pin};
#[cfg(feature = "validify")]
use validify::Validate;

use crate::{field::FieldState, form::*};

/// Gets the form state from the struct.
///
/// ***Applied automatically by macro***
#[cfg(not(feature = "validify"))]
pub trait GetFormState {
    fn get_form_state(&self) -> Store<FormState>;
}

#[cfg(feature = "validify")]
pub trait GetFormState: Validate {
    fn get_form_state(&self) -> Store<FormState>;
}

/// This is a helper trait that runs all the validations of all fields which are to be
/// run on submit
///
/// ***Applied automatically by macro***
pub trait ValidateOnSubmit {
    fn validate_on_submit(&mut self) -> Pin<Box<impl Future<Output = ()>>>;
}

/// This trait is used to init forms.
/// If macro based init is done, then this is not required as it auto
/// generates the initialization function.
///
/// ***Applied automatically by macro***
pub trait InitForm {
    /// This is a hook. So its used on the top level.
    fn use_form() -> Self;
}

/// This returns the original data struct on which the form was built.
/// If the fields are empty or not serialized somehow, then it results in
/// [Option::None](Option::None)
///
/// ***Applied automatically by macro***
pub trait FormSubmitInput {
    type SubmitInput: PartialEq;

    fn build_submit_input(&mut self) -> Option<Self::SubmitInput>;
}

/// Returns the FieldRegistry where all fields are resgistered.
/// Normally, its the Form struct itself.
///
/// ***Applied automatically by macro***
pub trait GetFieldRegistry: Clone + 'static {
    type FieldRegistry;
    fn get_field_registry(&self) -> Self::FieldRegistry;
}

/// returns name and field_state when a specific accessor struct is provided.
///
/// Example
/// ``` rust, no_run
///
/// #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
/// struct GetName;
///
/// impl FieldStateProvider<GetName> for TestForm {
///     type FieldValue = String;
///     type FieldError = FormError;
///     fn get_field_state(
///         &self,
///         field: &GetName,
///     ) -> Store<FieldState<Self::FieldValue, Self::FieldError>> {
///         self.field_state.name().into()
///     }
///     fn name(&self, field: &GetName) -> &'static str {
///         "name"
///     }
/// }
///
/// ```
///
/// ***Applied automatically by macro***
pub trait FieldStateProvider<TField: Clone + PartialEq + 'static>: Clone + 'static {
    type FieldValue: FieldValue;
    #[cfg(not(feature = "validify"))]
    type FieldError: FieldError
        + From<<<Self as FieldStateProvider<TField>>::FieldValue as FieldValue>::ConversionError>;
    #[cfg(feature = "validify")]
    type FieldError: FieldError
        + From<<<Self as FieldStateProvider<TField>>::FieldValue as FieldValue>::ConversionError>
        + OptionFrom<validify::ValidationError>;

    fn get_field_state(
        &self,
        field: &TField,
    ) -> Store<FieldState<Self::FieldValue, Self::FieldError>>;
    fn name(&self, field: &TField) -> &'static str;
}

/// Makes a type accessible to this library as field value.
pub trait FieldValue:
    Clone + Default + PartialEq + Debug + From<Self::PrimitiveValue> + StringValue + 'static
{
    type PrimitiveValue: PrimitiveFieldValue;
    type ConversionError: std::error::Error + Clone;
    fn from_str(value: &str) -> Result<Self, Self::ConversionError>;
    fn to_primitive_field_value(&self) -> Self::PrimitiveValue;
}

/// base type of field value which is accessible by the components.
pub trait PrimitiveFieldValue: Debug + Clone + PartialEq + 'static {}

/// Error returned by validators of a particular field. It should handle conversion errors
/// also.
pub trait FieldError:
    Clone + PartialEq + Eq + std::error::Error + PartialOrd + Ord + ToString + 'static
{
    fn custom_message(&self) -> Option<String> {
        None
    }

    /// ***Not to be used directly. This is just a priority selection.***
    fn error_value(&self) -> String {
        self.custom_message().unwrap_or(self.to_string())
    }
}

pub trait OptionFrom<T>: Sized {
    fn option_from(value: T) -> Option<Self>;
}

/// Needed to check whether all the fields are ready to be submitted.
/// Currently we can't scan field at runtime. So through macro,
/// we implement a function that checks whether all the fields are ready
/// to be submitted.
pub trait CalculateCanSubmit {
    fn calculate_can_submit(&mut self) -> bool;
}

/// Couldn't use `ToString` because native Option doesn't support it.
pub trait StringValue {
    fn to_string_value(&self) -> String {
        String::new()
    }
}