use std::iter::zip; use phono_models::presentation::{ DropdownOption, Presentation, PresentationDiscriminants, RFC_3339_S, TextInputMode, }; use serde::Deserialize; use crate::errors::AppError; /// A subset of an HTTP form that represents a [`Presentation`] in its component /// parts. /// /// WARNING: Though this was originally designed to be combined with /// `#[serde(flatten)]`, Serde's buffering strategy causes `flatten` to break /// deserialization of `Vec<_>` fields when provided with a sequence of length /// == 1. `#[serde(flatten)]` should be avoided. /// (Refer to: https://github.com/jplatte/serde_html_form/issues/25.) #[derive(Clone, Debug, Deserialize)] pub(crate) struct PresentationForm { pub(crate) presentation_tag: String, #[serde(default)] pub(crate) dropdown_option_colors: Vec, #[serde(default)] pub(crate) dropdown_option_values: Vec, #[serde(default)] pub(crate) text_input_mode: String, #[serde(default)] pub(crate) timestamp_format: String, } impl TryFrom for Presentation { type Error = AppError; fn try_from(form_value: PresentationForm) -> Result { let discriminant = PresentationDiscriminants::try_from(form_value.presentation_tag.as_str())?; Ok(match discriminant { PresentationDiscriminants::Dropdown => Presentation::Dropdown { allow_custom: false, options: zip( form_value.dropdown_option_colors, form_value.dropdown_option_values, ) .map(|(color, value)| DropdownOption { color, value }) .collect(), }, PresentationDiscriminants::Numeric => Presentation::Numeric {}, PresentationDiscriminants::Text => Presentation::Text { input_mode: TextInputMode::try_from(form_value.text_input_mode.as_str())?, }, PresentationDiscriminants::Timestamp => Presentation::Timestamp { format: if form_value.timestamp_format.is_empty() { RFC_3339_S.to_owned() } else { form_value.timestamp_format }, utc: true, }, PresentationDiscriminants::Uuid => Presentation::Uuid {}, }) } }