You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yuurei/src/hard_format.rs

408 lines
9.9 KiB

4 years ago
use super::*;
use std::marker::PhantomData;
use std::{fmt,error};
use std::ops::Deref;
use std::borrow::{Borrow, ToOwned};
4 years ago
/// 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)]
4 years ago
#[repr(transparent)]
4 years ago
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>{}
4 years ago
/// 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)]
4 years ago
pub struct FormattedString<F: FormatSpec + ?Sized>(String, PhantomData<F>);
// Deserialising
const _:() = {
use serde::{
de::Visitor,
de::Error,
de,
Serialize,
ser::Serializer,
};
impl<F: FormatSpec + ?Sized> Serialize for FormattedStr<F>
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.1)
}
}
impl<F: FormatSpec + ?Sized> Serialize for FormattedString<F>
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0)
}
}
#[derive(Debug, Clone, Copy)]
struct FormattedStringVisitor<F: FormatSpec + ?Sized>(PhantomData<F>);
impl<'de, F: FormatSpec + ?Sized> Visitor<'de> for FormattedStringVisitor<F>
{
type Value = FormattedString<F>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "a string satisfying the requirements of {}", std::any::type_name::<F>())
}
fn visit_str<E: Error>(self, s: &str) -> Result<Self::Value, E>
{
FormattedStr::<F>::new(s).map_err(|x| E::custom(x.into())).map(ToOwned::to_owned)
}
fn visit_string<E: Error>(self, s: String) -> Result<Self::Value, E>
{
FormattedString::<F>::new(s).map_err(|x| E::custom(x.into()))
}
}
impl<'de, F: FormatSpec + ?Sized> de::Deserialize<'de> for FormattedString<F> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let vis = FormattedStringVisitor::<F>(PhantomData);
deserializer.deserialize_string(vis)
}
}
};
4 years ago
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_ref<'a>(&'a self) -> &'a FormattedStr<F>
4 years ago
{
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> Borrow<FormattedStr<F>> for FormattedString<F>
{
#[inline] fn borrow(&self) -> &FormattedStr<F> {
self.as_ref()
}
}
impl<F: FormatSpec + ?Sized> ToOwned for FormattedStr<F>
{
type Owned = FormattedString<F>;
#[inline] fn to_owned(&self) -> Self::Owned {
FormattedString(String::from(&self.1), PhantomData)
}
}
4 years ago
impl<F: FormatSpec + ?Sized> AsRef<FormattedStr<F>> for FormattedString<F>
{
#[inline] fn as_ref(&self) -> &FormattedStr<F> {
self.as_ref()
4 years ago
}
}
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
{
4 years ago
use super::*;
cfg_if! {
if #[cfg(feature="nightly")] {
/// A format valid for any string
pub type AnyFormat = !;
} else {
/// A format valid for any string
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AnyFormat{}
}
}
impl FormatSpec for AnyFormat
{
type Error = std::convert::Infallible;
#[inline] fn validate(_: &str) -> Result<(), Self::Error>
{
Ok(())
}
}
/// A format spec that must satisfy both these format specs in order
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BothFormat<T, U = AnyFormat>(PhantomData<(T, U)>)
where T: FormatSpec, U: FormatSpec;
#[derive(Debug)]
pub enum MultiFormatError<T,U>
{
First(T),
Second(U),
}
impl<T, U> FormatSpec for BothFormat<T,U>
where T: FormatSpec,
U: FormatSpec,
T::Error : error::Error + 'static + Send + Sync,
U::Error : error::Error + 'static + Send + Sync,
{
type Error = MultiFormatError<T::Error, U::Error>;
fn validate(s: &str) -> Result<(), Self::Error> {
T::validate(s).map_err(MultiFormatError::First)?;
U::validate(s).map_err(MultiFormatError::Second)?;
Ok(())
}
}
impl<T: 'static, U: 'static> error::Error for MultiFormatError<T,U>
where T: error::Error,
U: error::Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self {
Self::First(f) => Some(f),
Self::Second(n) => Some(n),
}
}
}
impl<T, U> fmt::Display for MultiFormatError<T,U>
where T: fmt::Display,
U: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::First(_) => write!(f, "the first condition failed"),
Self::Second(_) => write!(f, "the second condition failed"),
}
}
}
4 years ago
/// A hex string format specifier
4 years ago
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4 years ago
pub enum HexFormat{}
/// A string with a constant max length
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MaxLenFormat<const MAX: usize>{}
impl<const MAX: usize> FormatSpec for MaxLenFormat<MAX>
{
type Error = MaxLenExceededError<MAX>;
4 years ago
#[inline] fn validate(s: &str) -> Result<(), Self::Error> {
if s.len() > MAX {
Err(MaxLenExceededError)
} else {
Ok(())
}
}
}
#[derive(Debug)]
pub struct MaxLenExceededError<const MAX: usize>;
impl<const MAX: usize> error::Error for MaxLenExceededError<MAX>{}
impl<const MAX: usize> fmt::Display for MaxLenExceededError<MAX>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "string length exceeds {}", MAX)
}
}
4 years ago
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.
4 years ago
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4 years ago
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> {
4 years ago
todo!("Learn the PEM ciphertext format");
Ok(())
4 years ago
}
}
pub type MaxLenStr<const MAX: usize> = FormattedStr<MaxLenFormat<MAX>>;
pub type MaxLenString<const MAX: usize> = FormattedString<MaxLenFormat<MAX>>;
4 years ago
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");
}
}
}