use super::*; use std::marker::PhantomData; use std::{fmt,error}; use std::ops::Deref; use std::borrow::{Borrow, ToOwned}; /// A spec to validate the formatting of a string for. pub trait FormatSpec { type Error: Into; fn validate(s: &str) -> Result<(), Self::Error>; } /// A strongly validated string slice #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct FormattedStr(PhantomData<*const F>, str); impl std::marker::Unpin for FormattedStr{} unsafe impl std::marker::Send for FormattedStr{} unsafe impl std::marker::Sync for FormattedStr{} /// A strongly validated string // TODO: How to perform validation on deserialise? Custom `Deserialize` impl? might have to. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FormattedString(String, PhantomData); // Deserialising const _:() = { use serde::{ de::Visitor, de::Error, de, Serialize, ser::Serializer, }; impl Serialize for FormattedStr { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.1) } } impl Serialize for FormattedString { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.0) } } #[derive(Debug, Clone, Copy)] struct FormattedStringVisitor(PhantomData); impl<'de, F: FormatSpec + ?Sized> Visitor<'de> for FormattedStringVisitor { type Value = FormattedString; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "a string satisfying the requirements of {}", std::any::type_name::()) } fn visit_str(self, s: &str) -> Result { FormattedStr::::new(s).map_err(|x| E::custom(x.into())).map(ToOwned::to_owned) } fn visit_string(self, s: String) -> Result { FormattedString::::new(s).map_err(|x| E::custom(x.into())) } } impl<'de, F: FormatSpec + ?Sized> de::Deserialize<'de> for FormattedString { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let vis = FormattedStringVisitor::(PhantomData); deserializer.deserialize_string(vis) } } }; impl FormattedStr { /// Create a new instance without validating the input. /// /// # Safety /// You must be sure the input is of a valid state to `F`. #[inline(always)] pub unsafe fn new_unchecked<'a>(s: &'a str) -> &'a Self { std::mem::transmute(s) } /// Create and validate a the format of a new instance. pub fn new<'a>(s: &'a str) -> Result<&'a Self, F::Error> { F::validate(s).map(move |_| unsafe {Self::new_unchecked(s)}) } /// Get the inner str #[inline] pub fn as_str<'a>(&'a self) -> &'a str { &self.1 } } impl FormattedString { /// Create a new instance without validating the input. /// /// # Safety /// You must be sure the input is of a valid state to `F`. #[inline(always)] pub unsafe fn new_unchecked(s: String) -> Self { std::mem::transmute(s) } /// Create and validate a the format of a new instance. pub fn new(s: String) -> Result { F::validate(&s) .map(move |_| unsafe {Self::new_unchecked(s)}) } } impl FormattedString { /// As a formatted str #[inline] pub fn as_ref<'a>(&'a self) -> &'a FormattedStr { unsafe { FormattedStr::new_unchecked(&self.0) } } } impl AsRef for FormattedStr { #[inline] fn as_ref(&self) -> &str { self.as_str() } } impl Borrow> for FormattedString { #[inline] fn borrow(&self) -> &FormattedStr { self.as_ref() } } impl ToOwned for FormattedStr { type Owned = FormattedString; #[inline] fn to_owned(&self) -> Self::Owned { FormattedString(String::from(&self.1), PhantomData) } } impl AsRef> for FormattedString { #[inline] fn as_ref(&self) -> &FormattedStr { self.as_ref() } } impl Deref for FormattedString { type Target = FormattedStr; #[inline] fn deref(&self) -> &Self::Target { self.as_ref() } } impl<'a,F: FormatSpec + ?Sized> From<&'a FormattedStr> for &'a str { #[inline] fn from(from: &'a FormattedStr) -> Self { &from.1 } } impl<'a,F: FormatSpec + ?Sized> TryFrom<&'a str> for &'a FormattedStr { type Error = F::Error; #[inline] fn try_from(from: &'a str) -> Result { FormattedStr::new(from) } } impl<'a, F: FormatSpec + ?Sized> From> for String { #[inline] fn from(from: FormattedString) -> Self { from.0 } } impl<'a, F: FormatSpec + ?Sized> TryFrom for FormattedString { type Error = F::Error; fn try_from(from: String) -> Result { Self::new(from) } } pub mod formats { use super::*; /// A hex string format specifier #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum HexFormat{} #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum MaxLenFormat{} impl FormatSpec for MaxLenFormat { type Error = MaxLenExceededError; #[inline] fn validate(s: &str) -> Result<(), Self::Error> { if s.len() > MAX { Err(MaxLenExceededError) } else { Ok(()) } } } #[derive(Debug)] pub struct MaxLenExceededError; impl error::Error for MaxLenExceededError{} impl fmt::Display for MaxLenExceededError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "string length exceeds {}", MAX) } } impl HexFormat { const HEX_MAP: &'static [u8] = b"1234567890abcdefABCDEF"; } impl FormatSpec for HexFormat { type Error = HexFormatError; fn validate(s: &str) -> Result<(), Self::Error> { for (i, chr) in s.char_indices() { if !chr.is_ascii_alphanumeric() || !Self::HEX_MAP.contains(&(chr as u8)) { return Err(HexFormatError(chr, i)); } } Ok(()) } } /// Error for an invalid hex string. #[derive(Debug)] pub struct HexFormatError(char, usize); impl error::Error for HexFormatError{} impl fmt::Display for HexFormatError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "invalid hex char {:?} at index {}", self.0, self.1) } } /// A PEM formatted string. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PEMFormat{} #[derive(Debug)] pub struct PEMFormatError; impl error::Error for PEMFormatError{} impl fmt::Display for PEMFormatError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "invalid PEM format") } } impl FormatSpec for PEMFormat { type Error = PEMFormatError; fn validate(_s: &str) -> Result<(), Self::Error> { Ok(()) } } pub type MaxLenStr = FormattedStr>; pub type MaxLenString = FormattedString>; pub type HexFormattedStr = FormattedStr; pub type HexFormattedString = FormattedString; pub type PEMFormattedStr = FormattedStr; pub type PEMFormattedString = FormattedString; #[cfg(test)] mod tests { use super::*; #[test] fn hex_format() { let _invalid = HexFormattedStr::new("ab120982039840i ").expect_err("Invalidation"); let _valid = HexFormattedStr::new("abc123982095830495adcfDD").expect("Validation"); } } }