1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
use std::io::Cursor; use std::ops::Deref; use std::fmt; use std::result; use std::error; use base64; use chrono; use rand::{self, Rng}; use byteorder::{LittleEndian, WriteBytesExt}; pub type Result<T> = result::Result<T, Error>; #[derive(Debug)] pub enum Error { Short, Divisable, } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Error::Short => fmt.write_str("Length must be >= 16"), Error::Divisable => fmt.write_str("Length must be divisable by 4") } } } impl error::Error for Error { fn description(&self) -> &str { match *self { Error::Short => "Length must be >= 16.", Error::Divisable => "Length must be divisable by 4." } } fn cause(&self) -> Option<&error::Error> { None } } #[derive(Clone, PartialEq, Debug)] pub struct TextNonce(pub String); impl TextNonce { pub fn new() -> TextNonce { TextNonce::sized(32).unwrap() } pub fn sized(length: usize) -> Result<TextNonce> { TextNonce::sized_configured(length, base64::STANDARD) } pub fn sized_urlsafe(length: usize) -> Result<TextNonce> { TextNonce::sized_configured(length, base64::URL_SAFE) } pub fn sized_configured(length: usize, config: base64::Config) -> Result<TextNonce> { if length < 16 { return Err(Error::Short); } if length % 4 != 0 { return Err(Error::Divisable); } let bytelength: usize = (length / 4) * 3; let mut raw: Vec<u8> = Vec::with_capacity(bytelength); unsafe { raw.set_len(bytelength); } { let now = chrono::Utc::now(); let secs: i64 = now.timestamp(); let nsece: u32 = now.timestamp_subsec_nanos(); let mut cursor = Cursor::new(&mut *raw); cursor.write_u32::<LittleEndian>(nsece).unwrap(); cursor.write_i64::<LittleEndian>(secs).unwrap(); } match rand::OsRng::new() { Ok(mut g) => g.fill_bytes(&mut raw[12..bytelength]), Err(_) => rand::thread_rng().fill_bytes(&mut raw[12..bytelength]) } Ok(TextNonce(base64::encode_config(&raw, config))) } pub fn into_string(self) -> String { let TextNonce(s) = self; s } } impl fmt::Display for TextNonce { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl Deref for TextNonce { type Target = str; fn deref<'a>(&'a self) -> &'a str { &*self.0 } }