use super::{EstToAstError, InstantiationError};
use crate::ast;
use crate::entities::{EntityUidJSON, JsonDeserializationErrorContext};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(tag = "op")]
pub enum PrincipalConstraint {
All,
#[serde(rename = "==")]
Eq(EqConstraint),
#[serde(rename = "in")]
In(PrincipalOrResourceInConstraint),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(tag = "op")]
pub enum ActionConstraint {
All,
#[serde(rename = "==")]
Eq(EqConstraint),
#[serde(rename = "in")]
In(ActionInConstraint),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(tag = "op")]
pub enum ResourceConstraint {
All,
#[serde(rename = "==")]
Eq(EqConstraint),
#[serde(rename = "in")]
In(PrincipalOrResourceInConstraint),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum EqConstraint {
Entity {
entity: EntityUidJSON,
},
Slot {
slot: ast::SlotId,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PrincipalOrResourceInConstraint {
Entity {
entity: EntityUidJSON,
},
Slot {
slot: ast::SlotId,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ActionInConstraint {
Single {
entity: EntityUidJSON,
},
Set {
entities: Vec<EntityUidJSON>,
},
}
impl PrincipalConstraint {
pub fn instantiate(
self,
vals: &HashMap<ast::SlotId, EntityUidJSON>,
) -> Result<Self, InstantiationError> {
match self {
PrincipalConstraint::All => Ok(PrincipalConstraint::All),
PrincipalConstraint::Eq(EqConstraint::Entity { entity }) => {
Ok(PrincipalConstraint::Eq(EqConstraint::Entity { entity }))
}
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }) => Ok(
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }),
),
PrincipalConstraint::Eq(EqConstraint::Slot { slot }) => match vals.get(&slot) {
Some(val) => Ok(PrincipalConstraint::Eq(EqConstraint::Entity {
entity: val.clone(),
})),
None => Err(InstantiationError::MissedSlot { slot }),
},
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Slot { slot }) => {
match vals.get(&slot) {
Some(val) => Ok(PrincipalConstraint::In(
PrincipalOrResourceInConstraint::Entity {
entity: val.clone(),
},
)),
None => Err(InstantiationError::MissedSlot { slot }),
}
}
}
}
}
impl ResourceConstraint {
pub fn instantiate(
self,
vals: &HashMap<ast::SlotId, EntityUidJSON>,
) -> Result<Self, InstantiationError> {
match self {
ResourceConstraint::All => Ok(ResourceConstraint::All),
ResourceConstraint::Eq(EqConstraint::Entity { entity }) => {
Ok(ResourceConstraint::Eq(EqConstraint::Entity { entity }))
}
ResourceConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }) => Ok(
ResourceConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }),
),
ResourceConstraint::Eq(EqConstraint::Slot { slot }) => match vals.get(&slot) {
Some(val) => Ok(ResourceConstraint::Eq(EqConstraint::Entity {
entity: val.clone(),
})),
None => Err(InstantiationError::MissedSlot { slot }),
},
ResourceConstraint::In(PrincipalOrResourceInConstraint::Slot { slot }) => {
match vals.get(&slot) {
Some(val) => Ok(ResourceConstraint::In(
PrincipalOrResourceInConstraint::Entity {
entity: val.clone(),
},
)),
None => Err(InstantiationError::MissedSlot { slot }),
}
}
}
}
}
impl ActionConstraint {
pub fn instantiate(
self,
_vals: &HashMap<ast::SlotId, EntityUidJSON>,
) -> Result<Self, InstantiationError> {
Ok(self)
}
}
impl From<ast::PrincipalConstraint> for PrincipalConstraint {
fn from(constraint: ast::PrincipalConstraint) -> PrincipalConstraint {
constraint.constraint.into()
}
}
impl TryFrom<PrincipalConstraint> for ast::PrincipalConstraint {
type Error = EstToAstError;
fn try_from(
constraint: PrincipalConstraint,
) -> Result<ast::PrincipalConstraint, EstToAstError> {
constraint.try_into().map(ast::PrincipalConstraint::new)
}
}
impl From<ast::ResourceConstraint> for ResourceConstraint {
fn from(constraint: ast::ResourceConstraint) -> ResourceConstraint {
constraint.constraint.into()
}
}
impl TryFrom<ResourceConstraint> for ast::ResourceConstraint {
type Error = EstToAstError;
fn try_from(constraint: ResourceConstraint) -> Result<ast::ResourceConstraint, EstToAstError> {
constraint.try_into().map(ast::ResourceConstraint::new)
}
}
impl From<ast::PrincipalOrResourceConstraint> for PrincipalConstraint {
fn from(constraint: ast::PrincipalOrResourceConstraint) -> PrincipalConstraint {
match constraint {
ast::PrincipalOrResourceConstraint::Any => PrincipalConstraint::All,
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::EUID(e)) => {
PrincipalConstraint::Eq(EqConstraint::Entity {
entity: EntityUidJSON::ImplicitEntityEscape((&*e).into()),
})
}
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::Slot) => {
PrincipalConstraint::Eq(EqConstraint::Slot {
slot: ast::SlotId::principal(),
})
}
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::EUID(e)) => {
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Entity {
entity: EntityUidJSON::ImplicitEntityEscape((&*e).into()),
})
}
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::Slot) => {
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Slot {
slot: ast::SlotId::principal(),
})
}
}
}
}
impl From<ast::PrincipalOrResourceConstraint> for ResourceConstraint {
fn from(constraint: ast::PrincipalOrResourceConstraint) -> ResourceConstraint {
match constraint {
ast::PrincipalOrResourceConstraint::Any => ResourceConstraint::All,
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::EUID(e)) => {
ResourceConstraint::Eq(EqConstraint::Entity {
entity: EntityUidJSON::ImplicitEntityEscape((&*e).into()),
})
}
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::Slot) => {
ResourceConstraint::Eq(EqConstraint::Slot {
slot: ast::SlotId::resource(),
})
}
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::EUID(e)) => {
ResourceConstraint::In(PrincipalOrResourceInConstraint::Entity {
entity: EntityUidJSON::ImplicitEntityEscape((&*e).into()),
})
}
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::Slot) => {
ResourceConstraint::In(PrincipalOrResourceInConstraint::Slot {
slot: ast::SlotId::resource(),
})
}
}
}
}
impl TryFrom<PrincipalConstraint> for ast::PrincipalOrResourceConstraint {
type Error = EstToAstError;
fn try_from(
constraint: PrincipalConstraint,
) -> Result<ast::PrincipalOrResourceConstraint, EstToAstError> {
match constraint {
PrincipalConstraint::All => Ok(ast::PrincipalOrResourceConstraint::Any),
PrincipalConstraint::Eq(EqConstraint::Entity { entity }) => Ok(
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::EUID(Arc::new(
entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?,
))),
),
PrincipalConstraint::Eq(EqConstraint::Slot { slot }) => {
if slot == ast::SlotId::principal() {
Ok(ast::PrincipalOrResourceConstraint::Eq(
ast::EntityReference::Slot,
))
} else {
Err(EstToAstError::InvalidSlotName)
}
}
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }) => Ok(
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::EUID(Arc::new(
entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?,
))),
),
PrincipalConstraint::In(PrincipalOrResourceInConstraint::Slot { slot }) => {
if slot == ast::SlotId::principal() {
Ok(ast::PrincipalOrResourceConstraint::In(
ast::EntityReference::Slot,
))
} else {
Err(EstToAstError::InvalidSlotName)
}
}
}
}
}
impl TryFrom<ResourceConstraint> for ast::PrincipalOrResourceConstraint {
type Error = EstToAstError;
fn try_from(
constraint: ResourceConstraint,
) -> Result<ast::PrincipalOrResourceConstraint, EstToAstError> {
match constraint {
ResourceConstraint::All => Ok(ast::PrincipalOrResourceConstraint::Any),
ResourceConstraint::Eq(EqConstraint::Entity { entity }) => Ok(
ast::PrincipalOrResourceConstraint::Eq(ast::EntityReference::EUID(Arc::new(
entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?,
))),
),
ResourceConstraint::Eq(EqConstraint::Slot { slot }) => {
if slot == ast::SlotId::resource() {
Ok(ast::PrincipalOrResourceConstraint::Eq(
ast::EntityReference::Slot,
))
} else {
Err(EstToAstError::InvalidSlotName)
}
}
ResourceConstraint::In(PrincipalOrResourceInConstraint::Entity { entity }) => Ok(
ast::PrincipalOrResourceConstraint::In(ast::EntityReference::EUID(Arc::new(
entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?,
))),
),
ResourceConstraint::In(PrincipalOrResourceInConstraint::Slot { slot }) => {
if slot == ast::SlotId::resource() {
Ok(ast::PrincipalOrResourceConstraint::In(
ast::EntityReference::Slot,
))
} else {
Err(EstToAstError::InvalidSlotName)
}
}
}
}
}
impl From<ast::ActionConstraint> for ActionConstraint {
fn from(constraint: ast::ActionConstraint) -> ActionConstraint {
match constraint {
ast::ActionConstraint::Any => ActionConstraint::All,
ast::ActionConstraint::Eq(e) => ActionConstraint::Eq(EqConstraint::Entity {
entity: EntityUidJSON::ImplicitEntityEscape((&*e).into()),
}),
ast::ActionConstraint::In(es) => match &es[..] {
[e] => ActionConstraint::In(ActionInConstraint::Single {
entity: EntityUidJSON::ImplicitEntityEscape((&**e).into()),
}),
es => ActionConstraint::In(ActionInConstraint::Set {
entities: es
.iter()
.map(|e| EntityUidJSON::ImplicitEntityEscape((&**e).into()))
.collect(),
}),
},
}
}
}
impl TryFrom<ActionConstraint> for ast::ActionConstraint {
type Error = EstToAstError;
fn try_from(constraint: ActionConstraint) -> Result<ast::ActionConstraint, EstToAstError> {
match constraint {
ActionConstraint::All => Ok(ast::ActionConstraint::Any),
ActionConstraint::Eq(EqConstraint::Entity { entity }) => Ok(ast::ActionConstraint::Eq(
Arc::new(entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?),
)),
ActionConstraint::Eq(EqConstraint::Slot { .. }) => Err(EstToAstError::ActionSlot),
ActionConstraint::In(ActionInConstraint::Single { entity }) => {
Ok(ast::ActionConstraint::In(vec![Arc::new(
entity.into_euid(|| JsonDeserializationErrorContext::EntityUid)?,
)]))
}
ActionConstraint::In(ActionInConstraint::Set { entities }) => {
Ok(ast::ActionConstraint::In(
entities
.into_iter()
.map(|e| {
e.into_euid(|| JsonDeserializationErrorContext::EntityUid)
.map(Arc::new)
})
.collect::<Result<Vec<_>, _>>()?,
))
}
}
}
}