Skip to main content

credential_exchange_format/
editable_field.rs

1use std::{borrow::Cow, fmt, str};
2
3use chrono::{Month, NaiveDate};
4use serde::{
5    de::{
6        value::{StrDeserializer, StringDeserializer},
7        DeserializeOwned, Visitor,
8    },
9    ser::SerializeStruct,
10    Deserialize, Serialize,
11};
12
13use crate::{B64Url, Extension};
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct EditableField<T, E = ()> {
17    /// A unique identifier for the [EditableField] which is machine generated and an opaque byte
18    /// sequence with a maximum size of 64 bytes. It SHOULD NOT be displayed to the user.
19    pub id: Option<B64Url>,
20    /// This member contains the fieldType defined by the user.
21    pub value: Expected<T>,
22    /// This member contains a user facing value describing the value stored. This value MAY be
23    /// user defined.
24    pub label: Option<String>,
25    /// This member permits the exporting provider to add additional information associated to this
26    /// [EditableField]. This MAY be used to provide an exchange where a minimal amount of
27    /// information is lost.
28    pub extensions: Option<Vec<Extension<E>>>,
29}
30
31/// A field of the incorrect type that was passed instead of an expected field type.
32///
33/// For example if the spec requires a `string` type, but the user passes in
34/// `concealed-string` instead.
35#[derive(Clone, Debug, PartialEq, Eq)]
36#[non_exhaustive]
37pub enum UnexpectedField {
38    String(EditableFieldString),
39    ConcealedString(EditableFieldConcealedString),
40    Boolean(EditableFieldBoolean),
41    Date(EditableFieldDate),
42    YearMonth(EditableFieldYearMonth),
43    SubdivisionCode(EditableFieldSubdivisionCode),
44    CountryCode(EditableFieldCountryCode),
45    WifiNetworkSecurityType(EditableFieldWifiNetworkSecurityType),
46    Email(EditableFieldEmail),
47    Number(EditableFieldNumber),
48    Unknown {
49        /// The unexpected field type.
50        field_type: FieldType,
51        /// The Unknown string passed as a value of type `field_type`.
52        ///
53        /// Example: a `date` that doesn't conform to `yyyy-mm-dd`.
54        value: String,
55    },
56}
57
58impl UnexpectedField {
59    /// Returns the [`FieldType`] for this value.
60    #[inline]
61    pub fn field_type(&self) -> FieldType {
62        match self {
63            Self::String(_) => EditableFieldString::field_type(),
64            Self::ConcealedString(_) => EditableFieldConcealedString::field_type(),
65            Self::Boolean(_) => EditableFieldBoolean::field_type(),
66            Self::Date(_) => EditableFieldDate::field_type(),
67            Self::YearMonth(_) => EditableFieldYearMonth::field_type(),
68            Self::SubdivisionCode(_) => EditableFieldSubdivisionCode::field_type(),
69            Self::CountryCode(_) => EditableFieldCountryCode::field_type(),
70            Self::WifiNetworkSecurityType(_) => EditableFieldWifiNetworkSecurityType::field_type(),
71            Self::Email(_) => EditableFieldEmail::field_type(),
72            Self::Number(_) => EditableFieldNumber::field_type(),
73            Self::Unknown { field_type, .. } => field_type.clone(),
74        }
75    }
76}
77
78impl From<UnexpectedField> for String {
79    #[inline]
80    fn from(value: UnexpectedField) -> Self {
81        match value {
82            UnexpectedField::String(v) => v.0,
83            UnexpectedField::ConcealedString(v) => v.0,
84            UnexpectedField::WifiNetworkSecurityType(v) => v.into(),
85            UnexpectedField::SubdivisionCode(v) => v.0,
86            UnexpectedField::CountryCode(v) => v.0,
87            UnexpectedField::Unknown { value: v, .. } => v,
88            UnexpectedField::Email(v) => v.0,
89            UnexpectedField::Number(v) => v.0,
90            UnexpectedField::Boolean(v) => v.into(),
91            UnexpectedField::Date(v) => v.into(),
92            UnexpectedField::YearMonth(v) => v.into(),
93        }
94    }
95}
96
97/// Holds onto an editable field, and records whether it was an expected or unexpected field.
98#[derive(Clone, Debug, PartialEq, Eq)]
99enum ExpectedInner<T> {
100    /// The field we found had the same field type we expected.
101    Expected(T),
102    /// The field we found had a different field type than what we expected.
103    Unexpected(UnexpectedField),
104}
105
106/// Holds onto an editable field, and records whether it was an expected or unexpected field.
107///
108/// This is used as a type-safe wrapper around an editable field to encode expectations around
109/// the type of field we are expecting in a credential.
110///
111/// This can only be instantiated via the exposed `From` implementation so that newly
112/// constructed credentials remain spec-compliant with regards to their fields.
113#[derive(Clone, Debug, PartialEq, Eq)]
114pub struct Expected<T>(ExpectedInner<T>);
115
116impl<T> Expected<T>
117where
118    T: EditableFieldType,
119{
120    /// Returns the real [`FieldType`] of this field.
121    #[inline]
122    pub fn field_type(&self) -> FieldType {
123        match &self.0 {
124            ExpectedInner::Expected(_) => T::field_type(),
125            ExpectedInner::Unexpected(f) => f.field_type(),
126        }
127    }
128
129    /// Tries to return the field type we were expecting.
130    #[inline]
131    pub fn as_expected(&self) -> Result<&T, &UnexpectedField> {
132        match &self.0 {
133            ExpectedInner::Expected(t) => Ok(t),
134            ExpectedInner::Unexpected(f) => Err(f),
135        }
136    }
137
138    /// Tries to return the field type we were expecting as an owned type.
139    #[inline]
140    pub fn into_expected(self) -> Result<T, UnexpectedField> {
141        match self.0 {
142            ExpectedInner::Expected(t) => Ok(t),
143            ExpectedInner::Unexpected(f) => Err(f),
144        }
145    }
146}
147
148impl<T> From<T> for Expected<T> {
149    #[inline]
150    fn from(t: T) -> Self {
151        Self(ExpectedInner::Expected(t))
152    }
153}
154
155impl<T> From<Expected<T>> for String
156where
157    T: Into<String>,
158{
159    #[inline]
160    fn from(value: Expected<T>) -> Self {
161        match value.0 {
162            ExpectedInner::Expected(t) => t.into(),
163            ExpectedInner::Unexpected(f) => f.into(),
164        }
165    }
166}
167
168#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
169#[serde(rename_all = "kebab-case")]
170#[non_exhaustive]
171pub enum FieldType {
172    /// A UTF-8 encoded string value which is unconcealed and does not have a specified format.
173    String,
174    /// A UTF-8 encoded string value which should be considered secret and not displayed unless the
175    /// user explicitly requests it.
176    ConcealedString,
177    /// A UTF-8 encoded string value which follows the format specified in
178    /// [RFC5322](https://www.rfc-editor.org/rfc/rfc5322#section-3.4). This field SHOULD be
179    /// unconcealed.
180    Email,
181    /// A stringified numeric value which is unconcealed.
182    Number,
183    /// A boolean value which is unconcealed. It MUST be of the values "true" or "false".
184    Boolean,
185    /// A string value representing a calendar date which follows the format specified in
186    /// [RFC3339](https://www.rfc-editor.org/rfc/rfc3339).
187    Date,
188    /// A string value representing a calendar date which follows the date-fullyear "-" date-month
189    /// pattern as established in [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) Appendix A.
190    /// This is equivalent to the `YYYY-MM` format specified in ISO8601.
191    YearMonth,
192    /// A string value representing a value that SHOULD be a member of WIFINetworkSecurityType.
193    WifiNetworkSecurityType,
194    /// A string value which MUST follow the ISO3166-1 alpha-2 format.
195    SubdivisionCode,
196    /// A string which MUST follow the ISO3166-2 format.
197    CountryCode,
198    #[serde(untagged)]
199    Unknown(String),
200}
201
202/// A trait to associate the field structs with their `field_type` tag.
203pub trait EditableFieldType {
204    /// The `field_type` value associated with the type
205    fn field_type() -> FieldType;
206}
207
208impl<T, E> Serialize for EditableField<T, E>
209where
210    T: EditableFieldType + Serialize,
211    E: Serialize,
212{
213    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214    where
215        S: serde::Serializer,
216    {
217        let len = 2
218            + self.id.is_some() as usize
219            + self.label.is_some() as usize
220            + self.extensions.is_some() as usize;
221        let mut state = serializer.serialize_struct("editable_field", len)?;
222
223        if let Some(ref id) = self.id {
224            state.serialize_field("id", id)?;
225        } else {
226            state.skip_field("id")?;
227        }
228
229        state.serialize_field("fieldType", &self.value.field_type())?;
230        match &self.value.0 {
231            ExpectedInner::Expected(t) => {
232                state.serialize_field("value", &t)?;
233            }
234            ExpectedInner::Unexpected(t) => match t {
235                UnexpectedField::String(v) => state.serialize_field("value", &v)?,
236                UnexpectedField::ConcealedString(v) => state.serialize_field("value", &v)?,
237                UnexpectedField::WifiNetworkSecurityType(v) => {
238                    state.serialize_field("value", &v)?
239                }
240                UnexpectedField::SubdivisionCode(v) => state.serialize_field("value", &v)?,
241                UnexpectedField::CountryCode(v) => state.serialize_field("value", &v)?,
242                UnexpectedField::Unknown { value: v, .. } => state.serialize_field("value", &v)?,
243                UnexpectedField::Boolean(b) => {
244                    let v = if b.0 { "true" } else { "false" };
245                    state.serialize_field("value", v)?
246                }
247                UnexpectedField::Date(date) => state.serialize_field("value", &date)?,
248                UnexpectedField::YearMonth(v) => state.serialize_field("value", &v)?,
249                UnexpectedField::Email(v) => state.serialize_field("value", &v)?,
250                UnexpectedField::Number(v) => state.serialize_field("value", &v)?,
251            },
252        }
253
254        if let Some(ref label) = self.label {
255            state.serialize_field("label", label)?;
256        } else {
257            state.skip_field("label")?;
258        }
259
260        if let Some(ref ext) = self.extensions {
261            if ext.is_empty() {
262                state.skip_field("extensions")?;
263            } else {
264                state.serialize_field("extensions", ext)?;
265            }
266        } else {
267            state.skip_field("extensions")?;
268        }
269
270        state.end()
271    }
272}
273
274#[derive(Deserialize)]
275#[serde(rename_all = "camelCase")]
276struct EditableFieldHelper<E> {
277    #[serde(default)]
278    id: Option<B64Url>,
279    value: String,
280    field_type: FieldType,
281    #[serde(default)]
282    label: Option<String>,
283    #[serde(default = "none::<E>")]
284    extensions: Option<Vec<Extension<E>>>,
285}
286
287// Need to use this instead of the normal default,
288// otherwise the derive creates a `E: Default` constraint.
289fn none<E>() -> Option<Vec<Extension<E>>> {
290    None
291}
292
293impl<'de, T, E> Deserialize<'de> for EditableField<T, E>
294where
295    T: EditableFieldType + DeserializeOwned,
296    E: Deserialize<'de>,
297{
298    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
299    where
300        D: serde::Deserializer<'de>,
301    {
302        let helper: EditableFieldHelper<E> = EditableFieldHelper::deserialize(deserializer)?;
303
304        let value = if T::field_type() == helper.field_type {
305            match T::deserialize(StrDeserializer::<serde::de::value::Error>::new(
306                helper.value.as_str(),
307            )) {
308                Ok(t) => ExpectedInner::Expected(t),
309                Err(_) => ExpectedInner::Unexpected(UnexpectedField::Unknown {
310                    field_type: helper.field_type,
311                    value: helper.value,
312                }),
313            }
314        } else {
315            macro_rules! deserialize_from_str {
316                ($t:tt => $variant:ident) => {
317                    match $t::deserialize(StrDeserializer::<D::Error>::new(&helper.value)) {
318                        Ok(v) => UnexpectedField::$variant(v),
319                        _ => UnexpectedField::Unknown {
320                            field_type: helper.field_type,
321                            value: helper.value,
322                        },
323                    }
324                };
325            }
326
327            let value = match helper.field_type {
328                FieldType::String => UnexpectedField::String(EditableFieldString(helper.value)),
329                FieldType::ConcealedString => {
330                    UnexpectedField::ConcealedString(EditableFieldConcealedString(helper.value))
331                }
332                FieldType::Boolean => deserialize_from_str!(EditableFieldBoolean => Boolean),
333                FieldType::Date => deserialize_from_str!(EditableFieldDate => Date),
334                FieldType::YearMonth => deserialize_from_str!(EditableFieldYearMonth => YearMonth),
335                FieldType::WifiNetworkSecurityType => {
336                    deserialize_from_str!(EditableFieldWifiNetworkSecurityType => WifiNetworkSecurityType)
337                }
338                FieldType::SubdivisionCode => {
339                    UnexpectedField::SubdivisionCode(EditableFieldSubdivisionCode(helper.value))
340                }
341                FieldType::CountryCode => {
342                    UnexpectedField::CountryCode(EditableFieldCountryCode(helper.value))
343                }
344                FieldType::Email => UnexpectedField::Email(EditableFieldEmail(helper.value)),
345                FieldType::Number => UnexpectedField::Number(EditableFieldNumber(helper.value)),
346                FieldType::Unknown(_) => UnexpectedField::Unknown {
347                    field_type: helper.field_type,
348                    value: helper.value,
349                },
350            };
351
352            ExpectedInner::Unexpected(value)
353        };
354
355        Ok(Self {
356            id: helper.id,
357            value: Expected(value),
358            label: helper.label,
359            extensions: helper.extensions,
360        })
361    }
362}
363
364// Helper for converting inner types into EditableField
365impl<T, E> From<T> for EditableField<T, E> {
366    fn from(s: T) -> Self {
367        EditableField {
368            id: None,
369            value: s.into(),
370            label: None,
371            extensions: None,
372        }
373    }
374}
375
376/// Macro to define a string-backed `EditableField` type with its `EditableFieldType` impl
377/// and `From` conversions for `String`.
378macro_rules! editable_field_string_type {
379    ($name:ident, $variant:ident) => {
380        #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
381        #[serde(transparent)]
382        pub struct $name(pub String);
383
384        impl EditableFieldType for $name {
385            fn field_type() -> FieldType {
386                FieldType::$variant
387            }
388        }
389
390        impl From<String> for $name {
391            #[inline]
392            fn from(s: String) -> Self {
393                $name(s)
394            }
395        }
396
397        impl<E> From<String> for EditableField<$name, E> {
398            #[inline]
399            fn from(s: String) -> Self {
400                $name(s).into()
401            }
402        }
403
404        impl<E> From<EditableField<$name, E>> for String {
405            fn from(s: EditableField<$name, E>) -> Self {
406                match s.value.0 {
407                    ExpectedInner::Expected(f) => f.0.into(),
408                    ExpectedInner::Unexpected(f) => f.into(),
409                }
410            }
411        }
412
413        impl From<$name> for String {
414            #[inline]
415            fn from(v: $name) -> Self {
416                v.0
417            }
418        }
419
420        impl AsRef<str> for $name {
421            #[inline]
422            fn as_ref(&self) -> &str {
423                &self.0
424            }
425        }
426    };
427}
428
429editable_field_string_type!(EditableFieldString, String);
430editable_field_string_type!(EditableFieldConcealedString, ConcealedString);
431editable_field_string_type!(EditableFieldEmail, Email);
432editable_field_string_type!(EditableFieldNumber, Number);
433editable_field_string_type!(EditableFieldSubdivisionCode, SubdivisionCode);
434editable_field_string_type!(EditableFieldCountryCode, CountryCode);
435
436#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
437#[serde(transparent)]
438pub struct EditableFieldBoolean(#[serde(with = "serde_bool")] pub bool);
439impl EditableFieldType for EditableFieldBoolean {
440    fn field_type() -> FieldType {
441        FieldType::Boolean
442    }
443}
444
445impl<E> From<bool> for EditableField<EditableFieldBoolean, E> {
446    fn from(b: bool) -> Self {
447        EditableFieldBoolean(b).into()
448    }
449}
450
451impl From<EditableFieldBoolean> for String {
452    #[inline]
453    fn from(value: EditableFieldBoolean) -> Self {
454        if value.0 { "true" } else { "false" }.into()
455    }
456}
457
458#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
459#[serde(transparent)]
460pub struct EditableFieldDate(pub NaiveDate);
461impl EditableFieldType for EditableFieldDate {
462    fn field_type() -> FieldType {
463        FieldType::Date
464    }
465}
466
467impl From<EditableFieldDate> for String {
468    #[inline]
469    fn from(value: EditableFieldDate) -> Self {
470        value.0.format("%Y-%m-%d").to_string()
471    }
472}
473
474#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
475#[serde(into = "String")]
476pub struct EditableFieldYearMonth {
477    /// The year in the format `YYYY`
478    pub year: u16,
479    /// The month in the format `MM`
480    pub month: Month,
481}
482
483impl From<EditableFieldYearMonth> for String {
484    #[inline]
485    fn from(value: EditableFieldYearMonth) -> String {
486        format!("{:04}-{:02}", value.year, value.month.number_from_month())
487    }
488}
489
490impl EditableFieldType for EditableFieldYearMonth {
491    fn field_type() -> FieldType {
492        FieldType::YearMonth
493    }
494}
495
496impl<'de> Deserialize<'de> for EditableFieldYearMonth {
497    fn deserialize<D>(deserializer: D) -> Result<EditableFieldYearMonth, D::Error>
498    where
499        D: serde::Deserializer<'de>,
500    {
501        let s = deserializer.deserialize_str(CowVisitor)?;
502        let (year_str, month_str) = s
503            .split_once('-')
504            .ok_or_else(|| serde::de::Error::custom("Invalid format"))?;
505
506        Ok(EditableFieldYearMonth {
507            year: year_str
508                .parse::<u16>()
509                .map_err(|_| serde::de::Error::custom("Invalid year"))?,
510            month: month_str
511                .parse::<u8>()
512                .map_err(|_| serde::de::Error::custom("Invalid month"))?
513                .try_into()
514                .map_err(|_| serde::de::Error::custom("Invalid month"))?,
515        })
516    }
517}
518
519/// Deserialize strings into `Cow` to avoid unnecessary allocations
520struct CowVisitor;
521impl<'de> Visitor<'de> for CowVisitor {
522    type Value = Cow<'de, str>;
523
524    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
525        formatter.write_str("a string")
526    }
527
528    fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
529    where
530        E: serde::de::Error,
531    {
532        Ok(Cow::Borrowed(value))
533    }
534
535    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
536    where
537        E: serde::de::Error,
538    {
539        Ok(Cow::Owned(value.to_owned()))
540    }
541
542    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
543    where
544        E: serde::de::Error,
545    {
546        Ok(Cow::Owned(value))
547    }
548}
549
550#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
551#[serde(rename_all = "kebab-case")]
552#[non_exhaustive]
553pub enum EditableFieldWifiNetworkSecurityType {
554    Unsecured,
555    WpaPersonal,
556    Wpa2Personal,
557    Wpa3Personal,
558    Wep,
559
560    #[serde(untagged)]
561    Other(String),
562}
563impl EditableFieldType for EditableFieldWifiNetworkSecurityType {
564    fn field_type() -> FieldType {
565        FieldType::WifiNetworkSecurityType
566    }
567}
568
569impl From<EditableFieldWifiNetworkSecurityType> for String {
570    #[inline]
571    fn from(value: EditableFieldWifiNetworkSecurityType) -> Self {
572        match value {
573            EditableFieldWifiNetworkSecurityType::Unsecured => "unsecured".into(),
574            EditableFieldWifiNetworkSecurityType::WpaPersonal => "wpa-personal".into(),
575            EditableFieldWifiNetworkSecurityType::Wpa2Personal => "wpa2-personal".into(),
576            EditableFieldWifiNetworkSecurityType::Wpa3Personal => "wpa3-personal".into(),
577            EditableFieldWifiNetworkSecurityType::Wep => "wep".into(),
578            EditableFieldWifiNetworkSecurityType::Other(o) => o,
579        }
580    }
581}
582
583/// Helper wrapper for `CustomFieldsCredential`.
584#[derive(Clone, Debug, Serialize)]
585#[serde(untagged, bound(deserialize = "E: Deserialize<'de>"))]
586#[non_exhaustive]
587pub enum EditableFieldValue<E = ()> {
588    String(EditableField<EditableFieldString, E>),
589    ConcealedString(EditableField<EditableFieldConcealedString, E>),
590    Email(EditableField<EditableFieldEmail, E>),
591    Number(EditableField<EditableFieldNumber, E>),
592    Boolean(EditableField<EditableFieldBoolean, E>),
593    Date(EditableField<EditableFieldDate, E>),
594    YearMonth(EditableField<EditableFieldYearMonth, E>),
595    SubdivisionCode(EditableField<EditableFieldSubdivisionCode, E>),
596    CountryCode(EditableField<EditableFieldCountryCode, E>),
597    WifiNetworkSecurityType(EditableField<EditableFieldWifiNetworkSecurityType, E>),
598}
599
600impl<'de, E> Deserialize<'de> for EditableFieldValue<E>
601where
602    E: Deserialize<'de>,
603{
604    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
605    where
606        D: serde::Deserializer<'de>,
607    {
608        let helper: EditableFieldHelper<E> = EditableFieldHelper::deserialize(deserializer)?;
609
610        macro_rules! deserialize_field {
611            ($t: tt) => {{
612                let v = $t::deserialize(StringDeserializer::new(helper.value))?;
613
614                EditableField {
615                    id: helper.id,
616                    value: Expected(ExpectedInner::Expected(v)),
617                    label: helper.label,
618                    extensions: helper.extensions,
619                }
620            }};
621        }
622
623        let res = match helper.field_type {
624            FieldType::String => Self::String(deserialize_field!(EditableFieldString)),
625            FieldType::ConcealedString => {
626                Self::ConcealedString(deserialize_field!(EditableFieldConcealedString))
627            }
628            FieldType::Boolean => Self::Boolean(deserialize_field!(EditableFieldBoolean)),
629            FieldType::Date => Self::Date(deserialize_field!(EditableFieldDate)),
630            FieldType::YearMonth => Self::YearMonth(deserialize_field!(EditableFieldYearMonth)),
631            FieldType::WifiNetworkSecurityType => Self::WifiNetworkSecurityType(
632                deserialize_field!(EditableFieldWifiNetworkSecurityType),
633            ),
634            FieldType::SubdivisionCode => {
635                Self::SubdivisionCode(deserialize_field!(EditableFieldSubdivisionCode))
636            }
637            FieldType::CountryCode => {
638                Self::CountryCode(deserialize_field!(EditableFieldCountryCode))
639            }
640            FieldType::Email => Self::Email(deserialize_field!(EditableFieldEmail)),
641            FieldType::Number => Self::Number(deserialize_field!(EditableFieldNumber)),
642            FieldType::Unknown(_) => {
643                return Err(serde::de::Error::custom("Unknown custom field type"))
644            }
645        };
646
647        Ok(res)
648    }
649}
650
651mod serde_bool {
652    pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
653    where
654        S: serde::Serializer,
655    {
656        serializer.serialize_str(&value.to_string())
657    }
658
659    pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
660    where
661        D: serde::Deserializer<'de>,
662    {
663        let s = deserializer.deserialize_str(super::CowVisitor)?;
664
665        s.trim()
666            .to_lowercase()
667            .parse()
668            .map_err(serde::de::Error::custom)
669    }
670}
671
672#[cfg(test)]
673mod tests {
674    use serde_json::json;
675
676    use super::*;
677
678    #[test]
679    fn test_serialize_editable_field_string() {
680        let field: EditableField<EditableFieldString> = EditableField {
681            id: None,
682            value: EditableFieldString("value".to_string()).into(),
683            label: Some("label".to_string()),
684            extensions: None,
685        };
686        let json = json!({
687            "value": "value",
688            "fieldType": "string",
689            "label": "label",
690        });
691        assert_eq!(serde_json::to_value(&field).unwrap(), json);
692    }
693
694    #[test]
695    fn test_deserialize_field_string() {
696        let json = json!({
697            "value": "value",
698            "fieldType": "string",
699            "label": "label",
700        });
701        let field: EditableField<EditableFieldString> = serde_json::from_value(json).unwrap();
702
703        assert_eq!(
704            field,
705            EditableField {
706                id: None,
707                value: EditableFieldString("value".to_string()).into(),
708                label: Some("label".to_string()),
709                extensions: None,
710            }
711        );
712    }
713
714    #[test]
715    fn test_serialize_field_concealed_string() {
716        let field: EditableField<EditableFieldConcealedString> = EditableField {
717            id: None,
718            value: EditableFieldConcealedString("value".to_string()).into(),
719            label: Some("label".to_string()),
720            extensions: None,
721        };
722        let json = json!({
723            "fieldType": "concealed-string",
724            "value": "value",
725            "label": "label",
726        });
727        assert_eq!(serde_json::to_value(&field).unwrap(), json);
728    }
729
730    #[test]
731    fn test_deserialize_field_wrong_type() {
732        let json = json!({
733            "value": "value",
734            "fieldType": "string",
735            "label": "label",
736        });
737        let field: Result<EditableField<EditableFieldConcealedString>, _> =
738            serde_json::from_value(json);
739
740        assert!(field.unwrap().value.as_expected().is_err());
741    }
742
743    #[test]
744    fn test_deserialize_field_bad_value_string() {
745        let json = json!({
746            "value": 5,
747            "fieldType": "string",
748            "label": "label",
749        });
750        let field: Result<EditableField<EditableFieldString>, _> = serde_json::from_value(json);
751
752        assert!(field.is_err());
753    }
754
755    #[test]
756    fn test_deserialize_field_bad_value_bool() {
757        let json = json!({
758            "value": "bad",
759            "fieldType": "bool",
760            "label": "label",
761        });
762        let field: Result<EditableField<EditableFieldBoolean>, _> = serde_json::from_value(json);
763
764        assert!(field.unwrap().value.as_expected().is_err());
765    }
766
767    #[test]
768    fn test_deserialize_field_missing_type() {
769        let json = json!({
770            "value": "value",
771            "label": "label",
772        });
773        let field: Result<EditableField<EditableFieldConcealedString>, _> =
774            serde_json::from_value(json);
775
776        assert!(field.is_err());
777    }
778
779    #[test]
780    fn test_deserialize_field_concealed_string() {
781        let json = json!({
782            "value": "value",
783            "fieldType": "concealed-string",
784            "label": "label",
785        });
786        let field: EditableField<EditableFieldConcealedString> =
787            serde_json::from_value(json).unwrap();
788
789        assert_eq!(
790            field,
791            EditableField {
792                id: None,
793                value: EditableFieldConcealedString("value".to_string()).into(),
794                label: Some("label".to_string()),
795                extensions: None,
796            }
797        );
798    }
799
800    #[test]
801    fn test_serialize_field_boolean() {
802        let field: EditableField<EditableFieldBoolean> = EditableField {
803            id: None,
804            value: EditableFieldBoolean(true).into(),
805            label: Some("label".to_string()),
806            extensions: None,
807        };
808        let json = json!({
809            "fieldType": "boolean",
810            "value": "true",
811            "label": "label",
812        });
813        assert_eq!(serde_json::to_value(&field).unwrap(), json);
814    }
815
816    #[test]
817    fn test_serialize_field_date() {
818        let field: EditableField<EditableFieldDate> = EditableField {
819            id: None,
820            value: EditableFieldDate(NaiveDate::from_ymd_opt(2025, 2, 24).unwrap()).into(),
821            label: None,
822            extensions: None,
823        };
824        let json = json!({
825            "fieldType": "date",
826            "value": "2025-02-24",
827        });
828        assert_eq!(serde_json::to_value(&field).unwrap(), json);
829    }
830
831    #[test]
832    fn test_serialize_editable_field_year_month() {
833        let field: EditableField<EditableFieldYearMonth> = EditableField {
834            id: None,
835            value: EditableFieldYearMonth {
836                year: 2025,
837                month: Month::February,
838            }
839            .into(),
840            label: None,
841            extensions: None,
842        };
843        let json = json!({
844            "fieldType": "year-month",
845            "value": "2025-02",
846        });
847        assert_eq!(serde_json::to_value(&field).unwrap(), json);
848    }
849
850    #[test]
851    fn test_deserialize_editable_field_year_month() {
852        let json = json!({
853            "fieldType": "year-month",
854            "value": "2025-02",
855        });
856        let field: EditableField<EditableFieldYearMonth> = serde_json::from_value(json).unwrap();
857
858        assert_eq!(
859            field,
860            EditableField {
861                id: None,
862                value: EditableFieldYearMonth {
863                    year: 2025,
864                    month: Month::February,
865                }
866                .into(),
867                label: None,
868                extensions: None,
869            }
870        );
871    }
872
873    #[test]
874    fn test_deserialize_editable_field_year_month_invalid_format() {
875        let json = json!({
876            "fieldType": "year-month",
877            "value": "2025/02",
878        });
879        let field: EditableField<EditableFieldYearMonth> = serde_json::from_value(json).unwrap();
880        assert_eq!(
881            field.value.as_expected(),
882            Err(&UnexpectedField::Unknown {
883                field_type: FieldType::YearMonth,
884                value: "2025/02".into(),
885            })
886        );
887    }
888
889    #[test]
890    fn test_extension_round_trip() {
891        let json = json!({
892            "fieldType": "string",
893            "value": "hello",
894            "extensions": [
895                {
896                    "name": "test",
897                    "contents": "world"
898                }
899            ]
900        });
901        #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
902        #[serde(tag = "name", rename_all = "camelCase")]
903        enum CustomExtension {
904            Test { contents: String },
905        }
906
907        let field: EditableField<EditableFieldString, CustomExtension> =
908            serde_json::from_value(json.clone()).expect("Could not deserialize custom extensions");
909
910        assert_eq!(
911            field,
912            EditableField {
913                id: None,
914                value: EditableFieldString("hello".to_string()).into(),
915                label: None,
916                extensions: Some(vec![Extension::External(CustomExtension::Test {
917                    contents: "world".into()
918                })])
919            }
920        );
921
922        let returned = serde_json::to_value(field).expect("Could not serialize custom extensions");
923
924        assert_eq!(returned, json);
925    }
926
927    #[test]
928    fn editable_string_deserialized_from_others() {
929        for (field_type, expected) in [
930            (
931                "concealed-string",
932                UnexpectedField::ConcealedString("hello".to_owned().into()),
933            ),
934            (
935                "unknown",
936                UnexpectedField::Unknown {
937                    field_type: FieldType::Unknown("unknown".into()),
938                    value: "hello".into(),
939                },
940            ),
941            (
942                "date",
943                UnexpectedField::Unknown {
944                    field_type: FieldType::Date,
945                    value: "hello".into(),
946                },
947            ),
948        ] {
949            let json = json!({
950                "fieldType": field_type,
951                "value": "hello",
952            });
953
954            let field: EditableField<EditableFieldString> =
955                serde_json::from_value(json.clone()).expect("Could not deserialize field");
956
957            assert_eq!(field.value.into_expected(), Err(expected));
958        }
959    }
960}