byte_sequence/
lib.rs

1extern crate rand;
2extern crate serde;
3#[macro_use]
4extern crate failure;
5
6use std::result;
7
8use serde::de::{self, Visitor};
9
10pub type Error = failure::Error;
11
12pub trait Checkable: Sized {
13    const NAME: &'static str;
14    fn check(key: &str) -> Result<Self, failure::Error>;
15}
16
17#[derive(Debug, Fail)]
18pub enum ByteSequenceError {
19    #[fail(
20        display = "Failed to convert '{}' to '{}' because it had the wrong length",
21        raw_key,
22        typename
23    )]
24    InvalidKeyLen {
25        raw_key: String,
26        typename: &'static str,
27    },
28    #[fail(
29        display = "Failed to convert '{}' to '{}' because it contained invalid characters",
30        raw_key,
31        typename
32    )]
33    InvalidKeyChars {
34        raw_key: String,
35        typename: &'static str,
36    },
37}
38
39#[macro_export]
40macro_rules! byte_seq {
41    ($name:ident; $count:expr) => {
42        #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
43        pub struct $name([u8; $count]);
44
45        impl $crate::Checkable for $name {
46            const NAME: &'static str = stringify!($name);
47            fn check(key: &str) -> std::result::Result<$name, $crate::Error> {
48                ensure!(
49                    key.chars().count() == ($count * 2),
50                    $crate::ByteSequenceError::InvalidKeyLen {
51                        raw_key: key.to_string(),
52                        typename: stringify!($name)
53                    }
54                );
55
56                let mut bytes: [u8; $count] = [0u8; $count];
57                for i in 0..$count {
58                    if let Ok(byte) = u8::from_str_radix(&key[i * 2..i * 2 + 2], 16) {
59                        bytes[i] = byte;
60                    } else {
61                        bail!($crate::ByteSequenceError::InvalidKeyChars {
62                            raw_key: key.to_string(),
63                            typename: stringify!($name)
64                        })
65                    }
66                }
67                Ok($name(bytes))
68            }
69        }
70
71        impl $name {
72            pub fn generate_new() -> $name {
73                use rand::thread_rng;
74                use rand::RngCore;
75
76                let mut bytes: [u8; $count] = [0u8; $count];
77                thread_rng().fill_bytes(&mut bytes);
78                $name(bytes)
79            }
80
81            pub fn to_string(&self) -> String {
82                let mut result = String::with_capacity($count * 2);
83                for byte in &self.0 {
84                    result.push_str(&format!("{:0width$X}", byte, width = 2));
85                }
86                result
87            }
88        }
89
90        impl std::fmt::Display for $name {
91            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92                write!(f, "{}({})", stringify!($name), self.to_string())
93            }
94        }
95
96        impl std::fmt::Debug for $name {
97            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
98                write!(f, "{}", self)
99            }
100        }
101
102        impl serde::Serialize for $name {
103            fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
104            where
105                S: serde::Serializer,
106            {
107                serializer.serialize_str(&self.to_string())
108            }
109        }
110
111        impl<'de> serde::Deserialize<'de> for $name {
112            fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
113            where
114                D: serde::Deserializer<'de>,
115            {
116                deserializer.deserialize_str($crate::CheckableVisitor {
117                    _type: std::marker::PhantomData,
118                })
119            }
120        }
121    };
122}
123
124pub struct CheckableVisitor<T: Checkable> {
125    pub _type: std::marker::PhantomData<T>,
126}
127
128impl<'de, Checked: Checkable> Visitor<'de> for CheckableVisitor<Checked> {
129    type Value = Checked;
130
131    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
132        formatter.write_str("An $name in String format!")
133    }
134
135    fn visit_str<E>(self, value: &str) -> result::Result<Checked, E>
136    where
137        E: de::Error,
138    {
139        Checked::check(value)
140            .map_err(|e| E::custom(format!("Failed to deserialize {} '{:?}'", Checked::NAME, e)))
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use serde::{Deserialize, Deserializer, Serialize, Serializer};
148
149    byte_seq!(Test; 10);
150
151    #[test]
152    fn serialize_deserialize() {
153        let a = Test::generate_new();
154        assert_eq!(a, Test::check(&a.to_string()).unwrap());
155    }
156
157    byte_seq!(ApiKey; 32);
158
159    #[test]
160    fn example() {
161        // Creates a new ApiKey containing 32 random bytes using a thread_rng
162        let key = ApiKey::generate_new();
163
164        // The to_string method creates a hex encoded string:
165        // i.e. 'BBC47F308F3D02C3C6C3D6C9555296A64407FE72AD92DE8C7344D610CFFABF67'
166        assert_eq!(key.to_string().len(), 64);
167
168        // you can also do it the other way around: Parse a string into an ApiKey
169        let key = ApiKey::check("BBC47F308F3D02C3C6C3D6C9555296A64407FE72AD92DE8C7344D610CFFABF67")
170            .unwrap();
171        assert_eq!(
172            key.to_string(),
173            "BBC47F308F3D02C3C6C3D6C9555296A64407FE72AD92DE8C7344D610CFFABF67"
174        );
175    }
176
177}