1use std::{borrow::Cow, fmt, str};
2
3use chrono::{Month, NaiveDate};
4use serde::{
5 de::{DeserializeOwned, Visitor},
6 ser::SerializeStruct,
7 Deserialize, Serialize,
8};
9
10use crate::{B64Url, Extension};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct EditableField<T, E = ()> {
14 pub id: Option<B64Url>,
17 pub value: T,
19 pub label: Option<String>,
22 pub extensions: Option<Vec<Extension<E>>>,
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "kebab-case")]
30#[non_exhaustive]
31pub enum FieldType {
32 String,
34 ConcealedString,
37 Email,
41 Number,
43 Boolean,
45 Date,
48 YearMonth,
52 WifiNetworkSecurityType,
54 SubdivisionCode,
56 CountryCode,
58 #[serde(untagged)]
59 Unknown(String),
60}
61
62pub trait EditableFieldType {
64 fn field_type(&self) -> FieldType;
66}
67
68impl<T, E> Serialize for EditableField<T, E>
69where
70 T: EditableFieldType + Serialize,
71 E: Serialize,
72{
73 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74 where
75 S: serde::Serializer,
76 {
77 let len = 2
78 + self.id.is_some() as usize
79 + self.label.is_some() as usize
80 + self.extensions.is_some() as usize;
81 let mut state = serializer.serialize_struct("editable_field", len)?;
82
83 if let Some(ref id) = self.id {
84 state.serialize_field("id", id)?;
85 } else {
86 state.skip_field("id")?;
87 }
88
89 state.serialize_field("fieldType", &self.value.field_type())?;
90 state.serialize_field("value", &self.value)?;
91
92 if let Some(ref label) = self.label {
93 state.serialize_field("label", label)?;
94 } else {
95 state.skip_field("label")?;
96 }
97
98 if let Some(ref ext) = self.extensions {
99 if ext.is_empty() {
100 state.skip_field("extensions")?;
101 } else {
102 state.serialize_field("extensions", ext)?;
103 }
104 } else {
105 state.skip_field("extensions")?;
106 }
107
108 state.end()
109 }
110}
111
112impl<'de, T, E> Deserialize<'de> for EditableField<T, E>
113where
114 T: EditableFieldType + DeserializeOwned,
115 E: Deserialize<'de>,
116{
117 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
118 where
119 D: serde::Deserializer<'de>,
120 {
121 #[derive(Deserialize)]
122 #[serde(rename_all = "camelCase")]
123 struct EditableFieldHelper<T, E> {
124 #[serde(default)]
125 id: Option<B64Url>,
126 value: T,
127 field_type: FieldType,
128 #[serde(default)]
129 label: Option<String>,
130 #[serde(default = "none::<E>")]
131 extensions: Option<Vec<Extension<E>>>,
132 }
133 fn none<E>() -> Option<Vec<Extension<E>>> {
136 None
137 }
138
139 let helper: EditableFieldHelper<T, E> = EditableFieldHelper::deserialize(deserializer)?;
140
141 if helper.field_type != helper.value.field_type() {
142 return Err(serde::de::Error::custom(
143 "field_type does not match value type",
144 ));
145 }
146
147 Ok(Self {
148 id: helper.id,
149 value: helper.value,
150 label: helper.label,
151 extensions: helper.extensions,
152 })
153 }
154}
155
156impl<T, E> From<T> for EditableField<T, E> {
158 fn from(s: T) -> Self {
159 EditableField {
160 id: None,
161 value: s,
162 label: None,
163 extensions: None,
164 }
165 }
166}
167
168#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
169#[serde(transparent)]
170pub struct EditableFieldString(pub String);
171impl EditableFieldType for EditableFieldString {
172 fn field_type(&self) -> FieldType {
173 FieldType::String
174 }
175}
176
177impl<E> From<String> for EditableField<EditableFieldString, E> {
178 fn from(s: String) -> Self {
179 EditableFieldString(s).into()
180 }
181}
182
183impl<E> From<EditableField<EditableFieldString, E>> for String {
184 fn from(s: EditableField<EditableFieldString, E>) -> Self {
185 s.value.0
186 }
187}
188
189#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
190#[serde(transparent)]
191pub struct EditableFieldConcealedString(pub String);
192impl EditableFieldType for EditableFieldConcealedString {
193 fn field_type(&self) -> FieldType {
194 FieldType::ConcealedString
195 }
196}
197
198impl<E> From<String> for EditableField<EditableFieldConcealedString, E> {
199 fn from(s: String) -> Self {
200 EditableFieldConcealedString(s).into()
201 }
202}
203
204impl<E> From<EditableField<EditableFieldConcealedString, E>> for String {
205 fn from(s: EditableField<EditableFieldConcealedString, E>) -> Self {
206 s.value.0
207 }
208}
209
210#[derive(Clone, Debug, Serialize, Deserialize)]
211pub struct EditableFieldBoolean(#[serde(with = "serde_bool")] pub bool);
212impl EditableFieldType for EditableFieldBoolean {
213 fn field_type(&self) -> FieldType {
214 FieldType::Boolean
215 }
216}
217
218impl<E> From<bool> for EditableField<EditableFieldBoolean, E> {
219 fn from(b: bool) -> Self {
220 EditableFieldBoolean(b).into()
221 }
222}
223
224impl<E> From<EditableField<EditableFieldBoolean, E>> for bool {
225 fn from(b: EditableField<EditableFieldBoolean, E>) -> Self {
226 b.value.0
227 }
228}
229
230#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
231#[serde(transparent)]
232pub struct EditableFieldDate(pub NaiveDate);
233impl EditableFieldType for EditableFieldDate {
234 fn field_type(&self) -> FieldType {
235 FieldType::Date
236 }
237}
238
239#[derive(Clone, Debug, PartialEq, Eq)]
240pub struct EditableFieldYearMonth {
241 pub year: u16,
243 pub month: Month,
245}
246impl EditableFieldType for EditableFieldYearMonth {
247 fn field_type(&self) -> FieldType {
248 FieldType::YearMonth
249 }
250}
251
252impl Serialize for EditableFieldYearMonth {
253 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
254 where
255 S: serde::Serializer,
256 {
257 serializer.serialize_str(&format!(
258 "{:04}-{:02}",
259 self.year,
260 self.month.number_from_month()
261 ))
262 }
263}
264
265impl<'de> Deserialize<'de> for EditableFieldYearMonth {
266 fn deserialize<D>(deserializer: D) -> Result<EditableFieldYearMonth, D::Error>
267 where
268 D: serde::Deserializer<'de>,
269 {
270 let s = deserializer.deserialize_str(CowVisitor)?;
271 let (year_str, month_str) = s
272 .split_once('-')
273 .ok_or_else(|| serde::de::Error::custom("Invalid format"))?;
274
275 Ok(EditableFieldYearMonth {
276 year: year_str
277 .parse::<u16>()
278 .map_err(|_| serde::de::Error::custom("Invalid year"))?,
279 month: month_str
280 .parse::<u8>()
281 .map_err(|_| serde::de::Error::custom("Invalid month"))?
282 .try_into()
283 .map_err(|_| serde::de::Error::custom("Invalid month"))?,
284 })
285 }
286}
287
288struct CowVisitor;
290impl<'de> Visitor<'de> for CowVisitor {
291 type Value = Cow<'de, str>;
292
293 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
294 formatter.write_str("a string")
295 }
296
297 fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
298 where
299 E: serde::de::Error,
300 {
301 Ok(Cow::Borrowed(value))
302 }
303
304 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
305 where
306 E: serde::de::Error,
307 {
308 Ok(Cow::Owned(value.to_owned()))
309 }
310
311 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
312 where
313 E: serde::de::Error,
314 {
315 Ok(Cow::Owned(value))
316 }
317}
318
319#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
320#[serde(transparent)]
321pub struct EditableFieldSubdivisionCode(pub String);
322impl EditableFieldType for EditableFieldSubdivisionCode {
323 fn field_type(&self) -> FieldType {
324 FieldType::SubdivisionCode
325 }
326}
327
328impl<E> From<String> for EditableField<EditableFieldSubdivisionCode, E> {
329 fn from(s: String) -> Self {
330 EditableFieldSubdivisionCode(s).into()
331 }
332}
333
334impl<E> From<EditableField<EditableFieldSubdivisionCode, E>> for String {
335 fn from(s: EditableField<EditableFieldSubdivisionCode, E>) -> Self {
336 s.value.0
337 }
338}
339
340#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
341#[serde(transparent)]
342pub struct EditableFieldCountryCode(pub String);
343impl EditableFieldType for EditableFieldCountryCode {
344 fn field_type(&self) -> FieldType {
345 FieldType::CountryCode
346 }
347}
348
349impl<E> From<String> for EditableField<EditableFieldCountryCode, E> {
350 fn from(s: String) -> Self {
351 EditableFieldCountryCode(s).into()
352 }
353}
354
355impl<E> From<EditableField<EditableFieldCountryCode, E>> for String {
356 fn from(s: EditableField<EditableFieldCountryCode, E>) -> Self {
357 s.value.0
358 }
359}
360
361#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
362#[serde(rename_all = "kebab-case")]
363#[non_exhaustive]
364pub enum EditableFieldWifiNetworkSecurityType {
365 Unsecured,
366 WpaPersonal,
367 Wpa2Personal,
368 Wpa3Personal,
369 Wep,
370
371 #[serde(untagged)]
372 Other(String),
373}
374impl EditableFieldType for EditableFieldWifiNetworkSecurityType {
375 fn field_type(&self) -> FieldType {
376 FieldType::WifiNetworkSecurityType
377 }
378}
379
380#[derive(Clone, Debug, Serialize, Deserialize)]
382#[serde(untagged, bound(deserialize = "E: Deserialize<'de>"))]
383#[non_exhaustive]
384pub enum EditableFieldValue<E = ()> {
385 String(EditableField<EditableFieldString, E>),
386 ConcealedString(EditableField<EditableFieldConcealedString, E>),
387 Boolean(EditableField<EditableFieldBoolean, E>),
388 Date(EditableField<EditableFieldDate, E>),
389 YearMonth(EditableField<EditableFieldYearMonth, E>),
390 SubdivisionCode(EditableField<EditableFieldSubdivisionCode, E>),
391 CountryCode(EditableField<EditableFieldCountryCode, E>),
392 WifiNetworkSecurityType(EditableField<EditableFieldWifiNetworkSecurityType, E>),
393}
394
395mod serde_bool {
396 use serde::Deserialize;
397
398 pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
399 where
400 S: serde::Serializer,
401 {
402 serializer.serialize_str(&value.to_string())
403 }
404
405 pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
406 where
407 D: serde::Deserializer<'de>,
408 {
409 let value = <&str>::deserialize(deserializer)?;
410
411 value
412 .trim()
413 .to_lowercase()
414 .parse()
415 .map_err(serde::de::Error::custom)
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use serde_json::json;
422
423 use super::*;
424
425 #[test]
426 fn test_serialize_editable_field_string() {
427 let field: EditableField<EditableFieldString> = EditableField {
428 id: None,
429 value: EditableFieldString("value".to_string()),
430 label: Some("label".to_string()),
431 extensions: None,
432 };
433 let json = json!({
434 "value": "value",
435 "fieldType": "string",
436 "label": "label",
437 });
438 assert_eq!(serde_json::to_value(&field).unwrap(), json);
439 }
440
441 #[test]
442 fn test_deserialize_field_string() {
443 let json = json!({
444 "value": "value",
445 "fieldType": "string",
446 "label": "label",
447 });
448 let field: EditableField<EditableFieldString> = serde_json::from_value(json).unwrap();
449
450 assert_eq!(
451 field,
452 EditableField {
453 id: None,
454 value: EditableFieldString("value".to_string()),
455 label: Some("label".to_string()),
456 extensions: None,
457 }
458 );
459 }
460
461 #[test]
462 fn test_serialize_field_concealed_string() {
463 let field: EditableField<EditableFieldConcealedString> = EditableField {
464 id: None,
465 value: EditableFieldConcealedString("value".to_string()),
466 label: Some("label".to_string()),
467 extensions: None,
468 };
469 let json = json!({
470 "fieldType": "concealed-string",
471 "value": "value",
472 "label": "label",
473 });
474 assert_eq!(serde_json::to_value(&field).unwrap(), json);
475 }
476
477 #[test]
478 fn test_deserialize_field_wrong_type() {
479 let json = json!({
480 "value": "value",
481 "fieldType": "string",
482 "label": "label",
483 });
484 let field: Result<EditableField<EditableFieldConcealedString>, _> =
485 serde_json::from_value(json);
486
487 assert!(field.is_err());
488 }
489
490 #[test]
491 fn test_deserialize_field_bad_value_string() {
492 let json = json!({
493 "value": 5,
494 "fieldType": "string",
495 "label": "label",
496 });
497 let field: Result<EditableField<EditableFieldString>, _> = serde_json::from_value(json);
498
499 assert!(field.is_err());
500 }
501
502 #[test]
503 fn test_deserialize_field_bad_value_bool() {
504 let json = json!({
505 "value": "bad",
506 "fieldType": "bool",
507 "label": "label",
508 });
509 let field: Result<EditableField<EditableFieldBoolean>, _> = serde_json::from_value(json);
510
511 assert!(field.is_err());
512 }
513
514 #[test]
515 fn test_deserialize_field_missing_type() {
516 let json = json!({
517 "value": "value",
518 "label": "label",
519 });
520 let field: Result<EditableField<EditableFieldConcealedString>, _> =
521 serde_json::from_value(json);
522
523 assert!(field.is_err());
524 }
525
526 #[test]
527 fn test_deserialize_field_concealed_string() {
528 let json = json!({
529 "value": "value",
530 "fieldType": "concealed-string",
531 "label": "label",
532 });
533 let field: EditableField<EditableFieldConcealedString> =
534 serde_json::from_value(json).unwrap();
535
536 assert_eq!(
537 field,
538 EditableField {
539 id: None,
540 value: EditableFieldConcealedString("value".to_string()),
541 label: Some("label".to_string()),
542 extensions: None,
543 }
544 );
545 }
546
547 #[test]
548 fn test_serialize_field_boolean() {
549 let field: EditableField<EditableFieldBoolean> = EditableField {
550 id: None,
551 value: EditableFieldBoolean(true),
552 label: Some("label".to_string()),
553 extensions: None,
554 };
555 let json = json!({
556 "fieldType": "boolean",
557 "value": "true",
558 "label": "label",
559 });
560 assert_eq!(serde_json::to_value(&field).unwrap(), json);
561 }
562
563 #[test]
564 fn test_serialize_field_date() {
565 let field: EditableField<EditableFieldDate> = EditableField {
566 id: None,
567 value: EditableFieldDate(NaiveDate::from_ymd_opt(2025, 2, 24).unwrap()),
568 label: None,
569 extensions: None,
570 };
571 let json = json!({
572 "fieldType": "date",
573 "value": "2025-02-24",
574 });
575 assert_eq!(serde_json::to_value(&field).unwrap(), json);
576 }
577
578 #[test]
579 fn test_serialize_editable_field_year_month() {
580 let field: EditableField<EditableFieldYearMonth> = EditableField {
581 id: None,
582 value: EditableFieldYearMonth {
583 year: 2025,
584 month: Month::February,
585 },
586 label: None,
587 extensions: None,
588 };
589 let json = json!({
590 "fieldType": "year-month",
591 "value": "2025-02",
592 });
593 assert_eq!(serde_json::to_value(&field).unwrap(), json);
594 }
595
596 #[test]
597 fn test_deserialize_editable_field_year_month() {
598 let json = json!({
599 "fieldType": "year-month",
600 "value": "2025-02",
601 });
602 let field: EditableField<EditableFieldYearMonth> = serde_json::from_value(json).unwrap();
603
604 assert_eq!(
605 field,
606 EditableField {
607 id: None,
608 value: EditableFieldYearMonth {
609 year: 2025,
610 month: Month::February,
611 },
612 label: None,
613 extensions: None,
614 }
615 );
616 }
617
618 #[test]
619 fn test_deserialize_editable_field_year_month_invalid_format() {
620 let json = json!({
621 "fieldType": "year-month",
622 "value": "2025/02",
623 });
624 let field: Result<EditableField<EditableFieldYearMonth>, _> = serde_json::from_value(json);
625 assert!(field.is_err());
626 }
627
628 #[test]
629 fn test_extension_round_trip() {
630 let json = json!({
631 "fieldType": "string",
632 "value": "hello",
633 "extensions": [
634 {
635 "name": "test",
636 "contents": "world"
637 }
638 ]
639 });
640 #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
641 #[serde(tag = "name", rename_all = "camelCase")]
642 enum CustomExtension {
643 Test { contents: String },
644 }
645
646 let field: EditableField<EditableFieldString, CustomExtension> =
647 serde_json::from_value(json.clone()).expect("Could not deserialize custom extensions");
648
649 assert_eq!(
650 field,
651 EditableField {
652 id: None,
653 value: EditableFieldString("hello".to_string()),
654 label: None,
655 extensions: Some(vec![Extension::External(CustomExtension::Test {
656 contents: "world".into()
657 })])
658 }
659 );
660
661 let returned = serde_json::to_value(field).expect("Could not serialize custom extensions");
662
663 assert_eq!(returned, json);
664 }
665}