1#[macro_export]
31macro_rules! forward_from_str {
32 (<$($generic:ident),+> in $this:ty => $inner:ty $(,)?) => {
33 impl <$($generic),+> ::core::str::FromStr for $this
34 where
35 $inner: ::core::str::FromStr,
36 {
37 type Err = <$inner as ::core::str::FromStr>::Err;
38
39 fn from_str(value: &str) -> ::core::result::Result<Self, Self::Err> {
40 <$inner as ::core::str::FromStr>::from_str(value).map(Self)
41 }
42 }
43 };
44
45 (<$($generic:ident),+> in $this:ty => $field:ident : $inner:ty $(,)?) => {
46 impl <$($generic),+> ::core::str::FromStr for $this
47 where
48 $inner: ::core::str::FromStr,
49 {
50 type Err = <$inner as ::core::str::FromStr>::Err;
51
52 fn from_str(value: &str) -> ::core::result::Result<Self, Self::Err> {
53 <$inner as ::core::str::FromStr>::from_str(value)
54 .map(|$field| Self { $field })
55 }
56 }
57 };
58
59 ($this:ty => $inner:ty $(,)?) => {
60 impl ::core::str::FromStr for $this {
61 type Err = <$inner as ::core::str::FromStr>::Err;
62
63 fn from_str(value: &str) -> ::core::result::Result<Self, Self::Err> {
64 <$inner as ::core::str::FromStr>::from_str(value).map(Self)
65 }
66 }
67 };
68
69 ($this:ty => $field:ident : $inner:ty $(,)?) => {
70 impl ::core::str::FromStr for $this {
71 type Err = <$inner as ::core::str::FromStr>::Err;
72
73 fn from_str(value: &str) -> ::core::result::Result<Self, Self::Err> {
74 <$inner as ::core::str::FromStr>::from_str(value)
75 .map(|$field| Self { $field })
76 }
77 }
78 };
79}
80
81#[cfg(test)]
82mod tests {
83 use core::{num::ParseIntError, str::FromStr as _};
84
85 #[derive(Debug, PartialEq)]
86 struct Newtype(u16);
87 forward_from_str!(Newtype => u16);
88
89 #[derive(Debug, PartialEq)]
90 struct Named {
91 inner: bool,
92 }
93 forward_from_str!(Named => inner: bool);
94
95 #[derive(Debug, PartialEq)]
96 struct Generic<T>(T);
97 forward_from_str!(<T> in Generic<T> => T);
98
99 #[derive(Debug, PartialEq)]
100 struct GenericNamed<T> {
101 inner: T,
102 }
103 forward_from_str!(<T> in GenericNamed<T> => inner: T);
104
105 static_assertions::assert_impl_all!(Newtype: core::str::FromStr<Err = ParseIntError>);
106 static_assertions::assert_impl_all!(Named: core::str::FromStr<Err = core::str::ParseBoolError>);
107 static_assertions::assert_impl_all!(Generic<u16>: core::str::FromStr<Err = ParseIntError>);
108 static_assertions::assert_impl_all!(
109 GenericNamed<bool>: core::str::FromStr<Err = core::str::ParseBoolError>
110 );
111
112 #[test]
113 fn forwards_newtype() {
114 assert_eq!(Newtype::from_str("8080"), Ok(Newtype(8080)));
115 assert!(Newtype::from_str("invalid").is_err());
116 }
117
118 #[test]
119 fn forwards_named_field() {
120 assert_eq!(Named::from_str("true"), Ok(Named { inner: true }));
121 assert!(Named::from_str("invalid").is_err());
122 }
123
124 #[test]
125 fn forwards_generic_newtype() {
126 assert_eq!(Generic::<u16>::from_str("8080"), Ok(Generic(8080)));
127 }
128
129 #[test]
130 fn forwards_generic_named_field() {
131 assert_eq!(
132 GenericNamed::<bool>::from_str("true"),
133 Ok(GenericNamed { inner: true })
134 );
135 }
136}