use jiff::tz::TimeZone;
use super::TypedValue;
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a form scalar",
label = "a form field of this type has no text spelling",
note = "a form scalar is `String`, a `TypedValue` type, an `Options` enum, or an `Option` of \
one; implement `TypedValue` for an app type, derive `Options` for a unit enum, or \
mark an `EmbeddedForm` value `#[form(embed)]`"
)]
pub trait FormScalar: Sized {
const INPUT_TYPE: &'static str = "text";
const STEP: Option<&'static str> = None;
const SORTABLE: bool = true;
fn input_value(stored: &str, zone: &TimeZone) -> String {
let _ = zone;
stored.to_string()
}
fn form_value(posted: &str, zone: &TimeZone) -> String {
let _ = zone;
posted.to_string()
}
fn parse_form(value: &str) -> std::result::Result<Self, crate::Text>;
fn to_form(&self) -> String;
fn to_label(&self) -> crate::Text {
self.to_form().into()
}
fn color(&self) -> Option<crate::Color> {
None
}
fn icon(&self) -> Option<topcoat::icon::IconData> {
None
}
fn order(&self, _other: &Self) -> Option<std::cmp::Ordering> {
None
}
#[doc(hidden)]
fn compared_as() -> &'static str {
std::any::type_name::<Self>()
}
}
impl FormScalar for String {
fn parse_form(value: &str) -> std::result::Result<Self, crate::Text> {
Ok(value.to_string())
}
fn to_form(&self) -> String {
self.clone()
}
}
impl<T: TypedValue> FormScalar for T {
const INPUT_TYPE: &'static str = T::INPUT_TYPE;
const STEP: Option<&'static str> = T::STEP;
const SORTABLE: bool = T::SORTABLE;
fn input_value(stored: &str, zone: &TimeZone) -> String {
T::input_value(stored, zone)
}
fn form_value(posted: &str, zone: &TimeZone) -> String {
T::form_value(posted, zone)
}
fn parse_form(value: &str) -> std::result::Result<Self, crate::Text> {
T::parse_input(value).ok_or_else(|| {
crate::text!(
"tablo-field-not-a",
value = value,
kind = crate::Text::message(T::NOUN)
)
})
}
fn to_form(&self) -> String {
self.form_spelling()
}
fn order(&self, other: &Self) -> Option<std::cmp::Ordering> {
TypedValue::order(self, other)
}
}
#[doc(hidden)]
pub trait NullableScalar: FormScalar {}
impl<T: TypedValue> NullableScalar for T {}
impl<T: NullableScalar> FormScalar for Option<T> {
const INPUT_TYPE: &'static str = T::INPUT_TYPE;
const STEP: Option<&'static str> = T::STEP;
const SORTABLE: bool = T::SORTABLE;
fn input_value(stored: &str, zone: &TimeZone) -> String {
T::input_value(stored, zone)
}
fn form_value(posted: &str, zone: &TimeZone) -> String {
T::form_value(posted, zone)
}
fn parse_form(value: &str) -> std::result::Result<Self, crate::Text> {
T::parse_form(value).map(Some)
}
fn to_form(&self) -> String {
self.as_ref().map(T::to_form).unwrap_or_default()
}
fn to_label(&self) -> crate::Text {
self.as_ref().map(T::to_label).unwrap_or_default()
}
fn color(&self) -> Option<crate::Color> {
self.as_ref().and_then(T::color)
}
fn icon(&self) -> Option<topcoat::icon::IconData> {
self.as_ref().and_then(T::icon)
}
fn order(&self, other: &Self) -> Option<std::cmp::Ordering> {
T::order(self.as_ref()?, other.as_ref()?)
}
fn compared_as() -> &'static str {
T::compared_as()
}
}
impl FormScalar for Option<String> {
fn parse_form(value: &str) -> std::result::Result<Self, crate::Text> {
Ok(Some(value.to_string()))
}
fn compared_as() -> &'static str {
String::compared_as()
}
fn to_form(&self) -> String {
self.clone().unwrap_or_default()
}
}
#[doc(hidden)]
pub fn assert_form_scalar<T: FormScalar>() {}