use std::collections::HashSet;
pub const WILDCARD: &str = "*";
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CapabilitySet(HashSet<String>);
impl CapabilitySet {
pub fn new() -> Self {
Self(HashSet::new())
}
pub fn wildcard() -> Self {
let mut set = HashSet::new();
set.insert(WILDCARD.to_string());
Self(set)
}
pub fn satisfies(&self, required: &str) -> bool {
self.0.contains(WILDCARD) || self.0.contains(required)
}
pub fn is_wildcard(&self) -> bool {
self.0.contains(WILDCARD)
}
pub fn insert(&mut self, capability: impl Into<String>) {
self.0.insert(capability.into());
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &String> {
self.0.iter()
}
}
impl<S: Into<String>> FromIterator<S> for CapabilitySet {
fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
Self(iter.into_iter().map(Into::into).collect())
}
}
pub fn preset(name: &str) -> Option<CapabilitySet> {
match name {
"operator" => Some(
["exec", "session.read", "session.manage"]
.into_iter()
.collect(),
),
"read-only" => Some(["session.read"].into_iter().collect()),
"full-control" => Some(CapabilitySet::wildcard()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_set_satisfies_nothing() {
let set = CapabilitySet::new();
assert!(set.is_empty());
assert!(!set.satisfies("exec"));
assert!(!set.is_wildcard());
}
#[test]
fn test_wildcard_satisfies_everything() {
let set = CapabilitySet::wildcard();
assert!(set.is_wildcard());
assert!(set.satisfies("exec"));
assert!(set.satisfies("session.read"));
assert!(set.satisfies("anything.at.all"));
}
#[test]
fn test_membership_is_exact() {
let set: CapabilitySet = ["session.read"].into_iter().collect();
assert!(set.satisfies("session.read"));
assert!(!set.satisfies("session.manage"));
assert!(!set.satisfies("session"));
assert!(!set.is_wildcard());
}
#[test]
fn test_multiple_capabilities() {
let set: CapabilitySet = ["exec", "session.read", "session.manage"]
.into_iter()
.collect();
assert_eq!(set.len(), 3);
assert!(set.satisfies("exec"));
assert!(set.satisfies("session.read"));
assert!(set.satisfies("session.manage"));
assert!(!set.satisfies("fs.read"));
}
#[test]
fn test_insert() {
let mut set = CapabilitySet::new();
set.insert("exec");
assert!(set.satisfies("exec"));
assert_eq!(set.len(), 1);
}
#[test]
fn test_presets() {
let operator = preset("operator").unwrap();
assert!(operator.satisfies("exec"));
assert!(operator.satisfies("session.read"));
assert!(operator.satisfies("session.manage"));
assert!(!operator.is_wildcard());
let read_only = preset("read-only").unwrap();
assert!(read_only.satisfies("session.read"));
assert!(!read_only.satisfies("session.manage"));
assert!(!read_only.satisfies("exec"));
let full = preset("full-control").unwrap();
assert!(full.is_wildcard());
assert!(full.satisfies("anything"));
assert!(preset("nonexistent").is_none());
}
}