use super::{
super::{IntoSchema, OptionSource, Schema},
ChoiceControl, ControlKind, Field, TextControl, choice,
};
macro_rules! common_modifiers {
($builder:ident) => {
impl $builder {
pub fn label(mut self, label: impl Into<String>) -> Self {
self.0.label = label.into();
self
}
pub fn required(mut self) -> Self {
self.0.required = true;
self
}
pub fn optional(mut self) -> Self {
self.0.required = false;
self
}
pub fn name(&self) -> &str {
&self.0.name
}
}
impl From<$builder> for Field {
fn from(builder: $builder) -> Field {
builder.0
}
}
impl IntoSchema for $builder {
fn into_schema(self) -> Schema {
self.0.into_schema()
}
}
};
}
pub struct TextField(pub(super) Field);
pub struct ChoiceField(pub(super) Field);
pub struct FileField(pub(super) Field);
pub struct CustomField(pub(super) Field);
common_modifiers!(TextField);
common_modifiers!(ChoiceField);
common_modifiers!(FileField);
common_modifiers!(CustomField);
impl TextField {
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.0.rules.set_email();
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 ChoiceField {
fn choice(&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().options = options.into_options();
self
}
pub fn searchable(mut self) -> Self {
self.choice().searchable = true;
self
}
pub fn relationship<R>(
mut self,
label: impl Fn(&R::Model) -> String + Send + Sync + 'static,
) -> Self
where
R: OptionSource + 'static,
{
self.choice().relationship = Some(choice::Relationship::new::<R>(label));
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, TextField};
macro_rules! deref_field {
($($builder:ident),*) => {$(
impl std::ops::Deref for $builder {
type Target = Field;
fn deref(&self) -> &Field {
&self.0
}
}
)*};
}
deref_field!(TextField, ChoiceField, FileField, CustomField);
}