#![no_std]
#[cfg(feature = "alloc")]
pub extern crate alloc;
pub extern crate paste;
#[cfg(feature = "serde_str_helpers")]
pub extern crate serde_str_helpers;
#[macro_export]
macro_rules! impl_try_from_stringly {
($to:ty $(, $from:ty)+ $(,)?) => {
$(
impl ::core::convert::TryFrom<$from> for $to {
type Error = <$to as ::core::str::FromStr>::Err;
#[inline]
fn try_from(value: $from) -> Result<Self, Self::Error> {
<$to as core::str::FromStr>::from_str(&value)
}
}
)*
};
(@std, $to:ty $(, $from:ty)+ $(,)?) => {
$(
#[cfg(feature = "std")]
impl std::convert::TryFrom<$from> for $to {
type Error = <$to as ::core::str::FromStr>::Err;
#[inline]
fn try_from(value: $from) -> Result<Self, Self::Error> {
<$to>::from_str(&value)
}
}
)*
}
}
#[macro_export]
macro_rules! impl_try_from_stringly_standard {
($type:ty) => {
$crate::impl_try_from_stringly_standard!($type, $type);
};
($type:ty, $module_suffix:ty) => {
$crate::paste::paste! {
#[allow(non_snake_case)]
mod [<__try_from_stringly_standard_ $module_suffix >] {
use super::*;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
impl_try_from_stringly! { $type,
&str,
}
#[cfg(feature = "alloc")]
impl_try_from_stringly! { $type,
String,
Cow<'_, str>,
Box<str>,
Box<Cow<'_, str>>,
Rc<str>,
Rc<String>,
Rc<Cow<'_, str>>,
Arc<str>,
Arc<String>,
Arc<Cow<'_, str>>,
}
#[cfg(feature = "serde_str_helpers")]
impl_try_from_stringly!($type, $crate::serde_str_helpers::DeserBorrowStr<'_>);
}
}
};
}
#[macro_export]
macro_rules! impl_into_stringly {
($from:ty $(, $into:ty)+ $(,)?) => {
$(
impl From<$from> for $into {
fn from(value: $from) -> Self {
$crate::alloc::string::ToString::to_string(&value).into()
}
}
)+
}
}
#[macro_export]
macro_rules! impl_into_stringly_standard {
($type:ty) => {
$crate::impl_into_stringly_standard!($type, $type);
};
($type:ty, $module_suffix:ty) => {
$crate::paste::paste! {
#[allow(non_snake_case)]
mod [< __into_stringly_standard_ $type >] {
#[allow(unused)]
use super::*;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
impl_into_stringly! { $type,
String,
Cow<'_, str>,
Box<str>,
Rc<str>,
Rc<String>,
Arc<str>,
Arc<String>,
}
}
}
};
}
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use core::fmt;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
struct Number(u32);
impl_try_from_stringly_standard!(Number);
impl_into_stringly_standard!(Number);
impl core::str::FromStr for Number {
type Err = core::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Number)
}
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
struct Foo<T>(T);
impl_try_from_stringly_standard!(Foo<u32>, Foo__u32);
impl core::str::FromStr for Foo<u32> {
type Err = core::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Foo)
}
}
#[test]
fn parse_str() {
assert_eq!(Number::try_from("42").unwrap().0, 42);
}
#[cfg(feature = "alloc")]
#[test]
fn parse_alloc() {
assert_eq!(Number::try_from(String::from("42")).unwrap().0, 42);
assert_eq!(Number::try_from(<Cow<'_, str>>::from("42")).unwrap().0, 42);
assert_eq!(Number::try_from(<Box<str>>::from("42")).unwrap().0, 42);
assert_eq!(Number::try_from(<Rc<str>>::from("42")).unwrap().0, 42);
assert_eq!(Number::try_from(Rc::new(String::from("42"))).unwrap().0, 42);
assert_eq!(Number::try_from(<Arc<str>>::from("42")).unwrap().0, 42);
assert_eq!(
Number::try_from(Arc::new(String::from("42"))).unwrap().0,
42
);
}
#[cfg(all(feature = "serde_str_helpers", feature = "alloc"))]
#[test]
fn test_serde_str_helpers() {
assert_eq!(
Number::try_from(serde_str_helpers::DeserBorrowStr::from(
<Cow<'_, str>>::from("42")
))
.unwrap()
.0,
42
);
}
#[cfg(feature = "alloc")]
#[test]
fn display() {
assert_eq!(&*<String>::from(Number(42)), "42");
assert_eq!(&*<Cow<'_, str>>::from(Number(42)), "42");
assert_eq!(&*<Box<str>>::from(Number(42)), "42");
assert_eq!(&*<Rc<str>>::from(Number(42)), "42");
assert_eq!(&*<Rc<String>>::from(Number(42)), "42");
assert_eq!(&*<Arc<str>>::from(Number(42)), "42");
assert_eq!(&*<Arc<String>>::from(Number(42)), "42");
}
}