pub mod membership;
pub mod model;
pub mod server;
pub mod lattices;
pub mod util;
use std::collections::HashSet;
use std::fmt::Display;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::model::{Clock, Namespaces};
use crate::KeyParseError::InvalidNamespace;
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize, Hash)]
pub enum Namespace {
User,
System,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum KeyParseError {
InvalidNamespace,
InvalidFormat,
}
impl Display for KeyParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InvalidNamespace => write!(f, "Invalid namespace"),
KeyParseError::InvalidFormat => write!(f, "Invalid key format"),
}
}
}
impl FromStr for Namespace {
type Err = KeyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"usr" => Ok(Namespace::User),
"sys" => Ok(Namespace::System),
_ => Err(InvalidNamespace),
}
}
}
pub type TableName = String;
pub type RowKey = String;
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Hash)]
pub struct Key {
pub namespace: Namespace,
pub table: TableName,
pub row_key: RowKey,
}
impl FromStr for Key {
type Err = KeyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts: Vec<String> = vec![];
let mut current_part = String::new();
let mut escaping = false;
for c in s.chars() {
match (escaping, c) {
(true, '\\') | (true, '/') => {
current_part.push(c);
escaping = false;
}
(true, _) => return Err(KeyParseError::InvalidFormat),
(false, '\\') => {
escaping = true;
}
(false, '/') => {
parts.push(current_part);
current_part = String::new();
}
(false, _) => {
current_part.push(c);
}
}
}
if escaping {
return Err(KeyParseError::InvalidFormat);
}
if !current_part.is_empty() {
parts.push(current_part);
}
if parts.len() != 4 {
return Err(KeyParseError::InvalidFormat);
}
if !parts[0].is_empty() {
return Err(KeyParseError::InvalidFormat);
}
if parts[2].is_empty() || parts[3].is_empty() {
return Err(KeyParseError::InvalidFormat);
}
let namespace = parts[1].parse()?;
Ok(Key {
namespace,
table: parts[2].to_string(),
row_key: parts[3].to_string(),
})
}
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
Get { key: Key },
Set { key: Key, value: String },
Delete { key: Key },
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum ClientResponse {
Get { key: Key, value: HashSet<String> },
Set { success: bool },
Delete { success: bool },
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum GossipMessage {
Gossip {
message_id: String,
member_id: String,
writes: Namespaces<Clock>,
},
Ack {
message_id: String,
member_id: String,
},
Nack {
message_id: String,
member_id: String,
},
}
#[cfg(test)]
mod tests {
use super::{Key, Namespace};
#[test]
fn test_key_parsing_sys_namespace() {
let first = "/sys/sys_table/sys_row".parse::<Key>().unwrap();
assert_eq!(first.namespace, Namespace::System);
assert_eq!(first.table, "sys_table");
assert_eq!(first.row_key, "sys_row");
}
#[test]
fn test_key_parsing_user_namespace() {
let second = "/usr/usr_table/usr_row".parse::<Key>().unwrap();
assert_eq!(second.namespace, Namespace::User);
assert_eq!(second.table, "usr_table");
assert_eq!(second.row_key, "usr_row");
}
#[test]
fn test_key_empty_table() {
let empty_table = "/usr//usr_row".parse::<Key>();
assert!(empty_table.is_err());
assert_eq!(
empty_table.unwrap_err(),
super::KeyParseError::InvalidFormat
);
}
#[test]
fn test_key_empty_row() {
let empty_row = "/usr/usr_table/".parse::<Key>();
assert!(empty_row.is_err());
assert_eq!(empty_row.unwrap_err(), super::KeyParseError::InvalidFormat);
}
#[test]
fn test_key_parsing_invalid_namespace() {
let non_existent_namespace = "/ne_namespace/ne_table/ne_row".parse::<Key>();
assert!(non_existent_namespace.is_err());
assert_eq!(
non_existent_namespace.unwrap_err(),
super::KeyParseError::InvalidNamespace
);
}
#[test]
fn test_key_parsing_invalid_format() {
let invalid_format = "/not_even_a_key".parse::<Key>();
assert!(invalid_format.is_err());
assert_eq!(
invalid_format.unwrap_err(),
super::KeyParseError::InvalidFormat
);
let invalid_format = "abcd/sys/sys_table/sys_row".parse::<Key>();
assert!(invalid_format.is_err());
assert_eq!(
invalid_format.unwrap_err(),
super::KeyParseError::InvalidFormat
);
}
#[test]
fn test_key_parsing_escaping() {
let key = r"/usr/usr\/table/usr\/row".parse::<Key>().unwrap();
assert_eq!(key.namespace, Namespace::User);
assert_eq!(key.table, r"usr/table");
assert_eq!(key.row_key, r"usr/row");
let key = r"/usr/usr\\table/usr\\row".parse::<Key>().unwrap();
assert_eq!(key.namespace, Namespace::User);
assert_eq!(key.table, r"usr\table");
assert_eq!(key.row_key, r"usr\row");
let key = r"/usr/usr\table/usr\row".parse::<Key>();
assert!(key.is_err());
assert_eq!(key.unwrap_err(), super::KeyParseError::InvalidFormat);
let key = r"/usr/usr_table/usr_row\".parse::<Key>();
assert!(key.is_err());
assert_eq!(key.unwrap_err(), super::KeyParseError::InvalidFormat);
}
}