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

223 lines
5.2 KiB

4 years ago
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)]
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
#[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
4 years ago
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4 years ago
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.
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> {
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");
}
}
}