stringly_conversions/
lib.rs1#![no_std]
21
22#[cfg(feature = "alloc")]
23pub extern crate alloc;
24
25pub extern crate paste;
26
27#[cfg(feature = "serde_str_helpers")]
29pub extern crate serde_str_helpers;
30
31#[macro_export]
43macro_rules! impl_try_from_stringly {
44 ($to:ty $(, $from:ty)+ $(,)?) => {
45 $(
46 impl ::core::convert::TryFrom<$from> for $to {
47 type Error = <$to as ::core::str::FromStr>::Err;
48 #[inline]
49 fn try_from(value: $from) -> Result<Self, Self::Error> {
50 <$to as core::str::FromStr>::from_str(&value)
51 }
52 }
53 )*
54 };
55
56 (@std, $to:ty $(, $from:ty)+ $(,)?) => {
57 $(
58 #[cfg(feature = "std")]
59 impl std::convert::TryFrom<$from> for $to {
60 type Error = <$to as ::core::str::FromStr>::Err;
61 #[inline]
62 fn try_from(value: $from) -> Result<Self, Self::Error> {
63 <$to>::from_str(&value)
64 }
65 }
66 )*
67 }
68}
69
70#[macro_export]
94macro_rules! impl_try_from_stringly_standard {
95 ($type:ty) => {
96 $crate::impl_try_from_stringly_standard!($type, $type);
97 };
98 ($type:ty, $module_suffix:ty) => {
99 $crate::paste::paste! {
100 #[allow(non_snake_case)]
101 mod [<__try_from_stringly_standard_ $module_suffix >] {
102 use super::*;
103 #[cfg(feature = "alloc")]
104 use alloc::string::String;
105 #[cfg(feature = "alloc")]
106 use alloc::boxed::Box;
107 #[cfg(feature = "alloc")]
108 use alloc::borrow::Cow;
109 #[cfg(feature = "alloc")]
110 use alloc::rc::Rc;
111 #[cfg(feature = "alloc")]
112 use alloc::sync::Arc;
113
114 impl_try_from_stringly! { $type,
115 &str,
116 }
117
118 #[cfg(feature = "alloc")]
119 impl_try_from_stringly! { $type,
120 String,
121 Cow<'_, str>,
122 Box<str>,
123 Box<Cow<'_, str>>,
124 Rc<str>,
125 Rc<String>,
126 Rc<Cow<'_, str>>,
127 Arc<str>,
128 Arc<String>,
129 Arc<Cow<'_, str>>,
130 }
131
132 #[cfg(feature = "serde_str_helpers")]
133 impl_try_from_stringly!($type, $crate::serde_str_helpers::DeserBorrowStr<'_>);
134 }
135 }
136 };
137}
138
139#[macro_export]
141macro_rules! impl_into_stringly {
142 ($from:ty $(, $into:ty)+ $(,)?) => {
143 $(
144 impl From<$from> for $into {
145 fn from(value: $from) -> Self {
146 $crate::alloc::string::ToString::to_string(&value).into()
147 }
148 }
149 )+
150 }
151}
152
153#[macro_export]
173macro_rules! impl_into_stringly_standard {
174 ($type:ty) => {
175 $crate::impl_into_stringly_standard!($type, $type);
176 };
177 ($type:ty, $module_suffix:ty) => {
178 $crate::paste::paste! {
179 #[allow(non_snake_case)]
180 mod [< __into_stringly_standard_ $type >] {
181 #[allow(unused)]
182 use super::*;
183 #[cfg(feature = "alloc")]
184 use alloc::borrow::Cow;
185 #[cfg(feature = "alloc")]
186 use alloc::rc::Rc;
187 #[cfg(feature = "alloc")]
188 use alloc::sync::Arc;
189
190 #[cfg(feature = "alloc")]
191 impl_into_stringly! { $type,
192 String,
193 Cow<'_, str>,
194 Box<str>,
195 Rc<str>,
196 Rc<String>,
197 Arc<str>,
198 Arc<String>,
199 }
200 }
201 }
202 };
203}
204
205#[cfg(test)]
206mod tests {
207 use core::convert::TryFrom;
208 use core::fmt;
209
210 #[cfg(feature = "alloc")]
211 use alloc::borrow::Cow;
212 #[cfg(feature = "alloc")]
213 use alloc::boxed::Box;
214 #[cfg(feature = "alloc")]
215 use alloc::rc::Rc;
216 #[cfg(feature = "alloc")]
217 use alloc::string::String;
218 #[cfg(feature = "alloc")]
219 use alloc::sync::Arc;
220
221 struct Number(u32);
222
223 impl_try_from_stringly_standard!(Number);
224 impl_into_stringly_standard!(Number);
225
226 impl core::str::FromStr for Number {
227 type Err = core::num::ParseIntError;
228
229 fn from_str(s: &str) -> Result<Self, Self::Err> {
230 s.parse().map(Number)
231 }
232 }
233
234 impl fmt::Display for Number {
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 write!(f, "{}", self.0)
237 }
238 }
239
240 struct Foo<T>(T);
242
243 impl_try_from_stringly_standard!(Foo<u32>, Foo__u32);
244
245 impl core::str::FromStr for Foo<u32> {
246 type Err = core::num::ParseIntError;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 s.parse().map(Foo)
250 }
251 }
252
253 #[test]
254 fn parse_str() {
255 assert_eq!(Number::try_from("42").unwrap().0, 42);
256 }
257
258 #[cfg(feature = "alloc")]
259 #[test]
260 fn parse_alloc() {
261 assert_eq!(Number::try_from(String::from("42")).unwrap().0, 42);
262 assert_eq!(Number::try_from(<Cow<'_, str>>::from("42")).unwrap().0, 42);
263 assert_eq!(Number::try_from(<Box<str>>::from("42")).unwrap().0, 42);
264 assert_eq!(Number::try_from(<Rc<str>>::from("42")).unwrap().0, 42);
265 assert_eq!(Number::try_from(Rc::new(String::from("42"))).unwrap().0, 42);
266 assert_eq!(Number::try_from(<Arc<str>>::from("42")).unwrap().0, 42);
267 assert_eq!(
268 Number::try_from(Arc::new(String::from("42"))).unwrap().0,
269 42
270 );
271 }
272
273 #[cfg(all(feature = "serde_str_helpers", feature = "alloc"))]
274 #[test]
275 fn test_serde_str_helpers() {
276 assert_eq!(
277 Number::try_from(serde_str_helpers::DeserBorrowStr::from(
278 <Cow<'_, str>>::from("42")
279 ))
280 .unwrap()
281 .0,
282 42
283 );
284 }
285
286 #[cfg(feature = "alloc")]
287 #[test]
288 fn display() {
289 assert_eq!(&*<String>::from(Number(42)), "42");
290 assert_eq!(&*<Cow<'_, str>>::from(Number(42)), "42");
291 assert_eq!(&*<Box<str>>::from(Number(42)), "42");
292 assert_eq!(&*<Rc<str>>::from(Number(42)), "42");
293 assert_eq!(&*<Rc<String>>::from(Number(42)), "42");
294 assert_eq!(&*<Arc<str>>::from(Number(42)), "42");
295 assert_eq!(&*<Arc<String>>::from(Number(42)), "42");
296 }
297}