1#![allow(clippy::use_self)]
18
19use super::models;
20use cedar_policy_core::ast;
21use cedar_policy_validator::types;
22use nonempty::NonEmpty;
23use smol_str::SmolStr;
24use std::collections::{BTreeMap, HashMap};
25
26impl From<&cedar_policy_validator::ValidatorSchema> for models::Schema {
27 fn from(v: &cedar_policy_validator::ValidatorSchema) -> Self {
28 Self {
29 entity_decls: v.entity_types().map(models::EntityDecl::from).collect(),
30 action_decls: v.action_ids().map(models::ActionDecl::from).collect(),
31 }
32 }
33}
34
35impl From<&models::Schema> for cedar_policy_validator::ValidatorSchema {
36 #[allow(clippy::expect_used)]
38 fn from(v: &models::Schema) -> Self {
39 Self::new(
40 v.entity_decls
41 .iter()
42 .map(cedar_policy_validator::ValidatorEntityType::from),
43 v.action_decls
44 .iter()
45 .map(cedar_policy_validator::ValidatorActionId::from),
46 )
47 }
48}
49
50impl From<&cedar_policy_validator::ValidationMode> for models::ValidationMode {
51 #[allow(clippy::unimplemented)]
53 fn from(v: &cedar_policy_validator::ValidationMode) -> Self {
54 match v {
55 cedar_policy_validator::ValidationMode::Strict => models::ValidationMode::Strict,
56 cedar_policy_validator::ValidationMode::Permissive => {
57 models::ValidationMode::Permissive
58 }
59 #[cfg(feature = "partial-validate")]
60 cedar_policy_validator::ValidationMode::Partial => unimplemented!(),
61 }
62 }
63}
64
65impl From<&models::ValidationMode> for cedar_policy_validator::ValidationMode {
66 fn from(v: &models::ValidationMode) -> Self {
67 match v {
68 models::ValidationMode::Strict => cedar_policy_validator::ValidationMode::Strict,
69 models::ValidationMode::Permissive => {
70 cedar_policy_validator::ValidationMode::Permissive
71 }
72 }
73 }
74}
75
76#[allow(clippy::fallible_impl_from)]
78impl From<&cedar_policy_validator::ValidatorActionId> for models::ActionDecl {
79 #[allow(clippy::panic)]
81 fn from(v: &cedar_policy_validator::ValidatorActionId) -> Self {
82 debug_assert_eq!(
83 v.attribute_types().keys().collect::<Vec<&SmolStr>>(),
84 Vec::<&SmolStr>::new(),
85 "action attributes are not currently supported in protobuf"
86 );
87 debug_assert_eq!(
88 v.attributes().collect::<Vec<_>>(),
89 vec![],
90 "action attributes are not currently supported in protobuf"
91 );
92 let ctx_attrs = match v.context() {
93 types::Type::EntityOrRecord(types::EntityRecordKind::Record {
94 attrs,
95 open_attributes: types::OpenTag::ClosedAttributes,
96 }) => attrs,
97 ty => panic!("expected context to be a closed record, but got {ty:?}"),
98 };
99 Self {
100 name: Some(models::EntityUid::from(v.name())),
101 principal_types: v.applies_to_principals().map(models::Name::from).collect(),
102 resource_types: v.applies_to_resources().map(models::Name::from).collect(),
103 descendants: v.descendants().map(models::EntityUid::from).collect(),
104 context: attributes_to_model(ctx_attrs),
105 }
106 }
107}
108
109impl From<&models::ActionDecl> for cedar_policy_validator::ValidatorActionId {
110 #[allow(clippy::expect_used)]
112 fn from(v: &models::ActionDecl) -> Self {
113 Self::new(
114 ast::EntityUID::from(v.name.as_ref().expect("name field should exist")),
115 v.principal_types.iter().map(ast::EntityType::from),
116 v.resource_types.iter().map(ast::EntityType::from),
117 v.descendants.iter().map(ast::EntityUID::from),
118 types::Type::EntityOrRecord(types::EntityRecordKind::Record {
119 attrs: model_to_attributes(&v.context),
120 open_attributes: types::OpenTag::default(),
121 }),
122 types::Attributes::with_attributes([]),
125 BTreeMap::new(),
126 None,
127 )
128 }
129}
130
131impl From<&cedar_policy_validator::ValidatorEntityType> for models::EntityDecl {
132 fn from(v: &cedar_policy_validator::ValidatorEntityType) -> Self {
133 let name = Some(models::Name::from(v.name()));
134 let descendants = v.descendants.iter().map(models::Name::from).collect();
135 let attributes = attributes_to_model(v.attributes());
136 let tags = v.tag_type().map(models::Type::from);
137 match &v.kind {
138 cedar_policy_validator::ValidatorEntityTypeKind::Standard(_) => Self {
139 name,
140 descendants,
141 attributes,
142 tags,
143 enum_choices: vec![],
144 },
145 cedar_policy_validator::ValidatorEntityTypeKind::Enum(enum_choices) => Self {
146 name,
147 descendants,
148 attributes,
149 tags,
150 enum_choices: enum_choices.into_iter().map(ToString::to_string).collect(),
151 },
152 }
153 }
154}
155
156impl From<&models::EntityDecl> for cedar_policy_validator::ValidatorEntityType {
157 #[allow(clippy::expect_used)]
159 fn from(v: &models::EntityDecl) -> Self {
160 let name = ast::EntityType::from(v.name.as_ref().expect("name field should exist"));
161 let descendants = v.descendants.iter().map(ast::EntityType::from);
162 match NonEmpty::collect(v.enum_choices.iter().map(SmolStr::from)) {
163 None => Self::new_standard(
165 name,
166 descendants,
167 model_to_attributes(&v.attributes),
168 types::OpenTag::default(),
169 v.tags.as_ref().map(types::Type::from),
170 None,
171 ),
172 Some(enum_choices) => {
173 assert_eq!(&v.attributes, &HashMap::new());
176 assert_eq!(&v.tags, &None);
177 Self::new_enum(name.clone(), descendants, enum_choices, name.loc().cloned())
178 }
179 }
180 }
181}
182
183impl From<&models::Type> for types::Type {
184 #[allow(clippy::expect_used)]
186 fn from(v: &models::Type) -> Self {
187 match v.data.as_ref().expect("data field should exist") {
188 models::r#type::Data::Prim(vt) => {
189 match models::r#type::Prim::try_from(vt.to_owned()).expect("decode should succeed")
190 {
191 models::r#type::Prim::Bool => types::Type::primitive_boolean(),
192 models::r#type::Prim::String => types::Type::primitive_string(),
193 models::r#type::Prim::Long => types::Type::primitive_long(),
194 }
195 }
196 models::r#type::Data::SetElem(elty) => types::Type::Set {
197 element_type: Some(Box::new(types::Type::from(elty.as_ref()))),
198 },
199 models::r#type::Data::Entity(e) => {
200 types::Type::EntityOrRecord(types::EntityRecordKind::Entity(
201 types::EntityLUB::single_entity(ast::EntityType::from(e)),
202 ))
203 }
204 models::r#type::Data::Record(r) => {
205 types::Type::EntityOrRecord(types::EntityRecordKind::Record {
206 attrs: model_to_attributes(&r.attrs),
207 open_attributes: types::OpenTag::default(),
208 })
209 }
210 models::r#type::Data::Ext(name) => types::Type::ExtensionType {
211 name: ast::Name::from(name),
212 },
213 }
214 }
215}
216
217#[allow(clippy::fallible_impl_from)]
219impl From<&types::Type> for models::Type {
220 #[allow(clippy::expect_used, clippy::panic)]
222 fn from(v: &types::Type) -> Self {
223 match v {
224 types::Type::Never => panic!("can't encode Never type in protobuf; Never should never appear in a Schema"),
225 types::Type::True | types::Type::False => panic!("can't encode singleton boolean type in protobuf; singleton boolean types should never appear in a Schema"),
226 types::Type::Primitive { primitive_type } => match primitive_type {
227 types::Primitive::Bool => Self {
228 data: Some(models::r#type::Data::Prim(models::r#type::Prim::Bool.into())),
229 },
230 types::Primitive::Long => Self {
231 data: Some(models::r#type::Data::Prim(models::r#type::Prim::Long.into())),
232 },
233 types::Primitive::String => Self {
234 data: Some(models::r#type::Data::Prim(models::r#type::Prim::String.into())),
235 },
236 },
237 types::Type::Set { element_type } => Self {
238 data: Some(models::r#type::Data::SetElem(Box::new(models::Type::from(
239 element_type
240 .as_ref()
241 .expect("can't encode Set without element type in protobuf; Set-without-element-type should never appear in a Schema")
242 .as_ref(),
243 )))),
244 },
245 types::Type::EntityOrRecord(types::EntityRecordKind::Entity(lub)) => Self {
246 data: Some(models::r#type::Data::Entity(models::Name::from(lub.get_single_entity().expect("can't encode non-singleton LUB in protobuf; non-singleton LUB types should never appear in a Schema").as_ref()))),
247 },
248 types::Type::EntityOrRecord(types::EntityRecordKind::Record { attrs, open_attributes }) => {
249 assert_eq!(open_attributes, &types::OpenTag::ClosedAttributes, "can't encode open record in protobuf");
250 Self {
251 data: Some(models::r#type::Data::Record(models::r#type::Record { attrs: attributes_to_model(attrs) })),
252 }
253 }
254 types::Type::EntityOrRecord(types::EntityRecordKind::ActionEntity { name, attrs }) => {
255 debug_assert_eq!(attrs.keys().collect::<Vec<&SmolStr>>(), Vec::<&SmolStr>::new(), "can't encode action attributes in protobuf");
256 Self {
257 data: Some(models::r#type::Data::Entity(models::Name::from(name.as_ref()))),
258 }
259 }
260 types::Type::EntityOrRecord(types::EntityRecordKind::AnyEntity) => panic!("can't encode AnyEntity type in protobuf; AnyEntity should never appear in a Schema"),
261 types::Type::ExtensionType { name } => Self {
262 data: Some(models::r#type::Data::Ext(models::Name::from(name))),
263 },
264 }
265 }
266}
267
268fn model_to_attributes(v: &HashMap<String, models::AttributeType>) -> types::Attributes {
269 types::Attributes::with_attributes(v.iter().map(|(k, v)| (k.into(), v.into())))
270}
271
272fn attributes_to_model(v: &types::Attributes) -> HashMap<String, models::AttributeType> {
273 v.iter()
274 .map(|(k, v)| (k.to_string(), models::AttributeType::from(v)))
275 .collect()
276}
277
278impl From<&models::AttributeType> for types::AttributeType {
279 #[allow(clippy::expect_used)]
281 fn from(v: &models::AttributeType) -> Self {
282 Self {
283 attr_type: types::Type::from(
284 v.attr_type.as_ref().expect("attr_type field should exist"),
285 ),
286 is_required: v.is_required,
287 #[cfg(feature = "extended-schema")]
288 loc: None,
289 }
290 }
291}
292
293impl From<&types::AttributeType> for models::AttributeType {
294 fn from(v: &types::AttributeType) -> Self {
295 Self {
296 attr_type: Some(models::Type::from(&v.attr_type)),
297 is_required: v.is_required,
298 }
299 }
300}