|
|
|
use super::*;
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use std::{fmt,error};
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
/// A spec to validate the formatting of a string for.
|
|
|
|
pub trait FormatSpec
|
|
|
|
{
|
|
|
|
type Error: Into<eyre::Report>;
|
|
|
|
fn validate(s: &str) -> Result<(), Self::Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// A strongly validated string slice
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
#[repr(transparent)]
|
|
|
|
pub struct FormattedStr<F: FormatSpec + ?Sized>(PhantomData<*const F>, str);
|
|
|
|
|
|
|
|
impl<F: FormatSpec + ?Sized> std::marker::Unpin for FormattedStr<F>{}
|
|
|
|
unsafe impl<F: FormatSpec + ?Sized> std::marker::Send for FormattedStr<F>{}
|
|
|
|
unsafe impl<F: FormatSpec + ?Sized> std::marker::Sync for FormattedStr<F>{}
|
|
|
|
|
|
|
|
/// A strongly validated string
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub struct FormattedString<F: FormatSpec + ?Sized>(String, PhantomData<F>);
|
|
|
|
|
|
|
|
impl<F: FormatSpec + ?Sized> FormattedStr<F>
|
|
|
|
{
|
|
|
|
/// 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<F: FormatSpec + ?Sized> FormattedString<F>
|
|
|
|
{
|
|
|
|
/// 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<Self, F::Error>
|
|
|
|
{
|
|
|
|
F::validate(&s)
|
|
|
|
.map(move |_| unsafe {Self::new_unchecked(s)})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: FormatSpec + ?Sized> FormattedString<F>
|
|
|
|
{
|
|
|
|
/// As a formatted str
|
|
|
|
#[inline] pub fn as_str<'a>(&'a self) -> &'a FormattedStr<F>
|
|
|
|
{
|
|
|
|
unsafe { FormattedStr::new_unchecked(&self.0) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: FormatSpec + ?Sized> AsRef<str> for FormattedStr<F>
|
|
|
|
{
|
|
|
|
#[inline] fn as_ref(&self) -> &str
|
|
|
|
{
|
|
|
|
self.as_str()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<F: FormatSpec + ?Sized> AsRef<FormattedStr<F>> for FormattedString<F>
|
|
|
|
{
|
|
|
|
#[inline] fn as_ref(&self) -> &FormattedStr<F> {
|
|
|
|
self.as_str()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: FormatSpec + ?Sized> Deref for FormattedString<F>
|
|
|
|
{
|
|
|
|
type Target = FormattedStr<F>;
|
|
|
|
#[inline] fn deref(&self) -> &Self::Target {
|
|
|
|
self.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a,F: FormatSpec + ?Sized> From<&'a FormattedStr<F>> for &'a str
|
|
|
|
{
|
|
|
|
#[inline] fn from(from: &'a FormattedStr<F>) -> Self
|
|
|
|
{
|
|
|
|
&from.1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'a,F: FormatSpec + ?Sized> TryFrom<&'a str> for &'a FormattedStr<F>
|
|
|
|
{
|
|
|
|
type Error = F::Error;
|
|
|
|
|
|
|
|
#[inline] fn try_from(from: &'a str) -> Result<Self, Self::Error>
|
|
|
|
{
|
|
|
|
FormattedStr::new(from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, F: FormatSpec + ?Sized> From<FormattedString<F>> for String
|
|
|
|
{
|
|
|
|
#[inline] fn from(from: FormattedString<F>) -> Self
|
|
|
|
{
|
|
|
|
from.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, F: FormatSpec + ?Sized> TryFrom<String> for FormattedString<F>
|
|
|
|
{
|
|
|
|
type Error = F::Error;
|
|
|
|
|
|
|
|
fn try_from(from: String) -> Result<Self, Self::Error>
|
|
|
|
{
|
|
|
|
Self::new(from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub mod formats
|
|
|
|
{
|
|
|
|
use super::*;
|
|
|
|
/// A hex string format specifier
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub enum HexFormat{}
|
|
|
|
|
|
|
|
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> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type HexFormattedStr = FormattedStr<HexFormat>;
|
|
|
|
pub type HexFormattedString = FormattedString<HexFormat>;
|
|
|
|
|
|
|
|
pub type PEMFormattedStr = FormattedStr<PEMFormat>;
|
|
|
|
pub type PEMFormattedString = FormattedString<PEMFormat>;
|
|
|
|
|
|
|
|
#[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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|