use std::{marker::PhantomData, sync::Arc};
pub use self::into::{IntoFormValue, IntoOptions};
use super::{
super::{
IntoSchema, OptionSource, Retype, Schema,
condition::{Condition, Watched},
repeater::RepeaterControl,
},
ChoiceControl, Control, ControlKind, Field, Kind, Pattern, TextControl,
};
#[cfg(feature = "rich-text")]
use super::{RichTextControl, RichTextField};
mod choices;
mod comparisons;
mod files;
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<crate::Text>) -> 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<crate::Text>) -> 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);
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);
#[cfg(feature = "rich-text")]
default_modifier!(RichTextField);
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 fn switch(mut self) -> Self {
self.0.control = ControlKind::Custom(Arc::new(super::Switch));
self.0.checkbox = true;
self
}
pub(crate) fn is_checkbox(&self) -> bool {
self.0.is_checkbox()
}
pub fn accepted(mut self) -> Self {
self.0.accepted = true;
self
}
}
common_modifiers!(TextField);
#[cfg(feature = "rich-text")]
common_modifiers!(RichTextField);
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());
self.0.checkbox = false;
ChoiceField(self.0, PhantomData)
}
}
)*};
}
to_choice!(TextField, FileField, CustomField);
impl<F> TextField<F> {
#[cfg(feature = "rich-text")]
pub fn rich_text(mut self) -> RichTextField<F> {
let input = self.text().input_type();
self.0.control = ControlKind::RichText(RichTextControl {
non_text: (input != "text").then_some(input),
..RichTextControl::default()
});
RichTextField(self.0, PhantomData)
}
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().kind = Kind::Email;
self
}
pub fn url(mut self) -> Self {
self.text().kind = Kind::Url;
self
}
pub fn tel(mut self) -> Self {
self.text().kind = Kind::Tel;
self
}
pub fn password(mut self) -> Self {
self.text().password = true;
self
}
pub(crate) fn current_password(mut self) -> Self {
let text = self.text();
text.password = true;
text.current_password = true;
self
}
pub fn unique(mut self) -> Self {
self.text().unique = true;
self
}
pub fn placeholder(mut self, placeholder: impl Into<crate::Text>) -> Self {
self.text().placeholder = Some(placeholder.into());
self
}
pub fn prefix(mut self, text: impl Into<crate::Text>) -> Self {
self.text().prefix = Some(text.into());
self
}
pub fn suffix(mut self, text: impl Into<crate::Text>) -> Self {
self.text().suffix = Some(text.into());
self
}
pub fn suggestions(mut self, values: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.text().suggestions = values.into_iter().map(Into::into).collect();
self
}
pub fn multiline(mut self, rows: u32) -> Self {
self.text().rows = Some(rows);
self
}
pub fn min_length(mut self, chars: usize) -> Self {
self.text().rules.min_length = Some(chars);
self
}
pub fn max_length(mut self, chars: usize) -> Self {
self.text().rules.max_length = Some(chars);
self
}
pub fn min(mut self, bound: impl std::fmt::Display) -> Self {
self.text().rules.min = Some(bound.to_string());
self
}
pub fn max(mut self, bound: impl std::fmt::Display) -> Self {
self.text().rules.max = Some(bound.to_string());
self
}
pub fn pattern(mut self, pattern: impl Into<String>) -> Self {
self.text().rules.pattern = Some(Pattern::new(pattern.into()));
self
}
}
impl<F> RepeaterField<F> {
fn repeater(&mut self) -> &mut RepeaterControl {
match &mut self.0.control {
ControlKind::Repeater(repeater) => repeater,
_ => unreachable!("a RepeaterField holds a repeater control"),
}
}
pub fn add_label(mut self, label: impl Into<crate::Text>) -> Self {
self.repeater().add_label = Some(label.into());
self
}
pub fn min_items(mut self, count: usize) -> Self {
self.repeater().bounds.min = Some(count);
self
}
pub fn max_items(mut self, count: usize) -> Self {
self.repeater().bounds.max = Some(count);
self
}
}
mod into;
#[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
);
}