use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TenantId(String);
impl TenantId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
pub fn is_valid(&self) -> bool {
!self.0.is_empty()
&& self.0.len() <= 255
&& self
.0
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '_')
}
}
impl fmt::Display for TenantId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl AsRef<str> for TenantId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl From<String> for TenantId {
fn from(s: String) -> Self {
Self(s)
}
}
impl From<&str> for TenantId {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
impl From<TenantId> for String {
fn from(tenant_id: TenantId) -> Self {
tenant_id.0
}
}
impl PartialEq<str> for TenantId {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl PartialEq<&str> for TenantId {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl PartialEq<String> for TenantId {
fn eq(&self, other: &String) -> bool {
&self.0 == other
}
}
impl PartialEq<TenantId> for String {
fn eq(&self, other: &TenantId) -> bool {
self == &other.0
}
}
impl PartialEq<TenantId> for &str {
fn eq(&self, other: &TenantId) -> bool {
*self == other.0.as_str()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tenant_id_creation() {
let tenant_id = TenantId::new("tenant_123");
assert_eq!(tenant_id.as_str(), "tenant_123");
assert_eq!(tenant_id.to_string(), "tenant_123");
}
#[test]
fn test_tenant_id_clone() {
let tenant_id = TenantId::new("tenant_123");
let cloned = tenant_id.clone();
assert_eq!(tenant_id, cloned);
}
#[test]
fn test_tenant_id_from_string() {
let s = String::from("tenant_123");
let tenant_id: TenantId = s.into();
assert_eq!(tenant_id.as_str(), "tenant_123");
}
#[test]
fn test_tenant_id_from_str() {
let tenant_id: TenantId = "tenant_123".into();
assert_eq!(tenant_id.as_str(), "tenant_123");
}
#[test]
fn test_tenant_id_into_string() {
let tenant_id = TenantId::new("tenant_123");
let s: String = tenant_id.into();
assert_eq!(s, "tenant_123");
}
#[test]
fn test_tenant_id_validation() {
assert!(TenantId::new("tenant_123").is_valid());
assert!(TenantId::new("tenant-abc-123").is_valid());
assert!(TenantId::new("a").is_valid());
assert!(TenantId::new("123").is_valid());
assert!(TenantId::new("tenant_with_underscores").is_valid());
assert!(TenantId::new("tenant-with-hyphens").is_valid());
assert!(!TenantId::new("").is_valid());
assert!(!TenantId::new("tenant with spaces").is_valid());
assert!(!TenantId::new("tenant@example.com").is_valid());
assert!(!TenantId::new("tenant/path").is_valid());
assert!(!TenantId::new("x".repeat(256)).is_valid());
}
#[test]
fn test_tenant_id_equality() {
let tenant_id1 = TenantId::new("tenant_123");
let tenant_id2 = TenantId::new("tenant_123");
let tenant_id3 = TenantId::new("tenant_456");
assert_eq!(tenant_id1, tenant_id2);
assert_ne!(tenant_id1, tenant_id3);
}
#[test]
fn test_tenant_id_serialization() {
let tenant_id = TenantId::new("tenant_123");
let json = serde_json::to_string(&tenant_id).unwrap();
assert_eq!(json, r#""tenant_123""#);
let deserialized: TenantId = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, tenant_id);
}
#[test]
fn test_tenant_id_hash() {
use std::collections::HashMap;
let mut map = HashMap::new();
let tenant_id = TenantId::new("tenant_123");
map.insert(tenant_id.clone(), "value");
assert_eq!(map.get(&tenant_id), Some(&"value"));
}
#[test]
fn test_tenant_id_string_comparison() {
let tenant_id = TenantId::new("tenant_123");
let string = String::from("tenant_123");
let str_ref = "tenant_123";
assert_eq!(tenant_id, string);
assert_eq!(string, tenant_id);
assert_eq!(tenant_id, str_ref);
assert_eq!(str_ref, tenant_id);
assert!(tenant_id == *str_ref);
assert_ne!(tenant_id, "different_tenant");
assert_ne!(String::from("different_tenant"), tenant_id);
}
}