|
|
|
@ -5,27 +5,43 @@ use std::{
|
|
|
|
|
borrow::Borrow,
|
|
|
|
|
fmt,
|
|
|
|
|
error,
|
|
|
|
|
convert::TryFrom,
|
|
|
|
|
};
|
|
|
|
|
use regex::{
|
|
|
|
|
Regex,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const BASE64_VALIDATE_RE_STR: &'static str = r#"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$"#;
|
|
|
|
|
const MOD_BASE64_VALIDATE_RE_STR: &'static str = r#"^(?:[-A-Za-z0-9_]{4})*(?:[-_A-Za-z0-9]{2}==|[-_A-Za-z0-9]{3}=)?$"#;
|
|
|
|
|
|
|
|
|
|
lazy_static!{
|
|
|
|
|
static ref BASE64_CONV_TABLE: smallmap::Map<char, char> = smallmap![
|
|
|
|
|
{'/' => '_'},
|
|
|
|
|
{'+' => 'ł'},
|
|
|
|
|
{'=' => '-'},
|
|
|
|
|
{'+' => '-'},
|
|
|
|
|
];
|
|
|
|
|
static ref BASE64_CONV_TABLE_REV: smallmap::Map<char, char> = BASE64_CONV_TABLE.clone().reverse();
|
|
|
|
|
|
|
|
|
|
static ref BASE64_VALIDATE_REGEX: Regex = Regex::new(BASE64_VALIDATE_RE_STR).expect("Failed to compile base64 validation regex");
|
|
|
|
|
static ref MOD_BASE64_VALIDATE_REGEX: Regex = Regex::new(MOD_BASE64_VALIDATE_RE_STR).expect("Failed to compile modified base64 validation regex");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct Base64Error<T>(T);
|
|
|
|
|
|
|
|
|
|
impl<T> Base64Error<T>
|
|
|
|
|
{
|
|
|
|
|
/// The invalid value of this error
|
|
|
|
|
pub fn value(&self) -> &T
|
|
|
|
|
{
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
/// Consume into the invalid value from this instance
|
|
|
|
|
#[inline] pub fn into_inner(self) -> T
|
|
|
|
|
{
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> error::Error for Base64Error<T>
|
|
|
|
|
where T: AsRef<str> + fmt::Debug{}
|
|
|
|
|
impl<T> fmt::Display for Base64Error<T>
|
|
|
|
@ -52,7 +68,7 @@ impl fmt::Display for ModifiedBase64String
|
|
|
|
|
|
|
|
|
|
impl ModifiedBase64String
|
|
|
|
|
{
|
|
|
|
|
fn from_base64_unchecked(string: &str) -> Self
|
|
|
|
|
#[inline(always)] fn from_base64_unchecked(string: &str) -> Self
|
|
|
|
|
{
|
|
|
|
|
Self(conv_str(&BASE64_CONV_TABLE, string).collect())
|
|
|
|
|
}
|
|
|
|
@ -63,6 +79,22 @@ impl ModifiedBase64String
|
|
|
|
|
conv_char_iter(&BASE64_CONV_TABLE_REV, self.0.chars()).collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Consume into the inner modified base64 string
|
|
|
|
|
pub fn into_string(self) -> String
|
|
|
|
|
{
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create an instance from a modified base64 string
|
|
|
|
|
pub fn new(from: String) -> Result<Self, Base64Error<String>>
|
|
|
|
|
{
|
|
|
|
|
if MOD_BASE64_VALIDATE_REGEX.is_match(from.as_str()) {
|
|
|
|
|
Ok(Self(from))
|
|
|
|
|
} else {
|
|
|
|
|
Err(Base64Error(from))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Try to convert a base64 string into a modified base64 string
|
|
|
|
|
pub fn try_from_base64<T: AsRef<str>>(base64: T) -> Result<Self, Base64Error<T>>
|
|
|
|
|
{
|
|
|
|
@ -115,6 +147,24 @@ impl AsRef<str> for ModifiedBase64String
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<String> for ModifiedBase64String
|
|
|
|
|
{
|
|
|
|
|
type Error = Base64Error<String>;
|
|
|
|
|
|
|
|
|
|
#[inline] fn try_from(from: String) -> Result<Self, Self::Error>
|
|
|
|
|
{
|
|
|
|
|
Self::try_from_base64(from)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ModifiedBase64String> for String
|
|
|
|
|
{
|
|
|
|
|
#[inline] fn from(from: ModifiedBase64String) -> Self
|
|
|
|
|
{
|
|
|
|
|
from.into_base64()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Convert this string with a specified char map. Returns a `char` yielding iterator.
|
|
|
|
|
#[inline] pub fn conv_str<'a, 'b>(table: &'b smallmap::Map<char, char>, string: &'a (impl AsRef<str> + ?Sized)) -> CharSubstituteIter<'b, std::str::Chars<'a>>
|
|
|
|
@ -131,3 +181,19 @@ where I: IntoIterator<Item=T>,
|
|
|
|
|
{
|
|
|
|
|
iter.replace_chars(table)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests
|
|
|
|
|
{
|
|
|
|
|
#[test]
|
|
|
|
|
fn mod_base64_enc_dec()
|
|
|
|
|
{
|
|
|
|
|
let mut value = [0u8; 512];
|
|
|
|
|
getrandom::getrandom(&mut value[..]).expect("setup failed");
|
|
|
|
|
|
|
|
|
|
let md = super::ModifiedBase64String::encode(&value[..]);
|
|
|
|
|
println!("e-md: {:?}", md);
|
|
|
|
|
let ou = md.decode_new();
|
|
|
|
|
assert_eq!(&ou[..], &value[..]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|