use compact_str::CompactString;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Permission(i32);
#[rustfmt::skip]
impl Permission {
pub const READ: Permission = Permission(1);
pub const WRITE: Permission = Permission(2);
pub const CREATE: Permission = Permission(4);
pub const DELETE: Permission = Permission(8);
pub const ADMIN: Permission = Permission(16);
pub const ALL: Permission = Permission(31);
pub const NONE: Permission = Permission(0);
pub(crate) fn into_raw(self) -> i32 {
self.0
}
pub(crate) fn from_raw(raw: i32) -> Permission {
Permission(raw)
}
pub fn has(self, perm: Permission) -> bool {
(self.0 & perm.0) == perm.0
}
}
impl std::ops::BitAnd for Permission {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Permission(self.0 & rhs.0)
}
}
impl std::ops::BitOr for Permission {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Permission(self.0 | rhs.0)
}
}
impl std::fmt::Display for Permission {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if *self == Permission::ALL {
return f.write_str("ALL");
} else if *self == Permission::NONE {
return f.write_str("NONE");
}
[
(Permission::READ, "READ"),
(Permission::WRITE, "WRITE"),
(Permission::CREATE, "CREATE"),
(Permission::DELETE, "DELETE"),
(Permission::ADMIN, "ADMIN"),
]
.into_iter()
.filter_map(|(perm, str)| if self.has(perm) { Some(str) } else { None })
.enumerate()
.try_for_each(|(i, str)| if i == 0 { f.write_str(str) } else { write!(f, "|{}", str) })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Acl {
permission: Permission,
auth_id: AuthId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AuthId {
scheme: CompactString,
id: CompactString,
}
impl AuthId {
pub fn new(scheme: &str, id: &str) -> AuthId {
AuthId { scheme: CompactString::new(scheme), id: CompactString::new(id) }
}
pub const fn new_const(scheme: &'static str, id: &'static str) -> AuthId {
AuthId { scheme: CompactString::new_inline(scheme), id: CompactString::new_inline(id) }
}
pub fn scheme(&self) -> &str {
self.scheme.as_str()
}
pub fn id(&self) -> &str {
self.id.as_str()
}
pub const fn anyone() -> AuthId {
Self::new_const("world", "anyone")
}
pub const fn authed() -> AuthId {
Self::new_const("auth", "")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AuthUser {
scheme: CompactString,
user: CompactString,
}
impl AuthUser {
pub fn new(scheme: &str, user: &str) -> AuthUser {
AuthUser { scheme: CompactString::new(scheme), user: CompactString::new(user) }
}
pub fn scheme(&self) -> &str {
self.scheme.as_str()
}
pub fn user(&self) -> &str {
self.user.as_str()
}
}
static ANYONE_ALL: [Acl; 1] = [Acl::new_const(Permission::ALL, "world", "anyone")];
static ANYONE_READ: [Acl; 1] = [Acl::new_const(Permission::READ, "world", "anyone")];
static CREATOR_ALL: [Acl; 1] = [Acl::new_const(Permission::ALL, "auth", "")];
impl Acl {
pub fn new(permission: Permission, auth_id: AuthId) -> Acl {
Acl { permission, auth_id }
}
pub const fn new_const(permission: Permission, scheme: &'static str, id: &'static str) -> Acl {
Acl {
permission,
auth_id: AuthId { scheme: CompactString::new_inline(scheme), id: CompactString::new_inline(id) },
}
}
pub fn permission(&self) -> Permission {
self.permission
}
pub fn auth_id(&self) -> &AuthId {
&self.auth_id
}
pub fn scheme(&self) -> &str {
self.auth_id.scheme.as_str()
}
pub fn id(&self) -> &str {
self.auth_id.id.as_str()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AclsInner<'a> {
AnyoneAll,
AnyoneRead,
CreatorAll,
Acls { acls: &'a [Acl] },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Acls<'a> {
inner: AclsInner<'a>,
}
impl<'a> Acls<'a> {
pub fn new(acls: &'a [Acl]) -> Self {
Self { inner: AclsInner::Acls { acls } }
}
pub const fn anyone_all() -> Acls<'static> {
Acls { inner: AclsInner::AnyoneAll }
}
pub const fn anyone_read() -> Acls<'static> {
Acls { inner: AclsInner::AnyoneRead }
}
pub const fn creator_all() -> Acls<'static> {
Acls { inner: AclsInner::CreatorAll }
}
}
impl<'a> std::ops::Deref for Acls<'a> {
type Target = [Acl];
fn deref(&self) -> &'a [Acl] {
match self.inner {
AclsInner::AnyoneAll => &ANYONE_ALL,
AclsInner::AnyoneRead => &ANYONE_READ,
AclsInner::CreatorAll => &CREATOR_ALL,
AclsInner::Acls { acls } => acls,
}
}
}
impl<'a> From<&'a [Acl]> for Acls<'a> {
fn from(acls: &'a [Acl]) -> Self {
Self::new(acls)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn permission_test() {
let all = Permission::READ | Permission::WRITE | Permission::CREATE | Permission::DELETE | Permission::ADMIN;
assert!(Permission::ALL.has(all));
assert_eq!(Permission::ALL & Permission::ALL, Permission::ALL);
assert_eq!(Permission::ALL & Permission::READ, Permission::READ);
assert_eq!(Permission::READ & Permission::READ, Permission::READ);
assert_eq!(Permission::CREATE & Permission::READ, Permission::NONE);
}
#[test]
fn permission_display() {
assert_eq!(Permission::ALL.to_string(), "ALL");
assert_eq!(Permission::ADMIN.to_string(), "ADMIN");
let perms = Permission::READ | Permission::WRITE | Permission::CREATE | Permission::DELETE;
assert_eq!(perms.to_string(), "READ|WRITE|CREATE|DELETE");
}
#[test]
fn test_acls() {
assert_eq!(&Acls::anyone_all() as &[Acl], &ANYONE_ALL);
assert_eq!(&Acls::anyone_read() as &[Acl], &ANYONE_READ);
assert_eq!(&Acls::creator_all() as &[Acl], &CREATOR_ALL);
assert_eq!(&Acls::new(&CREATOR_ALL) as &[Acl], &CREATOR_ALL);
}
}