use alloc::{collections::BTreeMap, string::String, vec::Vec};
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum GuardError {
#[error("{0}")]
Input(String),
#[error("{0}")]
Predicate(String),
#[error("{0}")]
Role(String),
#[cfg(feature = "std")]
#[error("{0}")]
Io(String),
#[cfg(feature = "json")]
#[error("{0}")]
Json(String),
}
#[derive(Clone, Debug, PartialEq)]
pub enum GuardValue {
Null,
Bool(bool),
Integer(i64),
Unsigned(u64),
Float(f64),
String(String),
Array(Vec<Self>),
Object(BTreeMap<String, Self>),
}
impl From<&str> for GuardValue {
fn from(value: &str) -> Self {
Self::String(value.into())
}
}
impl From<String> for GuardValue {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<bool> for GuardValue {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<i64> for GuardValue {
fn from(value: i64) -> Self {
Self::Integer(value)
}
}
impl From<u64> for GuardValue {
fn from(value: u64) -> Self {
Self::Unsigned(value)
}
}
impl GuardValue {
pub(crate) fn as_str(&self) -> Option<&str> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
pub(crate) fn is_true(&self) -> bool {
matches!(self, Self::Bool(true))
}
pub(crate) fn is_false(&self) -> bool {
matches!(self, Self::Bool(false))
}
pub(crate) fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub(crate) fn is_truthy(&self) -> bool {
match self {
Self::Null | Self::Bool(false) => false,
Self::Bool(true) => true,
Self::Integer(value) => *value != 0,
Self::Unsigned(value) => *value != 0,
Self::Float(value) => *value != 0.0,
Self::String(value) => !value.is_empty(),
Self::Array(value) => !value.is_empty(),
Self::Object(value) => !value.is_empty(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Rule {
pub id: String,
pub description: String,
pub subject: String,
pub action: String,
pub effect: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PredicateExample {
pub name: String,
pub kind: String,
pub role: GuardValue,
pub action: String,
pub context: BTreeMap<String, GuardValue>,
pub result: String,
pub halt_code: Option<String>,
pub note: Option<String>,
pub extra: BTreeMap<String, GuardValue>,
}
impl PredicateExample {
pub fn flattened_context(&self) -> BTreeMap<String, GuardValue> {
let mut context = self.extra.clone();
context.extend(self.context.clone());
context
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PredicateDoc {
pub id: String,
pub version: i64,
pub description: String,
pub rules: Vec<Rule>,
pub examples: Vec<PredicateExample>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RoleFact {
pub role: String,
pub write_eligible: bool,
pub dispatchable: bool,
pub capabilities: Vec<String>,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum Decision {
Allow,
Deny,
Unresolved,
}
impl Decision {
pub const fn as_str(self) -> &'static str {
match self {
Self::Allow => "allow",
Self::Deny => "deny",
Self::Unresolved => "unresolved",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Verdict {
pub decision: Decision,
pub predicate: Option<String>,
pub rule: Option<String>,
pub halt_code: Option<String>,
pub reason: Option<String>,
pub missing: Vec<String>,
}
impl Verdict {
pub(crate) fn allow() -> Self {
Self {
decision: Decision::Allow,
predicate: None,
rule: None,
halt_code: None,
reason: None,
missing: Vec::new(),
}
}
pub(crate) fn unresolved(reason: impl Into<String>, missing: &[&str]) -> Self {
Self {
decision: Decision::Unresolved,
predicate: None,
rule: None,
halt_code: None,
reason: Some(reason.into()),
missing: missing.iter().map(|item| String::from(*item)).collect(),
}
}
pub(crate) fn deny(
predicate: impl Into<String>,
rule: impl Into<String>,
halt_code: Option<String>,
reason: impl Into<String>,
) -> Self {
Self {
decision: Decision::Deny,
predicate: Some(predicate.into()),
rule: Some(rule.into()),
halt_code,
reason: Some(reason.into()),
missing: Vec::new(),
}
}
}