use std::{marker::PhantomData, sync::Arc};
use super::{
super::{
IntoSchema, OptionSource, Retype, Schema,
condition::{Condition, Watched},
},
ChoiceControl, Control, ControlKind, Field, TextControl, choice,
};
use crate::form::{FormScalar, FormValue};
macro_rules! base_modifiers {
($builder:ident) => {
impl $builder {
pub(super) fn new(field: Field) -> Self {
Self(field, PhantomData)
}
}
impl<F> $builder<F> {
pub fn label(mut self, label: impl Into<String>) -> Self {
self.0.label = Some(label.into());
self
}
pub fn name(&self) -> &str {
self.0.name()
}
pub fn visible_when(
mut self,
watched: &impl Watched<F>,
values: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.0.condition = Some(Condition::new(watched, values));
self
}
}
impl<F> From<$builder<F>> for Field {
fn from(builder: $builder<F>) -> Field {
builder.0
}
}
impl<F> IntoSchema<F> for $builder<F> {
fn into_schema(self) -> Schema<F> {
self.0.into_schema().retype()
}
}
impl<F> Retype for $builder<F> {
type As<G> = $builder<G>;
fn retype<G>(self) -> $builder<G> {
$builder(self.0, PhantomData)
}
}
};
}
macro_rules! common_modifiers {
($builder:ident) => {
base_modifiers!($builder);
impl<F> $builder<F> {
pub fn help(mut self, text: impl Into<String>) -> Self {
self.0.help = Some(text.into());
self
}
pub fn disabled(mut self) -> Self {
self.0.disabled = super::Disabled::Always;
self
}
pub fn disabled_on_edit(mut self) -> Self {
self.0.disabled = super::Disabled::OnEdit;
self
}
pub fn custom(mut self, control: impl Control + 'static) -> CustomField<F> {
self.0.control = ControlKind::Custom(Arc::new(control));
self.0.checkbox = false;
CustomField(self.0, PhantomData)
}
}
};
}
#[cfg(test)]
macro_rules! required_for_tests {
($($builder:ident),*) => {$(
impl<F> $builder<F> {
pub(crate) fn required(mut self) -> Self {
self.0.set_required(true);
self
}
}
)*};
}
#[cfg(test)]
required_for_tests!(TextField, FileField);
macro_rules! default_modifier {
($($builder:ident),*) => {$(
impl<F> $builder<F> {
pub fn default(mut self, value: impl IntoFormValue) -> Self {
self.0.default = Some(value.into_form_value());
self
}
}
)*};
}
default_modifier!(TextField, ChoiceField, CustomField);
pub trait IntoFormValue {
fn into_form_value(self) -> FormValue;
}
impl<T: FormScalar> IntoFormValue for T {
fn into_form_value(self) -> FormValue {
FormValue::Text(self.to_form())
}
}
impl IntoFormValue for &str {
fn into_form_value(self) -> FormValue {
FormValue::Text(self.to_string())
}
}
impl IntoFormValue for &String {
fn into_form_value(self) -> FormValue {
FormValue::Text(self.clone())
}
}
impl<T: FormScalar> IntoFormValue for Vec<T> {
fn into_form_value(self) -> FormValue {
FormValue::List(self.iter().map(FormScalar::to_form).collect())
}
}
impl IntoFormValue for Vec<&str> {
fn into_form_value(self) -> FormValue {
FormValue::List(self.into_iter().map(str::to_string).collect())
}
}
impl<T: FormScalar, const N: usize> IntoFormValue for [T; N] {
fn into_form_value(self) -> FormValue {
Vec::from(self).into_form_value()
}
}
impl<const N: usize> IntoFormValue for [&str; N] {
fn into_form_value(self) -> FormValue {
Vec::from(self).into_form_value()
}
}
pub struct TextField<F = ()>(pub(super) Field, PhantomData<fn() -> F>);
pub struct ChoiceField<F = ()>(pub(super) Field, PhantomData<fn() -> F>);
pub struct FileField<F = ()>(pub(super) Field, PhantomData<fn() -> F>);
pub struct RepeaterField<F = ()>(pub(super) Field, PhantomData<fn() -> F>);
pub struct CustomField<F = ()>(pub(super) Field, PhantomData<fn() -> F>);
impl<F> CustomField<F> {
pub(crate) fn is_checkbox(&self) -> bool {
self.0.is_checkbox()
}
}
common_modifiers!(TextField);
common_modifiers!(ChoiceField);
common_modifiers!(FileField);
common_modifiers!(CustomField);
base_modifiers!(RepeaterField);
macro_rules! to_choice {
($($builder:ident),*) => {$(
impl<F> $builder<F> {
pub fn choice(mut self) -> ChoiceField<F> {
self.0.control = ControlKind::Choice(ChoiceControl::default());
ChoiceField(self.0, PhantomData)
}
}
)*};
}
to_choice!(TextField, FileField, CustomField);
impl<F> TextField<F> {
fn text(&mut self) -> &mut TextControl {
match &mut self.0.control {
ControlKind::Text(text) => text,
_ => unreachable!("a TextField holds a text control"),
}
}
pub fn email(mut self) -> Self {
self.text().email = true;
self
}
pub fn password(mut self) -> Self {
self.text().password = true;
self
}
pub fn unique(mut self) -> Self {
self.text().unique = true;
self
}
pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
self.text().placeholder = Some(placeholder.into());
self
}
pub fn multiline(mut self, rows: u32) -> Self {
self.text().rows = Some(rows);
self
}
}
impl<F> ChoiceField<F> {
fn choice_mut(&mut self) -> &mut ChoiceControl {
match &mut self.0.control {
ControlKind::Choice(choice) => choice,
_ => unreachable!("a ChoiceField holds a choice control"),
}
}
pub fn options(mut self, options: impl IntoOptions) -> Self {
self.choice_mut().options = options.into_options();
self
}
pub fn multiple(mut self) -> Self {
self.choice_mut().multiple = true;
self
}
pub fn searchable(mut self) -> Self {
self.choice_mut().searchable = true;
self
}
pub fn relationship<R>(mut self) -> Self
where
R: OptionSource + 'static,
{
self.choice_mut().relationship = Some(choice::Relationship::new::<R>(R::label));
self
}
pub fn depends_on<M, T>(
mut self,
parent: &impl Watched<F>,
column: impl Into<toasty::stmt::Path<M, T>>,
) -> Self
where
M: Send + Sync + 'static,
T: FormScalar + toasty::stmt::IntoExpr<T> + Send + Sync + 'static,
{
let column: toasty::stmt::Path<M, T> = column.into();
self.choice_mut().parent = Some(choice::Parent {
watched: parent.key().to_string(),
model: std::any::TypeId::of::<M>(),
scope: Arc::new(move |value: &str| {
T::parse_form(value)
.ok()
.map(|value| column.clone().eq(value))
}),
});
self
}
pub fn relationship_labelled<R>(
mut self,
label: impl Fn(&R::Model) -> String + Send + Sync + 'static,
) -> Self
where
R: OptionSource + 'static,
{
self.choice_mut().relationship = Some(choice::Relationship::new::<R>(
move |_cx: &topcoat::context::Cx, record: &R::Model| label(record),
));
self
}
}
impl<F> RepeaterField<F> {
pub fn add_label(mut self, label: impl Into<String>) -> Self {
match &mut self.0.control {
ControlKind::Repeater(repeater) => repeater.add_label = Some(label.into()),
_ => unreachable!("a RepeaterField holds a repeater control"),
}
self
}
}
pub trait IntoOptions {
fn into_options(self) -> Vec<(String, String)>;
}
impl IntoOptions for Vec<String> {
fn into_options(self) -> Vec<(String, String)> {
self.into_iter()
.map(|value| (value.clone(), value))
.collect()
}
}
impl IntoOptions for Vec<(String, String)> {
fn into_options(self) -> Vec<(String, String)> {
self
}
}
impl<const N: usize> IntoOptions for [&str; N] {
fn into_options(self) -> Vec<(String, String)> {
self.into_iter()
.map(|value| (value.to_string(), value.to_string()))
.collect()
}
}
#[cfg(test)]
mod test_deref {
use super::{ChoiceField, CustomField, Field, FileField, RepeaterField, TextField};
macro_rules! deref_field {
($($builder:ident),*) => {$(
impl<F> std::ops::Deref for $builder<F> {
type Target = Field;
fn deref(&self) -> &Field {
&self.0
}
}
)*};
}
deref_field!(
TextField,
ChoiceField,
FileField,
CustomField,
RepeaterField
);
}