1#![no_std]
2
3extern crate alloc;
4
5pub mod codec;
6mod hash;
7mod interface_id;
8
9use alloc::{
10 boxed::Box,
11 format,
12 string::{String, ToString as _},
13 vec,
14 vec::Vec,
15};
16use core::{
17 fmt::{Display, Write},
18 str::FromStr,
19};
20pub use interface_id::InterfaceId;
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24pub type Annotation = (String, Option<String>);
26
27#[derive(Debug, Default, Clone, PartialEq)]
34#[cfg_attr(
35 feature = "templates",
36 derive(askama::Template),
37 template(path = "idl.askama", escape = "none")
38)]
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40pub struct IdlDoc {
41 #[cfg_attr(
42 feature = "serde",
43 serde(default, skip_serializing_if = "Vec::is_empty")
44 )]
45 pub globals: Vec<Annotation>,
46 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
47 pub program: Option<ProgramUnit>,
48 #[cfg_attr(
49 feature = "serde",
50 serde(default, skip_serializing_if = "Vec::is_empty")
51 )]
52 pub services: Vec<ServiceUnit>,
53}
54
55impl IdlDoc {
56 #[cfg(feature = "serde")]
57 pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
58 serde_json::to_string(self)
59 }
60
61 #[cfg(feature = "serde")]
62 pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
63 serde_json::to_string_pretty(self)
64 }
65}
66
67#[derive(Debug, Default, Clone, PartialEq)]
77#[cfg_attr(
78 feature = "templates",
79 derive(askama::Template),
80 template(path = "program.askama", escape = "none")
81)]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83pub struct ProgramUnit {
84 pub name: String,
85 #[cfg_attr(
86 feature = "serde",
87 serde(default, skip_serializing_if = "Vec::is_empty")
88 )]
89 pub ctors: Vec<CtorFunc>,
90 #[cfg_attr(
91 feature = "serde",
92 serde(default, skip_serializing_if = "Vec::is_empty")
93 )]
94 pub services: Vec<ServiceExpo>,
95 #[cfg_attr(
96 feature = "serde",
97 serde(default, skip_serializing_if = "Vec::is_empty")
98 )]
99 pub types: Vec<Type>,
100 #[cfg_attr(
101 feature = "serde",
102 serde(default, skip_serializing_if = "Vec::is_empty")
103 )]
104 pub docs: Vec<String>,
105 #[cfg_attr(
106 feature = "serde",
107 serde(default, skip_serializing_if = "Vec::is_empty")
108 )]
109 pub annotations: Vec<Annotation>,
110}
111
112impl ProgramUnit {
113 pub fn normalize(&mut self) {
117 for (idx, ctor) in self.ctors.iter_mut().enumerate() {
118 ctor.entry_id = entry_id_from_annotations(&ctor.annotations, idx as u16);
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125pub struct ServiceIdent {
126 pub name: String,
127 #[cfg_attr(
128 feature = "serde",
129 serde(
130 default,
131 skip_serializing_if = "Option::is_none",
132 with = "serde_opt_str"
133 )
134 )]
135 pub interface_id: Option<InterfaceId>,
136}
137
138impl Display for ServiceIdent {
139 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140 f.write_str(&self.name)?;
141 if let Some(id) = self.interface_id {
142 f.write_str("@")?;
143 id.fmt(f)?;
144 }
145 Ok(())
146 }
147}
148
149impl FromStr for ServiceIdent {
150 type Err = String;
151
152 fn from_str(s: &str) -> Result<Self, Self::Err> {
153 let (name, interface_id) = match s.split_once('@') {
154 None => (s.trim().to_string(), None),
155 Some((name, id_str)) => {
156 if name.is_empty() {
157 return Err("name is empty".to_string());
158 }
159 if id_str.is_empty() {
160 return Err("interface_id is empty".to_string());
161 }
162
163 let id = id_str.trim().parse::<InterfaceId>()?;
165 (name.trim().to_string(), Some(id))
166 }
167 };
168 Ok(ServiceIdent { name, interface_id })
169 }
170}
171
172#[derive(Debug, Clone, PartialEq)]
179#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
180pub struct ServiceExpo {
181 #[cfg_attr(feature = "serde", serde(flatten))]
182 pub name: ServiceIdent,
183 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
184 pub route: Option<String>,
185 pub route_idx: u8,
186 #[cfg_attr(
187 feature = "serde",
188 serde(default, skip_serializing_if = "Vec::is_empty")
189 )]
190 pub docs: Vec<String>,
191 #[cfg_attr(
192 feature = "serde",
193 serde(default, skip_serializing_if = "Vec::is_empty")
194 )]
195 pub annotations: Vec<Annotation>,
196}
197
198#[derive(Debug, Clone, PartialEq)]
206#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
207pub struct CtorFunc {
208 pub name: String,
209 #[cfg_attr(feature = "serde", serde(default))]
210 pub params: Vec<FuncParam>,
211 #[cfg_attr(
212 feature = "serde",
213 serde(default, skip_serializing_if = "Option::is_none")
214 )]
215 pub throws: Option<TypeDecl>,
216 #[cfg_attr(feature = "serde", serde(default))]
217 pub entry_id: u16,
218 #[cfg_attr(
219 feature = "serde",
220 serde(default, skip_serializing_if = "Vec::is_empty")
221 )]
222 pub docs: Vec<String>,
223 #[cfg_attr(
224 feature = "serde",
225 serde(default, skip_serializing_if = "Vec::is_empty")
226 )]
227 pub annotations: Vec<Annotation>,
228}
229
230#[derive(Debug, Clone, PartialEq)]
239#[cfg_attr(
240 feature = "templates",
241 derive(askama::Template),
242 template(path = "service.askama", escape = "none")
243)]
244#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245pub struct ServiceUnit {
246 #[cfg_attr(feature = "serde", serde(flatten))]
247 pub name: ServiceIdent,
248 #[cfg_attr(
249 feature = "serde",
250 serde(default, skip_serializing_if = "Vec::is_empty")
251 )]
252 pub extends: Vec<ServiceIdent>,
253 #[cfg_attr(
254 feature = "serde",
255 serde(default, skip_serializing_if = "Vec::is_empty")
256 )]
257 pub funcs: Vec<ServiceFunc>,
258 #[cfg_attr(
259 feature = "serde",
260 serde(default, skip_serializing_if = "Vec::is_empty")
261 )]
262 pub events: Vec<ServiceEvent>,
263 #[cfg_attr(
264 feature = "serde",
265 serde(default, skip_serializing_if = "Vec::is_empty")
266 )]
267 pub types: Vec<Type>,
268 #[cfg_attr(
269 feature = "serde",
270 serde(default, skip_serializing_if = "Vec::is_empty")
271 )]
272 pub docs: Vec<String>,
273 #[cfg_attr(
274 feature = "serde",
275 serde(default, skip_serializing_if = "Vec::is_empty")
276 )]
277 pub annotations: Vec<Annotation>,
278}
279
280impl ServiceUnit {
281 pub fn normalize(&mut self) {
286 self.events.sort_by_key(|e| e.name.to_lowercase());
287 self.funcs.sort_by_key(|f| f.name.to_lowercase());
288 self.extends.sort_by_key(|e| e.name.to_lowercase());
289 for (idx, func) in self.funcs.iter_mut().enumerate() {
292 func.entry_id = entry_id_from_annotations(&func.annotations, idx as u16);
293 }
294 for (idx, event) in self.events.iter_mut().enumerate() {
295 event.entry_id = entry_id_from_annotations(&event.annotations, idx as u16);
296 }
297 }
298
299 pub fn is_partial(&self) -> bool {
304 self.annotations.iter().any(|(k, _)| k == "partial")
305 }
306}
307
308fn entry_id_from_annotations(annotations: &[(String, Option<String>)], fallback: u16) -> u16 {
309 annotations
310 .iter()
311 .find(|(k, _)| k == "entry_id")
312 .and_then(|(_, v)| v.as_ref()?.parse::<u16>().ok())
313 .unwrap_or(fallback)
314}
315
316#[derive(Debug, Clone, PartialEq)]
325#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
326pub struct ServiceFunc {
327 pub name: String,
328 #[cfg_attr(feature = "serde", serde(default))]
329 pub params: Vec<FuncParam>,
330 pub output: TypeDecl,
331 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
332 pub throws: Option<TypeDecl>,
333 pub kind: FunctionKind,
334 #[cfg_attr(feature = "serde", serde(default))]
335 pub entry_id: u16,
336 #[cfg_attr(
337 feature = "serde",
338 serde(default, skip_serializing_if = "Vec::is_empty")
339 )]
340 pub docs: Vec<String>,
341 #[cfg_attr(
342 feature = "serde",
343 serde(default, skip_serializing_if = "Vec::is_empty")
344 )]
345 pub annotations: Vec<Annotation>,
346}
347
348#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
350#[cfg_attr(
351 feature = "serde",
352 derive(Serialize, Deserialize),
353 serde(rename_all = "lowercase")
354)]
355pub enum FunctionKind {
356 #[default]
357 Command,
358 Query,
359}
360
361impl ServiceFunc {
362 pub fn returns_void(&self) -> bool {
365 use PrimitiveType::*;
366 use TypeDecl::*;
367 self.output == Primitive(Void)
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
376#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
377pub struct FuncParam {
378 pub name: String,
379 #[cfg_attr(feature = "serde", serde(rename = "type"))]
380 pub type_decl: TypeDecl,
381}
382
383impl Display for FuncParam {
384 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
385 let FuncParam { name, type_decl } = self;
386 write!(f, "{name}: {type_decl}")
387 }
388}
389
390pub type ServiceEvent = EnumVariant;
395
396#[derive(Debug, Clone, PartialEq, Eq, Hash)]
407#[cfg_attr(
408 feature = "serde",
409 derive(Serialize, Deserialize),
410 serde(rename_all = "lowercase", tag = "kind")
411)]
412pub enum TypeDecl {
413 Slice { item: Box<TypeDecl> },
415 Array { item: Box<TypeDecl>, len: u32 },
417 Tuple { types: Vec<TypeDecl> },
419 Generic { name: String },
421 Named {
423 name: String,
424 #[cfg_attr(
425 feature = "serde",
426 serde(default, skip_serializing_if = "Vec::is_empty")
427 )]
428 generics: Vec<TypeDecl>,
429 },
430 #[cfg_attr(feature = "serde", serde(untagged))]
432 Primitive(#[cfg_attr(feature = "serde", serde(with = "serde_str"))] PrimitiveType),
433}
434
435impl TypeDecl {
436 pub fn named(name: impl Into<String>) -> TypeDecl {
437 Self::named_with_generics(name, vec![])
438 }
439
440 pub fn named_with_generics(name: impl Into<String>, generics: Vec<TypeDecl>) -> TypeDecl {
441 TypeDecl::Named {
442 name: name.into(),
443 generics,
444 }
445 }
446
447 pub fn generic(name: impl Into<String>) -> TypeDecl {
448 TypeDecl::Generic { name: name.into() }
449 }
450
451 pub fn tuple(types: Vec<TypeDecl>) -> TypeDecl {
452 TypeDecl::Tuple { types }
453 }
454
455 pub fn option(item: TypeDecl) -> TypeDecl {
456 TypeDecl::Named {
457 name: "Option".to_string(),
458 generics: vec![item],
459 }
460 }
461
462 pub fn result(ok: TypeDecl, err: TypeDecl) -> TypeDecl {
463 TypeDecl::Named {
464 name: "Result".to_string(),
465 generics: vec![ok, err],
466 }
467 }
468
469 pub fn option_type_decl(ty: &TypeDecl) -> Option<TypeDecl> {
470 match ty {
471 TypeDecl::Named { name, generics } if name == "Option" => {
472 if let [item] = generics.as_slice() {
473 Some(item.clone())
474 } else {
475 None
476 }
477 }
478 _ => None,
479 }
480 }
481
482 pub fn result_type_decl(ty: &TypeDecl) -> Option<(TypeDecl, TypeDecl)> {
483 match ty {
484 TypeDecl::Named { name, generics } if name == "Result" => {
485 if let [ok, err] = generics.as_slice() {
486 Some((ok.clone(), err.clone()))
487 } else {
488 None
489 }
490 }
491 _ => None,
492 }
493 }
494
495 #[cfg(feature = "serde")]
496 pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
497 serde_json::to_string(self)
498 }
499
500 #[cfg(feature = "serde")]
501 pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
502 serde_json::to_string_pretty(self)
503 }
504}
505
506impl Display for TypeDecl {
507 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
508 use TypeDecl::*;
509 match self {
510 Slice { item } => write!(f, "[{item}]"),
511 Array { item, len } => write!(f, "[{item}; {len}]"),
512 Tuple { types } => {
513 f.write_char('(')?;
514 for (i, ty) in types.iter().enumerate() {
515 if i > 0 {
516 f.write_str(", ")?;
517 }
518 write!(f, "{ty}")?;
519 }
520 f.write_char(')')?;
521 Ok(())
522 }
523 Generic { name } => f.write_str(name),
524 Named { name, generics } => {
525 write!(f, "{name}")?;
526 if !generics.is_empty() {
527 f.write_char('<')?;
528 for (i, g) in generics.iter().enumerate() {
529 if i > 0 {
530 f.write_str(", ")?;
531 }
532 write!(f, "{g}")?;
533 }
534 f.write_char('>')?;
535 }
536 Ok(())
537 }
538 Primitive(primitive_type) => write!(f, "{primitive_type}"),
539 }
540 }
541}
542
543#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
549#[repr(u8)]
550pub enum PrimitiveType {
551 Void,
553 Bool,
554 Char,
555 String,
556 U8,
557 U16,
558 U32,
559 U64,
560 U128,
561 I8,
562 I16,
563 I32,
564 I64,
565 I128,
566 ActorId,
568 CodeId,
570 MessageId,
572 H160,
574 H256,
576 U256,
578}
579
580impl PrimitiveType {
581 pub const fn as_str(&self) -> &'static str {
583 use PrimitiveType::*;
584 match self {
585 Void => "()",
586 Bool => "bool",
587 Char => "char",
588 String => "String",
589 U8 => "u8",
590 U16 => "u16",
591 U32 => "u32",
592 U64 => "u64",
593 U128 => "u128",
594 I8 => "i8",
595 I16 => "i16",
596 I32 => "i32",
597 I64 => "i64",
598 I128 => "i128",
599 ActorId => "ActorId",
600 CodeId => "CodeId",
601 MessageId => "MessageId",
602 H160 => "H160",
603 H256 => "H256",
604 U256 => "U256",
605 }
606 }
607}
608
609impl Display for PrimitiveType {
610 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
611 f.write_str(self.as_str())
612 }
613}
614
615impl core::str::FromStr for PrimitiveType {
616 type Err = String;
617
618 fn from_str(s: &str) -> Result<Self, Self::Err> {
623 use PrimitiveType::*;
624 match s {
625 "()" => Ok(Void),
626 "bool" => Ok(Bool),
627 "char" => Ok(Char),
628 "String" | "string" => Ok(String),
629 "u8" => Ok(U8),
630 "u16" => Ok(U16),
631 "u32" => Ok(U32),
632 "u64" => Ok(U64),
633 "u128" => Ok(U128),
634 "i8" => Ok(I8),
635 "i16" => Ok(I16),
636 "i32" => Ok(I32),
637 "i64" => Ok(I64),
638 "i128" => Ok(I128),
639
640 "ActorId" | "actor" | "actor_id" => Ok(ActorId),
641 "CodeId" | "code" | "code_id" => Ok(CodeId),
642 "MessageId" | "messageid" | "message_id" => Ok(MessageId),
643
644 "H256" | "h256" => Ok(H256),
645 "U256" | "u256" => Ok(U256),
646 "H160" | "h160" => Ok(H160),
647
648 other => Err(format!("Unknown primitive type: {other}")),
649 }
650 }
651}
652
653#[derive(Debug, Clone, PartialEq)]
658#[cfg_attr(
659 feature = "templates",
660 derive(askama::Template),
661 template(path = "type.askama", escape = "none")
662)]
663#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
664pub struct Type {
665 pub name: String,
666 #[cfg_attr(
667 feature = "serde",
668 serde(default, skip_serializing_if = "Vec::is_empty")
669 )]
670 pub type_params: Vec<TypeParameter>,
671 #[cfg_attr(feature = "serde", serde(flatten))]
672 pub def: TypeDef,
673 #[cfg_attr(
674 feature = "serde",
675 serde(default, skip_serializing_if = "Vec::is_empty")
676 )]
677 pub docs: Vec<String>,
678 #[cfg_attr(
679 feature = "serde",
680 serde(default, skip_serializing_if = "Vec::is_empty")
681 )]
682 pub annotations: Vec<Annotation>,
683}
684
685impl Type {
686 #[cfg(feature = "serde")]
687 pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
688 serde_json::to_string(self)
689 }
690
691 #[cfg(feature = "serde")]
692 pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
693 serde_json::to_string_pretty(self)
694 }
695}
696
697#[derive(Debug, PartialEq, Eq, Hash, Clone)]
703#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
704pub struct TypeParameter {
705 pub name: String,
707 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
711 pub ty: Option<TypeDecl>,
712}
713
714impl Display for TypeParameter {
715 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
716 let TypeParameter { name, ty: _ } = self;
717 write!(f, "{name}")
718 }
719}
720
721#[derive(Debug, Clone, PartialEq)]
727#[cfg_attr(
728 feature = "serde",
729 derive(Serialize, Deserialize),
730 serde(rename_all = "lowercase", tag = "kind")
731)]
732pub enum TypeDef {
733 Struct(StructDef),
734 Enum(EnumDef),
735 Alias(AliasDef),
736}
737
738#[derive(Debug, Clone, PartialEq)]
745#[cfg_attr(
746 feature = "templates",
747 derive(askama::Template),
748 template(path = "struct_def.askama", escape = "none")
749)]
750#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
751pub struct StructDef {
752 #[cfg_attr(feature = "serde", serde(default))]
753 pub fields: Vec<StructField>,
754}
755
756impl StructDef {
757 pub fn is_unit(&self) -> bool {
759 self.fields.is_empty()
760 }
761
762 pub fn is_inline(&self) -> bool {
765 self.fields
766 .iter()
767 .all(|f| f.name.is_none() && f.docs.is_empty() && f.annotations.is_empty())
768 }
769
770 pub fn is_tuple(&self) -> bool {
772 self.fields.iter().all(|f| f.name.is_none())
773 }
774
775 #[cfg(feature = "serde")]
776 pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
777 serde_json::to_string(self)
778 }
779
780 #[cfg(feature = "serde")]
781 pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
782 serde_json::to_string_pretty(self)
783 }
784}
785
786#[derive(Debug, Clone, PartialEq)]
791#[cfg_attr(
792 feature = "templates",
793 derive(askama::Template),
794 template(path = "field.askama", escape = "none")
795)]
796#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
797pub struct StructField {
798 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
799 pub name: Option<String>,
800 #[cfg_attr(feature = "serde", serde(rename = "type"))]
801 pub type_decl: TypeDecl,
802 #[cfg_attr(
803 feature = "serde",
804 serde(default, skip_serializing_if = "Vec::is_empty")
805 )]
806 pub docs: Vec<String>,
807 #[cfg_attr(
808 feature = "serde",
809 serde(default, skip_serializing_if = "Vec::is_empty")
810 )]
811 pub annotations: Vec<Annotation>,
812}
813
814#[derive(Debug, Clone, PartialEq)]
819#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
820pub struct EnumDef {
821 #[cfg_attr(feature = "serde", serde(default))]
822 pub variants: Vec<EnumVariant>,
823}
824
825#[derive(Debug, Clone, PartialEq)]
833#[cfg_attr(
834 feature = "templates",
835 derive(askama::Template),
836 template(path = "variant.askama", escape = "none")
837)]
838#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
839pub struct EnumVariant {
840 pub name: String,
841 #[cfg_attr(feature = "serde", serde(flatten))]
842 pub def: StructDef,
843 #[cfg_attr(feature = "serde", serde(default))]
844 pub entry_id: u16,
845 #[cfg_attr(
846 feature = "serde",
847 serde(default, skip_serializing_if = "Vec::is_empty")
848 )]
849 pub docs: Vec<String>,
850 #[cfg_attr(
851 feature = "serde",
852 serde(default, skip_serializing_if = "Vec::is_empty")
853 )]
854 pub annotations: Vec<Annotation>,
855}
856
857#[derive(Debug, Clone, PartialEq)]
859#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
860pub struct AliasDef {
861 pub target: TypeDecl,
862}
863
864#[cfg(feature = "serde")]
865mod serde_str {
866 use super::*;
867 use core::str::FromStr;
868 use serde::{Deserializer, Serializer};
869
870 pub(super) fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
871 where
872 T: Display,
873 S: Serializer,
874 {
875 serializer.collect_str(value)
876 }
877
878 pub(super) fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
879 where
880 T: FromStr,
881 <T as FromStr>::Err: Display,
882 D: Deserializer<'de>,
883 {
884 let s = String::deserialize(deserializer)?;
885 T::from_str(s.as_str()).map_err(serde::de::Error::custom)
886 }
887}
888
889#[cfg(feature = "serde")]
890mod serde_opt_str {
891 use super::*;
892 use core::str::FromStr;
893 use serde::{Deserializer, Serializer};
894
895 pub(super) fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
896 where
897 T: Display,
898 S: Serializer,
899 {
900 match value {
901 Some(value) => serializer.collect_str(value),
902 None => serializer.serialize_none(),
903 }
904 }
905
906 pub(super) fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
907 where
908 T: FromStr,
909 <T as FromStr>::Err: Display,
910 D: Deserializer<'de>,
911 {
912 let opt = Option::<String>::deserialize(deserializer)?;
913 opt.map(|s| T::from_str(s.as_str()).map_err(serde::de::Error::custom))
914 .transpose()
915 }
916}