//! ASCII string in (pseudo) nostd and alloc environment. //! //! Types for strings which consists of only ASCII characters. use std as alloc; enum AsciiStrSpec {} impl validated_slice::SliceSpec for AsciiStrSpec { type Custom = AsciiStr; type Inner = str; type Error = AsciiError; fn validate(s: &Self::Inner) -> Result<(), Self::Error> { match s.as_bytes().iter().position(|b| !b.is_ascii()) { Some(pos) => Err(AsciiError { valid_up_to: pos }), None => Ok(()), } } validated_slice::impl_slice_spec_methods! { field=0; methods=[ as_inner, as_inner_mut, from_inner_unchecked, from_inner_unchecked_mut, ]; } } /// ASCII string validation error. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct AsciiError { /// Byte position of the first invalid byte. valid_up_to: usize, } /// ASCII string slice. // `#[repr(transparent)]` or `#[repr(C)]` is required. // Without it, generated codes would be unsound. // // You can use `#[derive(Debug, PartialEq, PartialOrd)]` here, but in this example they are // implemented by macros in `validated_slice`, or implemented manually. #[repr(transparent)] #[derive(Eq, Ord, Hash)] pub struct AsciiStr(str); impl core::fmt::Debug for AsciiStr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Ascii({:?})", &self.0) } } validated_slice::impl_std_traits_for_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiStrSpec, custom: AsciiStr, inner: str, error: AsciiError, }; // AsRef<[u8]> for AsciiStr { AsRef<[u8]> }; // AsRef for AsciiStr { AsRef }; // AsRef for AsciiStr { AsRef<{Custom}> }; // From<&'_ AsciiStr> for &'_ str { From<&{Custom}> for &{Inner} }; // From<&'_ AsciiStr> for Arc { From<&{Custom}> for Arc<{Custom}> }; // From<&'_ AsciiStr> for Box { From<&{Custom}> for Box<{Custom}> }; // From<&'_ AsciiStr> for Rc { From<&{Custom}> for Rc<{Custom}> }; // TryFrom<&'_ str> for &'_ AsciiStr { TryFrom<&{Inner}> for &{Custom} }; // TryFrom<&'_ mut str> for &'_ mut AsciiStr { TryFrom<&mut {Inner}> for &mut {Custom} }; // Default for &'_ AsciiStr { Default for &{Custom} }; // Default for &'_ mut AsciiStr { Default for &mut {Custom} }; // Display for AsciiStr { Display }; // Deref for Custom { Deref }; } validated_slice::impl_cmp_for_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiStrSpec, custom: AsciiStr, inner: str, base: Inner, }; Cmp { PartialEq, PartialOrd }; // This is same as `#[derive(PartialEq, PartialOrd)]`. { ({Custom}), ({Custom}) }; { ({Custom}), (&{Custom}), rev }; // NOTE: This requires `alloc::borrow::ToOwned for AsciiStr`. { ({Custom}), (Cow<{Custom}>), rev }; { ({Custom}), ({Inner}), rev }; { ({Custom}), (&{Inner}), rev }; { (&{Custom}), ({Inner}), rev }; { ({Custom}), (Cow<{Inner}>), rev }; { (&{Custom}), (Cow<{Inner}>), rev }; // NOTE: `{Inner}` should be local type to implement this. //{ ({Inner}), (Cow<{Custom}>), rev }; // NOTE: `{Inner}` should be local type to implement this. //{ (&{Inner}), (Cow<{Custom}>), rev }; } enum AsciiBoxStrSpec {} impl validated_slice::OwnedSliceSpec for AsciiBoxStrSpec { type Custom = AsciiBoxStr; type Inner = Box; type Error = AsciiError; type SliceSpec = AsciiStrSpec; type SliceCustom = AsciiStr; type SliceInner = str; type SliceError = AsciiError; #[inline] fn convert_validation_error(e: Self::SliceError, _: Self::Inner) -> Self::Error { e } #[inline] fn as_slice_inner(s: &Self::Custom) -> &Self::SliceInner { &s.0 } #[inline] fn as_slice_inner_mut(s: &mut Self::Custom) -> &mut Self::SliceInner { &mut s.0 } #[inline] fn inner_as_slice_inner(s: &Self::Inner) -> &Self::SliceInner { s } #[inline] unsafe fn from_inner_unchecked(s: Self::Inner) -> Self::Custom { AsciiBoxStr(s) } #[inline] fn into_inner(s: Self::Custom) -> Self::Inner { s.0 } } /// ASCII string boxed slice. #[derive(Default, Clone, Eq, Ord, Hash)] pub struct AsciiBoxStr(Box); impl From for AsciiBoxStr { fn from(s: AsciiString) -> Self { Self(s.0.into_boxed_str()) } } validated_slice::impl_std_traits_for_owned_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiBoxStrSpec, custom: AsciiBoxStr, inner: Box, error: AsciiError, slice_custom: AsciiStr, slice_inner: str, slice_error: AsciiError, }; // AsMut for AsciiBoxStr // NOTE: `AsMut<[u8]> for str` is not implemented. //{ AsMut }; // AsMut for AsciiBoxStr { AsMut<{SliceCustom}> }; // AsRef<[u8]> for AsciiBoxStr { AsRef<[u8]> }; // AsRef for AsciiBoxStr { AsRef }; // AsRef for AsciiBoxStr { AsRef<{SliceCustom}> }; // Borrow<[u8]> for AsciiBoxStr // NOTE: `Borrow<[u8]> for str` is not implemented. //{ Borrow<[u8]> }; // Borrow for AsciiBoxStr { Borrow }; // Borrow for AsciiBoxStr { Borrow<{SliceCustom}> }; // BorrowMut for AsciiBoxStr { BorrowMut<{SliceCustom}> }; // ToOwned for AsciiStr //{ ToOwned for {SliceCustom} }; // From<&'_ AsciiStr> for AsciiBoxStr { From<&{SliceCustom}> }; // From for Box { From<{Custom}> for {Inner} }; // TryFrom<&'_ str> for AsciiBoxStr { TryFrom<&{SliceInner}> }; // TryFrom> for AsciiBoxStr { TryFrom<{Inner}> }; // Default for AsciiBoxStr // NOTE: Same as `#[derive(Default)]` in this case. //{ Default }; // Debug for AsciiBoxStr { Debug }; // Display for AsciiBoxStr { Display }; // Deref for AsciiBoxStr { Deref }; // DerefMut for AsciiBoxStr { DerefMut }; // FromStr for AsciiBoxStr { FromStr }; } validated_slice::impl_cmp_for_owned_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiBoxStrSpec, custom: AsciiBoxStr, inner: Box, slice_custom: AsciiStr, slice_inner: str, base: Inner, }; Cmp { PartialEq, PartialOrd }; // { lhs, rhs }. { ({Custom}), ({Custom}) }; { ({Custom}), ({SliceCustom}), rev }; { ({Custom}), (&{SliceCustom}), rev }; //// NOTE: This requires `core::borrow::Borrow for AsciiBoxStr`. { ({Custom}), (Cow<{SliceCustom}>), rev }; { ({Custom}), ({Inner}), rev }; { ({Custom}), ({SliceInner}), rev }; { ({Custom}), (&{SliceInner}), rev }; { ({Custom}), (Cow<{SliceInner}>), rev }; { ({Inner}), ({SliceCustom}), rev }; { ({Inner}), (&{SliceCustom}), rev }; } enum AsciiStringSpec {} impl validated_slice::OwnedSliceSpec for AsciiStringSpec { type Custom = AsciiString; type Inner = String; type Error = AsciiError; type SliceSpec = AsciiStrSpec; type SliceCustom = AsciiStr; type SliceInner = str; type SliceError = AsciiError; #[inline] fn convert_validation_error(e: Self::SliceError, _: Self::Inner) -> Self::Error { e } #[inline] fn as_slice_inner(s: &Self::Custom) -> &Self::SliceInner { &s.0 } #[inline] fn as_slice_inner_mut(s: &mut Self::Custom) -> &mut Self::SliceInner { &mut s.0 } #[inline] fn inner_as_slice_inner(s: &Self::Inner) -> &Self::SliceInner { s } #[inline] unsafe fn from_inner_unchecked(s: Self::Inner) -> Self::Custom { AsciiString(s) } #[inline] fn into_inner(s: Self::Custom) -> Self::Inner { s.0 } } /// ASCII string boxed slice. #[derive(Default, Clone, Eq, Ord, Hash)] pub struct AsciiString(String); impl From for AsciiString { fn from(s: AsciiBoxStr) -> Self { Self(s.0.into()) } } validated_slice::impl_std_traits_for_owned_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiStringSpec, custom: AsciiString, inner: String, error: AsciiError, slice_custom: AsciiStr, slice_inner: str, slice_error: AsciiError, }; // AsMut for AsciiString // NOTE: `AsMut<[u8]> for str` is not implemented. //{ AsMut }; // AsMut for AsciiString { AsMut<{SliceCustom}> }; // AsRef<[u8]> for AsciiString { AsRef<[u8]> }; // AsRef for AsciiString { AsRef }; // AsRef for AsciiString { AsRef<{SliceCustom}> }; // Borrow<[u8]> for AsciiString // NOTE: `Borrow<[u8]> for str` is not implemented. //{ Borrow<[u8]> }; // Borrow for AsciiString { Borrow }; // Borrow for AsciiString { Borrow<{SliceCustom}> }; // BorrowMut for AsciiString { BorrowMut<{SliceCustom}> }; // ToOwned for AsciiStr { ToOwned for {SliceCustom} }; // From<&'_ AsciiStr> for AsciiString { From<&{SliceCustom}> }; // From for String { From<{Custom}> for {Inner} }; // TryFrom<&'_ str> for AsciiString { TryFrom<&{SliceInner}> }; // TryFrom for AsciiString { TryFrom<{Inner}> }; // Default for AsciiString // NOTE: Same as `#[derive(Default)]` in this case. //{ Default }; // Debug for AsciiString { Debug }; // Display for AsciiString { Display }; // Deref for AsciiString { Deref }; // DerefMut for AsciiString { DerefMut }; // FromStr for AsciiString { FromStr }; } validated_slice::impl_cmp_for_owned_slice! { Std { core: core, alloc: alloc, }; Spec { spec: AsciiStringSpec, custom: AsciiString, inner: String, slice_custom: AsciiStr, slice_inner: str, base: Inner, }; Cmp { PartialEq, PartialOrd }; // { lhs, rhs }. { ({Custom}), ({Custom}) }; { ({Custom}), ({SliceCustom}), rev }; { ({Custom}), (&{SliceCustom}), rev }; //// NOTE: This requires `core::borrow::Borrow for AsciiString`. { ({Custom}), (Cow<{SliceCustom}>), rev }; { ({Custom}), ({Inner}), rev }; { ({Custom}), ({SliceInner}), rev }; { ({Custom}), (&{SliceInner}), rev }; { ({Custom}), (Cow<{SliceInner}>), rev }; { ({Inner}), ({SliceCustom}), rev }; { ({Inner}), (&{SliceCustom}), rev }; } #[cfg(test)] mod ascii_str { use super::*; #[test] fn as_ref() where AsciiStr: AsRef<[u8]>, AsciiStr: AsRef, AsciiStr: AsRef, { } #[test] fn partial_eq_custom() where AsciiStr: PartialEq, for<'a> AsciiStr: PartialEq<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialEq, for<'a> AsciiStr: PartialEq>, for<'a> alloc::borrow::Cow<'a, AsciiStr>: PartialEq>, { } #[test] fn partial_eq_inner() where AsciiStr: PartialEq, str: PartialEq, for<'a> AsciiStr: PartialEq<&'a str>, for<'a> &'a str: PartialEq, for<'a> &'a AsciiStr: PartialEq, for<'a> str: PartialEq<&'a AsciiStr>, for<'a> AsciiStr: PartialEq>, for<'a> alloc::borrow::Cow<'a, str>: PartialEq, for<'a, 'b> &'b AsciiStr: PartialEq>, for<'a, 'b> alloc::borrow::Cow<'a, str>: PartialEq<&'b AsciiStr>, { } #[test] fn from() where for<'a> &'a str: From<&'a AsciiStr>, { } #[test] fn from_smart_ptr() where for<'a> alloc::sync::Arc: From<&'a AsciiStr>, for<'a> alloc::boxed::Box: From<&'a AsciiStr>, for<'a> alloc::rc::Rc: From<&'a AsciiStr>, { } #[test] fn try_from() where for<'a> &'a AsciiStr: core::convert::TryFrom<&'a str>, for<'a> &'a mut AsciiStr: core::convert::TryFrom<&'a mut str>, { } #[test] fn default() where for<'a> &'a AsciiStr: Default, for<'a> &'a mut AsciiStr: Default, { } #[test] fn fmt() where AsciiStr: core::fmt::Debug, AsciiStr: core::fmt::Display, { use core::convert::TryFrom; let sample_raw = "text"; let sample_ascii = <&AsciiStr>::try_from(sample_raw).expect("Should never fail"); assert_eq!(format!("{:?}", sample_ascii), "Ascii(\"text\")"); assert_eq!(format!("{}", sample_ascii), sample_raw); } #[test] fn deref() where AsciiStr: core::ops::Deref, { } } #[cfg(test)] mod ascii_box_str { use super::*; #[test] fn as_ref() where AsciiBoxStr: AsRef<[u8]>, AsciiBoxStr: AsRef, AsciiBoxStr: AsRef, AsciiBoxStr: AsMut, { } #[test] fn borrow() where AsciiBoxStr: core::borrow::Borrow, AsciiBoxStr: core::borrow::Borrow, { } #[test] fn borrow_mut() where AsciiBoxStr: core::borrow::BorrowMut, { } #[test] fn partial_eq_custom() where AsciiBoxStr: PartialEq, AsciiBoxStr: PartialEq, AsciiStr: PartialEq, for<'a> AsciiBoxStr: PartialEq<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialEq, for<'a> AsciiBoxStr: PartialEq>, for<'a> alloc::borrow::Cow<'a, AsciiStr>: PartialEq, { } #[test] fn partial_eq_inner() where AsciiBoxStr: PartialEq, str: PartialEq, for<'a> AsciiBoxStr: PartialEq<&'a str>, for<'a> &'a str: PartialEq, for<'a> AsciiBoxStr: PartialEq>, for<'a> alloc::borrow::Cow<'a, str>: PartialEq, Box: PartialEq, AsciiStr: PartialEq>, for<'a> Box: PartialEq<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialEq>, { } #[test] fn partial_ord_custom() where AsciiBoxStr: PartialOrd, AsciiBoxStr: PartialOrd, AsciiStr: PartialOrd, for<'a> AsciiBoxStr: PartialOrd<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialOrd, for<'a> AsciiBoxStr: PartialOrd>, for<'a> alloc::borrow::Cow<'a, AsciiStr>: PartialOrd, { } #[test] fn partial_ord_inner() where AsciiBoxStr: PartialOrd, str: PartialOrd, for<'a> AsciiBoxStr: PartialOrd<&'a str>, for<'a> &'a str: PartialOrd, for<'a> AsciiBoxStr: PartialOrd>, for<'a> alloc::borrow::Cow<'a, str>: PartialOrd, Box: PartialOrd, AsciiStr: PartialOrd>, for<'a> Box: PartialOrd<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialOrd>, { } #[test] fn from() where for<'a> AsciiBoxStr: From<&'a AsciiStr>, Box: From, { } #[test] fn try_from() where for<'a> AsciiBoxStr: core::convert::TryFrom<&'a str>, AsciiBoxStr: core::convert::TryFrom>, { } #[test] fn fmt() where AsciiBoxStr: core::fmt::Debug, AsciiBoxStr: core::fmt::Display, { use core::convert::TryFrom; let sample_raw = "text"; let sample_ascii = AsciiBoxStr::try_from(sample_raw).expect("Should never fail"); assert_eq!(format!("{:?}", sample_ascii), "Ascii(\"text\")"); assert_eq!(format!("{}", sample_ascii), sample_raw); let sample_ascii_borrowed = <&AsciiStr>::try_from(sample_raw).expect("Should never fail"); assert_eq!( format!("{:?}", sample_ascii), format!("{:?}", sample_ascii_borrowed) ); assert_eq!( format!("{}", sample_ascii), format!("{}", sample_ascii_borrowed) ); } #[test] fn deref() where AsciiBoxStr: core::ops::Deref, { } #[test] fn deref_mut() where AsciiBoxStr: core::ops::DerefMut, { } #[test] fn from_str() where AsciiBoxStr: core::str::FromStr, { } } #[cfg(test)] mod ascii_string { use super::*; #[test] fn as_ref() where AsciiString: AsRef<[u8]>, AsciiString: AsRef, AsciiString: AsRef, AsciiString: AsMut, { } #[test] fn borrow() where AsciiString: core::borrow::Borrow, AsciiString: core::borrow::Borrow, { } #[test] fn borrow_mut() where AsciiString: core::borrow::BorrowMut, { } #[test] fn to_owned() where AsciiStr: alloc::borrow::ToOwned, { } #[test] fn partial_eq_custom() where AsciiString: PartialEq, AsciiString: PartialEq, AsciiStr: PartialEq, for<'a> AsciiString: PartialEq<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialEq, for<'a> AsciiString: PartialEq>, for<'a> alloc::borrow::Cow<'a, AsciiStr>: PartialEq, { } #[test] fn partial_eq_inner() where AsciiString: PartialEq, str: PartialEq, for<'a> AsciiString: PartialEq<&'a str>, for<'a> &'a str: PartialEq, for<'a> AsciiString: PartialEq>, for<'a> alloc::borrow::Cow<'a, str>: PartialEq, String: PartialEq, AsciiStr: PartialEq, for<'a> String: PartialEq<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialEq, { } #[test] fn partial_ord_custom() where AsciiString: PartialOrd, AsciiString: PartialOrd, AsciiStr: PartialOrd, for<'a> AsciiString: PartialOrd<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialOrd, for<'a> AsciiString: PartialOrd>, for<'a> alloc::borrow::Cow<'a, AsciiStr>: PartialOrd, { } #[test] fn partial_ord_inner() where AsciiString: PartialOrd, str: PartialOrd, for<'a> AsciiString: PartialOrd<&'a str>, for<'a> &'a str: PartialOrd, for<'a> AsciiString: PartialOrd>, for<'a> alloc::borrow::Cow<'a, str>: PartialOrd, String: PartialOrd, AsciiStr: PartialOrd, for<'a> String: PartialOrd<&'a AsciiStr>, for<'a> &'a AsciiStr: PartialOrd, { } #[test] fn from() where for<'a> AsciiString: From<&'a AsciiStr>, String: From, { } #[test] fn try_from() where for<'a> AsciiString: core::convert::TryFrom<&'a str>, AsciiString: core::convert::TryFrom, { } #[test] fn fmt() where AsciiString: core::fmt::Debug, AsciiString: core::fmt::Display, { use core::convert::TryFrom; let sample_raw = "text"; let sample_ascii = AsciiString::try_from(sample_raw).expect("Should never fail"); assert_eq!(format!("{:?}", sample_ascii), "Ascii(\"text\")"); assert_eq!(format!("{}", sample_ascii), sample_raw); let sample_ascii_borrowed = <&AsciiStr>::try_from(sample_raw).expect("Should never fail"); assert_eq!( format!("{:?}", sample_ascii), format!("{:?}", sample_ascii_borrowed) ); assert_eq!( format!("{}", sample_ascii), format!("{}", sample_ascii_borrowed) ); } #[test] fn deref() where AsciiString: core::ops::Deref, { } #[test] fn deref_mut() where AsciiString: core::ops::DerefMut, { } #[test] fn from_str() where AsciiString: core::str::FromStr, { } }