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