scuffle_cedar_policy_codegen/
cedar_namespace.rs

1use std::collections::BTreeMap;
2
3use cedar_policy_core::ast::{Id, UnreservedId};
4use cedar_policy_core::validator::RawName;
5use cedar_policy_core::validator::json_schema::{self, ActionType, CommonType, CommonTypeId, EntityType};
6
7use crate::cedar_action::CedarAction;
8use crate::error::CodegenError;
9use crate::types::{ActionEid, CedarType};
10use crate::utils::convert_cedar_to_rust;
11
12/// Represents a Cedar namespace containing types and actions
13#[derive(Default, Debug)]
14pub(crate) struct CedarNamespace {
15    pub types: BTreeMap<Id, CedarType>,
16    pub actions: BTreeMap<String, CedarAction>,
17}
18
19impl CedarNamespace {
20    /// Handles a common type definition
21    pub(crate) fn handle_common_type(&mut self, id: &CommonTypeId, ty: &CommonType<RawName>) -> Result<(), CodegenError> {
22        let id = id.as_ref().clone().into();
23        if self.types.contains_key(&id) {
24            return Err(CodegenError::DuplicateType(id));
25        }
26
27        self.types.insert(id, convert_cedar_to_rust(&ty.ty)?);
28        Ok(())
29    }
30
31    /// Handles an entity type definition
32    pub(crate) fn handle_entity_type(&mut self, id: &UnreservedId, ty: &EntityType<RawName>) -> Result<(), CodegenError> {
33        let id = id.clone().into();
34        if self.types.contains_key(&id) {
35            return Err(CodegenError::DuplicateType(id));
36        }
37
38        let cedar_type = match &ty.kind {
39            json_schema::EntityTypeKind::Enum { choices } => {
40                CedarType::Enum(choices.iter().map(|c| c.to_string()).collect())
41            }
42            json_schema::EntityTypeKind::Standard(std) => CedarType::Entity {
43                parents: std.member_of_types.iter().cloned().map(Into::into).collect(),
44                tag_type: std.tags.as_ref().map(convert_cedar_to_rust).transpose()?.map(Box::new),
45                shape: Box::new(convert_cedar_to_rust(&std.shape.0)?),
46            },
47        };
48
49        self.types.insert(id, cedar_type);
50        Ok(())
51    }
52
53    /// Handles an action definition
54    pub(crate) fn handle_action(&mut self, action: &str, ty: &ActionType<RawName>) -> Result<(), CodegenError> {
55        if self.actions.contains_key(action) {
56            return Err(CodegenError::DuplicateAction(action.to_string()));
57        }
58
59        let member_of = ty
60            .member_of
61            .as_ref()
62            .map(|m| m.iter())
63            .into_iter()
64            .flatten()
65            .map(|m| ActionEid {
66                id: m.ty.clone().map(Into::into),
67                name: m.id.to_string(),
68            })
69            .collect();
70
71        let mut cedar_action = CedarAction {
72            parents: member_of,
73            ..Default::default()
74        };
75
76        if let Some(applies_to) = &ty.applies_to {
77            cedar_action.context = self.extract_context_type(&applies_to.context.0)?;
78            cedar_action.principals = applies_to.principal_types.iter().cloned().map(Into::into).collect();
79            cedar_action.resources = applies_to.resource_types.iter().cloned().map(Into::into).collect();
80        }
81
82        self.actions.insert(action.to_owned(), cedar_action);
83        Ok(())
84    }
85
86    /// Extracts context type from JSON schema type
87    fn extract_context_type(&self, context_type: &json_schema::Type<RawName>) -> Result<Option<CedarType>, CodegenError> {
88        match context_type {
89            json_schema::Type::Type {
90                ty: json_schema::TypeVariant::Record(r),
91                ..
92            } if !r.additional_attributes && r.attributes.is_empty() => Ok(None),
93            r => Ok(Some(convert_cedar_to_rust(r)?)),
94        }
95    }
96}