use std::str::FromStr;
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Scope {
#[default]
None,
User(String),
Tenant(String),
}
impl Scope {
pub fn none() -> Self {
Scope::None
}
pub fn user(id: impl Into<String>) -> Self {
Scope::User(id.into())
}
pub fn user_id(id: i64) -> Self {
Scope::User(id.to_string())
}
pub fn tenant(name: impl Into<String>) -> Self {
Scope::Tenant(name.into())
}
pub fn id(&self) -> &str {
match self {
Scope::None => "",
Scope::User(id) | Scope::Tenant(id) => id,
}
}
pub fn id_as<T: FromStr>(&self) -> Option<T> {
self.id().parse().ok()
}
pub fn kind(&self) -> &'static str {
match self {
Scope::None => "global",
Scope::User(_) => "user",
Scope::Tenant(_) => "tenant",
}
}
pub fn is_none(&self) -> bool {
matches!(self, Scope::None)
}
pub fn key(&self) -> String {
match self {
Scope::None => "global".to_string(),
Scope::User(id) => format!("user:{id}"),
Scope::Tenant(id) => format!("tenant:{id}"),
}
}
}
impl std::fmt::Display for Scope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.key())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_scope_carries_its_identifier_and_its_kind() {
assert_eq!(Scope::user("ada").kind(), "user");
assert_eq!(Scope::user("ada").id(), "ada");
assert_eq!(Scope::tenant("acme").kind(), "tenant");
assert_eq!(Scope::none().kind(), "global");
assert!(Scope::none().is_none());
assert!(!Scope::user("ada").is_none());
}
#[test]
fn a_numeric_identifier_survives_the_round_trip() {
assert_eq!(Scope::user_id(41).id(), "41");
assert_eq!(Scope::user_id(41).id_as::<i64>(), Some(41));
assert_eq!(Scope::user("ada").id_as::<i64>(), None);
assert_eq!(Scope::none().id_as::<i64>(), None);
}
#[test]
fn the_kind_is_part_of_the_key_so_two_namespaces_cannot_collide() {
assert_ne!(Scope::user("acme").key(), Scope::tenant("acme").key());
assert_eq!(Scope::user("acme").key(), "user:acme");
assert_eq!(Scope::none().key(), "global");
assert_eq!(Scope::none().to_string(), "global");
}
#[test]
fn the_default_scope_is_the_global_one() {
assert_eq!(Scope::default(), Scope::none());
}
}