use std::sync::atomic::{AtomicUsize, Ordering};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use rand;
use soft_ascii_string::SoftAsciiString;
use internals::error::EncodingError;
use headers::header_components::{MessageId, ContentId, Domain};
use ::context::MailIdGenComponent;
static MAIL_COUNTER: AtomicUsize = AtomicUsize::new(0);
fn counter_next() -> usize {
MAIL_COUNTER.fetch_add(1, Ordering::AcqRel)
}
fn anonymize_through_random_hash(num: usize) -> u64 {
let rnum = rand::random::<u32>();
let mut hasher = DefaultHasher::new();
hasher.write_usize(num);
hasher.write_u32(rnum);
hasher.finish()
}
fn gen_next_program_unique_number() -> u64 {
anonymize_through_random_hash(counter_next())
}
#[derive(Debug, Clone)]
pub struct HashedIdGen {
domain: SoftAsciiString,
part_unique_in_domain: SoftAsciiString
}
impl HashedIdGen {
pub fn new(domain: Domain, part_unique_in_domain: SoftAsciiString)
-> Result<Self, EncodingError>
{
let domain = domain.into_ascii_string()?;
Ok(HashedIdGen {
domain,
part_unique_in_domain
})
}
}
impl MailIdGenComponent for HashedIdGen {
fn generate_message_id(&self) -> MessageId {
let msg_id = format!("{unique}.{hash:x}@{domain}",
unique=self.part_unique_in_domain,
hash=gen_next_program_unique_number(),
domain=self.domain);
MessageId::from_unchecked(msg_id)
}
fn generate_content_id(&self) -> ContentId {
self.generate_message_id().into()
}
}
#[cfg(test)]
mod test {
mod HashedIdGen {
#![allow(non_snake_case)]
use std::sync::Arc;
use std::collections::HashSet;
use soft_ascii_string::SoftAsciiString;
use headers::header_components::Domain;
use headers::HeaderTryFrom;
#[allow(unused_imports)]
use ::context::MailIdGenComponent;
use super::super::HashedIdGen;
fn setup() -> Arc<HashedIdGen> {
let unique_part = SoftAsciiString::from_unchecked("bfr7tz4");
let domain = Domain::try_from("fooblabar.test").unwrap();
Arc::new(HashedIdGen::new(domain, unique_part).unwrap())
}
mod get_message_id {
use super::*;
#[test]
fn should_always_return_a_new_id() {
let id_gen = setup();
let mut cids = HashSet::new();
for _ in 0..20 {
assert!(cids.insert(id_gen.generate_message_id()))
}
}
}
mod generate_content_id {
use super::*;
#[test]
fn should_always_return_a_new_id() {
let id_gen = setup();
let mut cids = HashSet::new();
for _ in 0..20 {
assert!(cids.insert(id_gen.generate_content_id()))
}
}
}
}
}