use std::borrow::Cow;
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Clone, Debug)]
pub struct Role(Cow<'static, str>);
impl Role {
pub const fn admin() -> Self {
Self(Cow::Borrowed("admin"))
}
pub const fn staff() -> Self {
Self(Cow::Borrowed("staff"))
}
pub const fn user() -> Self {
Self(Cow::Borrowed("user"))
}
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self(name.into())
}
pub fn named(name: impl Into<String>) -> Self {
Self(Cow::Owned(name.into()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl PartialEq for Role {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for Role {}
impl Hash for Role {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl Serialize for Role {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for Role {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(Role::named(value))
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Permission(String);
impl Permission {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl From<&str> for Permission {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl From<String> for Permission {
fn from(value: String) -> Self {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn role_constructors_compare_by_string() {
assert_eq!(Role::admin(), Role::named("admin"));
assert_eq!(Role::admin(), Role::new("admin"));
assert_ne!(Role::admin(), Role::user());
assert_eq!(Role::admin().as_str(), "admin");
}
#[test]
fn role_deserialization_does_not_promote_to_privileged_variant() {
let role: Role = serde_json::from_str(r#""admin""#).unwrap();
assert_eq!(role, Role::admin());
assert_eq!(role.as_str(), "admin");
let custom: Role = serde_json::from_str(r#""editor""#).unwrap();
assert_eq!(custom.as_str(), "editor");
assert_ne!(custom, Role::admin());
}
}