pass_it_on/
notifications.rsuse crate::{Error, KEY_CONTEXT};
use blake3::Hash;
use serde::{Deserialize, Serialize};
use serde_json::StreamDeserializer;
use std::collections::HashSet;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)]
pub struct Message {
text: String,
time: u128,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct ClientReadyMessage {
message: Message,
notification_name: String,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct ValidatedNotification {
message: Message,
sub_name: String,
}
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)]
pub struct Notification {
message: Message,
key: String,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Key {
hash: Hash,
}
impl Notification {
pub fn new(message: Message, notification_key: &Key) -> Notification {
let key = message.create_key(notification_key).to_hex();
Notification { message, key }
}
pub fn from_json<S: AsRef<str>>(input: S) -> Result<Notification, Error> {
Ok(serde_json::from_str(input.as_ref())?)
}
pub fn from_json_multi<S: AsRef<str>>(input: S) -> Vec<Result<Notification, Error>> {
let mut notifications = Vec::new();
let stream: StreamDeserializer<_, Notification> =
serde_json::Deserializer::from_str(input.as_ref()).into_iter();
for item in stream {
match item {
Err(e) => notifications.push(Err(Error::SerdeJsonError(e))),
Ok(n) => notifications.push(Ok(n)),
};
}
notifications
}
pub fn to_json(&self) -> Result<String, Error> {
Ok(serde_json::to_string(self)?)
}
pub(crate) fn validate(&self, hash_key: &Key) -> bool {
let new_key = self.message.create_key(hash_key);
self.key == new_key.to_hex()
}
pub(crate) fn validate_set(&self, hash_keys: &HashSet<Key>) -> bool {
for hash_key in hash_keys {
match self.validate(hash_key) {
true => return true,
false => (),
}
}
false
}
pub fn message(&self) -> Message {
self.message.clone()
}
pub fn key(&self) -> &str {
&self.key
}
}
impl Message {
pub fn new<S: AsRef<str>>(text: S) -> Message {
let time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_nanos();
let body = String::from(text.as_ref());
Self { text: body, time }
}
pub fn text(&self) -> &str {
&self.text
}
pub fn time(&self) -> u128 {
self.time
}
fn create_key(&self, notification_key: &Key) -> Key {
let hash_string = format!("{}{}", self.text, self.time);
Key::generate(hash_string.as_str(), notification_key)
}
pub fn to_client_ready_message<S: AsRef<str>>(self, notification_name: S) -> ClientReadyMessage {
ClientReadyMessage::new(notification_name, self)
}
}
impl ClientReadyMessage {
pub(crate) fn new<S: AsRef<str>>(notification_name: S, message: Message) -> Self {
Self { notification_name: notification_name.as_ref().into(), message }
}
pub fn to_notification(self, client_key: &Key) -> Notification {
let key = Key::generate(self.notification_name(), client_key);
let message = self.message;
Notification::new(message, &key)
}
pub fn message(&self) -> &Message {
&self.message
}
pub fn notification_name(&self) -> &str {
&self.notification_name
}
}
impl ValidatedNotification {
pub fn new<S: AsRef<str>>(name_id: S, message: Message) -> ValidatedNotification {
Self { sub_name: name_id.as_ref().into(), message }
}
pub fn message(&self) -> &Message {
&self.message
}
pub fn sub_name(&self) -> &str {
&self.sub_name
}
}
impl Key {
pub fn generate<S: AsRef<str>>(name: S, hash_key: &Key) -> Key {
let mut hasher = blake3::Hasher::new_keyed(hash_key.as_bytes());
hasher.update(name.as_ref().as_bytes());
Self { hash: hasher.finalize() }
}
pub fn derive_shared_key<S: AsRef<str>>(key_string: S) -> Key {
Self::from_bytes(&blake3::derive_key(KEY_CONTEXT, key_string.as_ref().as_bytes()))
}
pub fn from_bytes(key: &[u8; 32]) -> Key {
let hash = Hash::from(*key);
Self { hash }
}
pub fn from_hex<S: AsRef<str>>(key: S) -> Key {
let hash = Hash::from_hex(key.as_ref()).expect("Unable to create Key from hex");
Self { hash }
}
pub fn to_hex(&self) -> String {
self.hash.to_hex().to_string()
}
pub fn as_bytes(&self) -> &[u8; 32] {
self.hash.as_bytes()
}
}