extern crate serde;
extern crate rand;
#[macro_use]
extern crate error_chain;
mod errors;
use std::result;
use serde::de::{self, Visitor};
pub use errors::{Error, ErrorKind, Result};
pub trait Checkable: Sized {
const NAME: &'static str;
fn check(key: &str) -> Result<Self>;
}
#[macro_export]
macro_rules! byte_seq {
($name:ident; $count:expr) => {
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
pub struct $name([u8; $count]);
impl $crate::Checkable for $name {
const NAME: &'static str = stringify!($name);
fn check(key: &str) -> $crate::Result<$name> {
ensure!(key.chars().count() == ($count * 2),
$crate::ErrorKind::InvalidKeyLen(key.to_string(), stringify!($name)));
let mut bytes: [u8; $count] = [0u8; $count];
for i in 0..$count {
if let Ok(byte) = u8::from_str_radix(&key[i*2..i*2+2], 16) {
bytes[i] = byte;
} else {
bail!($crate::ErrorKind::InvalidKeyChars(key.to_string(), stringify!($name)))
}
}
Ok($name(bytes))
}
}
impl $name {
pub fn generate_new() -> $name {
use rand::thread_rng;
use rand::Rng;
let mut bytes: [u8; $count] = [0u8; $count];
thread_rng().fill_bytes(&mut bytes);
$name(bytes)
}
pub fn to_string(&self) -> String {
let mut result = String::with_capacity($count * 2);
for byte in &self.0 {
result.push_str(&format!("{:0width$X}", byte, width=2));
}
result
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}({})", stringify!($name), self.to_string())
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where S: serde::Serializer
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where D: serde::Deserializer<'de>
{
deserializer.deserialize_str($crate::CheckableVisitor{_type:std::marker::PhantomData})
}
}
}
}
pub struct CheckableVisitor<T: Checkable> {
pub _type: std::marker::PhantomData<T>,
}
impl<'de, Checked: Checkable> Visitor<'de> for CheckableVisitor<Checked> {
type Value = Checked;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("An $name in String format!")
}
fn visit_str<E>(self, value: &str) -> result::Result<Checked, E>
where E: de::Error
{
Checked::check(value).map_err(|e| {
E::custom(format!("Failed to deserialize {} '{:?}'",
Checked::NAME,
e))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
byte_seq!(Test; 10);
#[test]
fn serialize_deserialize() {
let a = Test::generate_new();
assert_eq!(a, Test::check(&a.to_string()).unwrap());
}
byte_seq!(ApiKey; 32);
#[test]
fn example() {
let key = ApiKey::generate_new();
assert_eq!(key.to_string().len(), 64);
let key = ApiKey::check("BBC47F308F3D02C3C6C3D6C9555296A64407FE72AD92DE8C7344D610CFFABF67").unwrap();
assert_eq!(key.to_string(), "BBC47F308F3D02C3C6C3D6C9555296A64407FE72AD92DE8C7344D610CFFABF67");
}
}