use crate::ast::{BorrowedRestrictedExpr, EntityUID, ExprKind, RestrictedExpr};
use crate::entities::{ContextJsonParser, JsonDeserializationError, NullContextSchema};
use crate::extensions::Extensions;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::sync::Arc;
use super::{Expr, Literal, PartialValue, Value, Var};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Request {
pub(crate) principal: EntityUIDEntry,
pub(crate) action: EntityUIDEntry,
pub(crate) resource: EntityUIDEntry,
pub(crate) context: Option<Context>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EntityUIDEntry {
Concrete(Arc<EntityUID>),
Unknown,
}
impl EntityUIDEntry {
pub fn evaluate(&self, var: Var) -> PartialValue {
match self {
EntityUIDEntry::Concrete(euid) => Value::Lit(Literal::EntityUID(euid.clone())).into(),
EntityUIDEntry::Unknown => Expr::unknown(var.to_string()).into(),
}
}
pub fn concrete(euid: EntityUID) -> Self {
Self::Concrete(Arc::new(euid))
}
}
impl Request {
pub fn new(
principal: EntityUID,
action: EntityUID,
resource: EntityUID,
context: Context,
) -> Self {
Self {
principal: EntityUIDEntry::concrete(principal),
action: EntityUIDEntry::concrete(action),
resource: EntityUIDEntry::concrete(resource),
context: Some(context),
}
}
pub fn new_with_unknowns(
principal: EntityUIDEntry,
action: EntityUIDEntry,
resource: EntityUIDEntry,
context: Option<Context>,
) -> Self {
Self {
principal,
action,
resource,
context,
}
}
pub fn principal(&self) -> &EntityUIDEntry {
&self.principal
}
pub fn action(&self) -> &EntityUIDEntry {
&self.action
}
pub fn resource(&self) -> &EntityUIDEntry {
&self.resource
}
pub fn context(&self) -> Option<&Context> {
self.context.as_ref()
}
}
impl std::fmt::Display for Request {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let display_euid = |maybe_euid: &EntityUIDEntry| match maybe_euid {
EntityUIDEntry::Concrete(euid) => format!("{euid}"),
EntityUIDEntry::Unknown => "unknown".to_string(),
};
write!(
f,
"request with principal {}, action {}, resource {}, and context {}",
display_euid(&self.principal),
display_euid(&self.action),
display_euid(&self.resource),
match &self.context {
Some(x) => format!("{x}"),
None => "unknown".to_string(),
}
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Context {
#[serde(flatten)]
context: RestrictedExpr,
}
impl Context {
pub fn empty() -> Self {
Self::from_pairs([])
}
pub fn from_expr(expr: RestrictedExpr) -> Self {
debug_assert!(matches!(expr.expr_kind(), ExprKind::Record { .. }));
Self { context: expr }
}
pub fn from_pairs(pairs: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>) -> Self {
Self {
context: RestrictedExpr::record(pairs),
}
}
pub fn from_json_str(json: &str) -> Result<Self, JsonDeserializationError> {
ContextJsonParser::new(None::<&NullContextSchema>, Extensions::all_available())
.from_json_str(json)
}
pub fn from_json_value(json: serde_json::Value) -> Result<Self, JsonDeserializationError> {
ContextJsonParser::new(None::<&NullContextSchema>, Extensions::all_available())
.from_json_value(json)
}
pub fn from_json_file(json: impl std::io::Read) -> Result<Self, JsonDeserializationError> {
ContextJsonParser::new(None::<&NullContextSchema>, Extensions::all_available())
.from_json_file(json)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, BorrowedRestrictedExpr<'_>)> {
match self.context.as_ref().expr_kind() {
ExprKind::Record { pairs } => pairs
.iter()
.map(|(k, v)| (k.as_str(), BorrowedRestrictedExpr::new_unchecked(v))), e => panic!("internal invariant violation: expected Expr::Record, got {e:?}"),
}
}
}
impl AsRef<RestrictedExpr> for Context {
fn as_ref(&self) -> &RestrictedExpr {
&self.context
}
}
impl std::default::Default for Context {
fn default() -> Context {
Context::empty()
}
}
impl std::fmt::Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.context)
}
}