1#![allow(clippy::use_self)]
18
19use super::models;
20use cedar_policy_core::ast;
21use cedar_policy_core::validator::types;
22use nonempty::NonEmpty;
23use smol_str::SmolStr;
24use std::collections::HashMap;
25
26impl From<&cedar_policy_core::validator::ValidatorSchema> for models::Schema {
27 fn from(v: &cedar_policy_core::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_core::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_core::validator::ValidatorEntityType::from),
43 v.action_decls
44 .iter()
45 .map(cedar_policy_core::validator::ValidatorActionId::from),
46 )
47 }
48}
49
50impl From<&cedar_policy_core::validator::ValidationMode> for models::ValidationMode {
51 #[allow(clippy::unimplemented)]
53 fn from(v: &cedar_policy_core::validator::ValidationMode) -> Self {
54 match v {
55 cedar_policy_core::validator::ValidationMode::Strict => models::ValidationMode::Strict,
56 cedar_policy_core::validator::ValidationMode::Permissive => {
57 models::ValidationMode::Permissive
58 }
59 #[cfg(feature = "partial-validate")]
60 cedar_policy_core::validator::ValidationMode::Partial => {
61 models::ValidationMode::Partial
62 }
63 }
64 }
65}
66
67impl From<&models::ValidationMode> for cedar_policy_core::validator::ValidationMode {
68 fn from(v: &models::ValidationMode) -> Self {
69 match v {
70 models::ValidationMode::Strict => cedar_policy_core::validator::ValidationMode::Strict,
71 models::ValidationMode::Permissive => {
72 cedar_policy_core::validator::ValidationMode::Permissive
73 }
74 #[cfg(feature = "partial-validate")]
75 models::ValidationMode::Partial => {
76 cedar_policy_core::validator::ValidationMode::Partial
77 }
78 #[cfg(not(feature = "partial-validate"))]
79 models::ValidationMode::Partial => {
80 panic!("Protobuf specifies partial validation, but `partial-validate` feature not enabled in this build")
81 }
82 }
83 }
84}
85
86#[allow(clippy::fallible_impl_from)]
88impl From<&cedar_policy_core::validator::ValidatorActionId> for models::ActionDecl {
89 #[allow(clippy::panic)]
91 fn from(v: &cedar_policy_core::validator::ValidatorActionId) -> Self {
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_core::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 None,
123 )
124 }
125}
126
127impl From<&cedar_policy_core::validator::ValidatorEntityType> for models::EntityDecl {
128 fn from(v: &cedar_policy_core::validator::ValidatorEntityType) -> Self {
129 let name = Some(models::Name::from(v.name()));
130 let descendants = v.descendants.iter().map(models::Name::from).collect();
131 let attributes = attributes_to_model(v.attributes());
132 let tags = v.tag_type().map(models::Type::from);
133 match &v.kind {
134 cedar_policy_core::validator::ValidatorEntityTypeKind::Standard(_) => Self {
135 name,
136 descendants,
137 attributes,
138 tags,
139 enum_choices: vec![],
140 },
141 cedar_policy_core::validator::ValidatorEntityTypeKind::Enum(enum_choices) => Self {
142 name,
143 descendants,
144 attributes,
145 tags,
146 enum_choices: enum_choices.into_iter().map(ToString::to_string).collect(),
147 },
148 }
149 }
150}
151
152impl From<&models::EntityDecl> for cedar_policy_core::validator::ValidatorEntityType {
153 #[allow(clippy::expect_used)]
155 fn from(v: &models::EntityDecl) -> Self {
156 let name = ast::EntityType::from(v.name.as_ref().expect("name field should exist"));
157 let descendants = v.descendants.iter().map(ast::EntityType::from);
158 match NonEmpty::collect(v.enum_choices.iter().map(SmolStr::from)) {
159 None => Self::new_standard(
161 name,
162 descendants,
163 model_to_attributes(&v.attributes),
164 types::OpenTag::default(),
165 v.tags.as_ref().map(types::Type::from),
166 None,
167 ),
168 Some(enum_choices) => {
169 assert_eq!(&v.attributes, &HashMap::new());
172 assert_eq!(&v.tags, &None);
173 Self::new_enum(name.clone(), descendants, enum_choices, name.loc().cloned())
174 }
175 }
176 }
177}
178
179impl From<&models::Type> for types::Type {
180 #[allow(clippy::expect_used)]
182 fn from(v: &models::Type) -> Self {
183 match v.data.as_ref().expect("data field should exist") {
184 models::r#type::Data::Prim(vt) => {
185 match models::r#type::Prim::try_from(vt.to_owned()).expect("decode should succeed")
186 {
187 models::r#type::Prim::Bool => types::Type::primitive_boolean(),
188 models::r#type::Prim::String => types::Type::primitive_string(),
189 models::r#type::Prim::Long => types::Type::primitive_long(),
190 }
191 }
192 models::r#type::Data::SetElem(elty) => types::Type::Set {
193 element_type: Some(Box::new(types::Type::from(elty.as_ref()))),
194 },
195 models::r#type::Data::Entity(e) => {
196 types::Type::EntityOrRecord(types::EntityRecordKind::Entity(
197 types::EntityLUB::single_entity(ast::EntityType::from(e)),
198 ))
199 }
200 models::r#type::Data::Record(r) => {
201 types::Type::EntityOrRecord(types::EntityRecordKind::Record {
202 attrs: model_to_attributes(&r.attrs),
203 open_attributes: types::OpenTag::default(),
204 })
205 }
206 models::r#type::Data::Ext(name) => types::Type::ExtensionType {
207 name: ast::Name::from(name),
208 },
209 }
210 }
211}
212
213#[allow(clippy::fallible_impl_from)]
215impl From<&types::Type> for models::Type {
216 #[allow(clippy::expect_used, clippy::panic)]
218 fn from(v: &types::Type) -> Self {
219 match v {
220 types::Type::Never => panic!("can't encode Never type in protobuf; Never should never appear in a Schema"),
221 types::Type::True | types::Type::False => panic!("can't encode singleton boolean type in protobuf; singleton boolean types should never appear in a Schema"),
222 types::Type::Primitive { primitive_type } => match primitive_type {
223 types::Primitive::Bool => Self {
224 data: Some(models::r#type::Data::Prim(models::r#type::Prim::Bool.into())),
225 },
226 types::Primitive::Long => Self {
227 data: Some(models::r#type::Data::Prim(models::r#type::Prim::Long.into())),
228 },
229 types::Primitive::String => Self {
230 data: Some(models::r#type::Data::Prim(models::r#type::Prim::String.into())),
231 },
232 },
233 types::Type::Set { element_type } => Self {
234 data: Some(models::r#type::Data::SetElem(Box::new(models::Type::from(
235 element_type
236 .as_ref()
237 .expect("can't encode Set without element type in protobuf; Set-without-element-type should never appear in a Schema")
238 .as_ref(),
239 )))),
240 },
241 types::Type::EntityOrRecord(types::EntityRecordKind::Entity(lub)) => Self {
242 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()))),
243 },
244 types::Type::EntityOrRecord(types::EntityRecordKind::Record { attrs, open_attributes }) => {
245 assert_eq!(open_attributes, &types::OpenTag::ClosedAttributes, "can't encode open record in protobuf");
246 Self {
247 data: Some(models::r#type::Data::Record(models::r#type::Record { attrs: attributes_to_model(attrs) })),
248 }
249 }
250 types::Type::EntityOrRecord(types::EntityRecordKind::AnyEntity) => panic!("can't encode AnyEntity type in protobuf; AnyEntity should never appear in a Schema"),
251 types::Type::ExtensionType { name } => Self {
252 data: Some(models::r#type::Data::Ext(models::Name::from(name))),
253 },
254 }
255 }
256}
257
258fn model_to_attributes(v: &HashMap<String, models::AttributeType>) -> types::Attributes {
259 types::Attributes::with_attributes(v.iter().map(|(k, v)| (k.into(), v.into())))
260}
261
262fn attributes_to_model(v: &types::Attributes) -> HashMap<String, models::AttributeType> {
263 v.iter()
264 .map(|(k, v)| (k.to_string(), models::AttributeType::from(v)))
265 .collect()
266}
267
268impl From<&models::AttributeType> for types::AttributeType {
269 #[allow(clippy::expect_used)]
271 fn from(v: &models::AttributeType) -> Self {
272 Self {
273 attr_type: types::Type::from(
274 v.attr_type.as_ref().expect("attr_type field should exist"),
275 ),
276 is_required: v.is_required,
277 #[cfg(feature = "extended-schema")]
278 loc: None,
279 }
280 }
281}
282
283impl From<&types::AttributeType> for models::AttributeType {
284 fn from(v: &types::AttributeType) -> Self {
285 Self {
286 attr_type: Some(models::Type::from(&v.attr_type)),
287 is_required: v.is_required,
288 }
289 }
290}