1use std::collections::{hash_map::Entry, BTreeMap, HashMap, HashSet};
21
22use cedar_policy_core::{
23 ast::{
24 EntityAttrEvaluationError, EntityType, EntityUID, InternalName, Name, PartialValue,
25 UnreservedId,
26 },
27 entities::{json::err::JsonDeserializationErrorContext, CedarValueJson},
28 evaluator::RestrictedEvaluator,
29 extensions::Extensions,
30 fuzzy_match::fuzzy_search,
31 parser::Loc,
32};
33use itertools::Itertools;
34use nonempty::{nonempty, NonEmpty};
35use smol_str::{SmolStr, ToSmolStr};
36
37use super::{internal_name_to_entity_type, AllDefs, ValidatorApplySpec, ValidatorType};
38use crate::{
39 err::{schema_errors::*, SchemaError},
40 json_schema::{self, CommonTypeId, EntityTypeKind},
41 partition_nonempty::PartitionNonEmpty,
42 types::{AttributeType, Attributes, OpenTag, Type},
43 ActionBehavior, ConditionalName, RawName, ReferenceType,
44};
45
46#[derive(Debug, Clone)]
72pub struct ValidatorNamespaceDef<N, A> {
73 namespace: Option<InternalName>,
85 pub(super) common_types: CommonTypeDefs<N>,
88 pub(super) entity_types: EntityTypesDef<N>,
90 pub(super) actions: ActionsDef<N, A>,
92 #[cfg(feature = "extended-schema")]
93 pub(super) loc: Option<Loc>,
94}
95
96impl<N, A> ValidatorNamespaceDef<N, A> {
97 pub fn all_declared_entity_type_names(&self) -> impl Iterator<Item = &InternalName> {
100 self.entity_types
101 .defs
102 .keys()
103 .map(|ety| ety.as_ref().as_ref())
104 }
105
106 pub fn all_declared_common_type_names(&self) -> impl Iterator<Item = &InternalName> {
109 self.common_types.defs.keys()
110 }
111
112 pub fn all_declared_action_names(&self) -> impl Iterator<Item = &EntityUID> {
115 self.actions.actions.keys()
116 }
117
118 pub fn namespace(&self) -> Option<&InternalName> {
121 self.namespace.as_ref()
122 }
123}
124
125impl ValidatorNamespaceDef<ConditionalName, ConditionalName> {
126 pub fn from_namespace_definition(
128 namespace: Option<InternalName>,
129 namespace_def: json_schema::NamespaceDefinition<RawName>,
130 action_behavior: ActionBehavior,
131 extensions: &Extensions<'_>,
132 ) -> crate::err::Result<ValidatorNamespaceDef<ConditionalName, ConditionalName>> {
133 Self::check_action_behavior(&namespace_def, action_behavior)?;
136
137 let common_types =
140 CommonTypeDefs::from_raw_common_types(namespace_def.common_types, namespace.as_ref())?;
141 let actions =
142 ActionsDef::from_raw_actions(namespace_def.actions, namespace.as_ref(), extensions)?;
143 let entity_types =
144 EntityTypesDef::from_raw_entity_types(namespace_def.entity_types, namespace.as_ref())?;
145
146 Ok(ValidatorNamespaceDef {
147 namespace,
148 common_types,
149 entity_types,
150 actions,
151 #[cfg(feature = "extended-schema")]
152 loc: namespace_def.loc,
153 })
154 }
155
156 pub fn from_common_type_defs(
160 namespace: Option<InternalName>,
161 defs: HashMap<UnreservedId, json_schema::Type<ConditionalName>>,
162 ) -> crate::err::Result<ValidatorNamespaceDef<ConditionalName, ConditionalName>> {
163 let common_types = CommonTypeDefs::from_conditionalname_typedefs(defs, namespace.as_ref())?;
164 Ok(ValidatorNamespaceDef {
165 namespace,
166 common_types,
167 entity_types: EntityTypesDef::new(),
168 actions: ActionsDef::new(),
169 #[cfg(feature = "extended-schema")]
170 loc: None,
171 })
172 }
173
174 pub fn from_common_type_def(
181 namespace: Option<InternalName>,
182 def: (UnreservedId, json_schema::Type<ConditionalName>),
183 ) -> ValidatorNamespaceDef<ConditionalName, ConditionalName> {
184 let common_types = CommonTypeDefs::from_conditionalname_typedef(def, namespace.as_ref());
185 ValidatorNamespaceDef {
186 namespace,
187 common_types,
188 entity_types: EntityTypesDef::new(),
189 actions: ActionsDef::new(),
190 #[cfg(feature = "extended-schema")]
191 loc: None,
192 }
193 }
194
195 pub fn fully_qualify_type_references(
202 self,
203 all_defs: &AllDefs,
204 ) -> Result<ValidatorNamespaceDef<InternalName, EntityType>, SchemaError> {
205 match (
206 self.common_types.fully_qualify_type_references(all_defs),
207 self.entity_types.fully_qualify_type_references(all_defs),
208 self.actions.fully_qualify_type_references(all_defs),
209 ) {
210 (Ok(common_types), Ok(entity_types), Ok(actions)) => Ok(ValidatorNamespaceDef {
211 namespace: self.namespace,
212 common_types,
213 entity_types,
214 actions,
215 #[cfg(feature = "extended-schema")]
216 loc: self.loc,
217 }),
218 (res1, res2, res3) => {
219 #[allow(clippy::expect_used)]
221 let errs = NonEmpty::collect(
222 res1.err()
223 .into_iter()
224 .map(SchemaError::from)
225 .chain(res2.err().map(SchemaError::from))
226 .chain(res3.err()),
227 )
228 .expect("there must be an error");
229 Err(SchemaError::join_nonempty(errs))
230 }
231 }
232 }
233
234 fn check_action_behavior<N>(
240 schema_nsdef: &json_schema::NamespaceDefinition<N>,
241 action_behavior: ActionBehavior,
242 ) -> crate::err::Result<()> {
243 if schema_nsdef
244 .entity_types
245 .iter()
246 .any(|(name, _)| name.to_smolstr() == cedar_policy_core::ast::ACTION_ENTITY_TYPE)
250 {
251 return Err(ActionEntityTypeDeclaredError {}.into());
252 }
253 if action_behavior == ActionBehavior::ProhibitAttributes {
254 let mut actions_with_attributes: Vec<String> = Vec::new();
255 for (name, a) in &schema_nsdef.actions {
256 if a.attributes.is_some() {
257 actions_with_attributes.push(name.to_string());
258 }
259 }
260 if !actions_with_attributes.is_empty() {
261 actions_with_attributes.sort(); return Err(
263 UnsupportedFeatureError(UnsupportedFeature::ActionAttributes(
264 actions_with_attributes,
265 ))
266 .into(),
267 );
268 }
269 }
270
271 Ok(())
272 }
273}
274
275#[derive(Debug, Clone)]
282pub struct CommonTypeDefs<N> {
283 pub(super) defs: HashMap<InternalName, json_schema::Type<N>>,
284}
285
286impl CommonTypeDefs<ConditionalName> {
287 pub(crate) fn from_raw_common_types(
291 schema_file_type_def: impl IntoIterator<Item = (CommonTypeId, json_schema::CommonType<RawName>)>,
292 schema_namespace: Option<&InternalName>,
293 ) -> crate::err::Result<Self> {
294 let mut defs = HashMap::new();
295 for (id, schema_ty) in schema_file_type_def {
296 let name = RawName::new_from_unreserved(id.into(), schema_ty.loc)
297 .qualify_with(schema_namespace); match defs.entry(name) {
299 Entry::Vacant(ventry) => {
300 ventry.insert(
301 schema_ty
302 .ty
303 .conditionally_qualify_type_references(schema_namespace),
304 );
305 }
306 Entry::Occupied(oentry) => {
307 return Err(SchemaError::DuplicateCommonType(DuplicateCommonTypeError {
308 ty: oentry.key().clone(),
309 }));
310 }
311 }
312 }
313 Ok(Self { defs })
314 }
315
316 pub(crate) fn from_conditionalname_typedefs(
321 input_type_defs: HashMap<UnreservedId, json_schema::Type<ConditionalName>>,
322 schema_namespace: Option<&InternalName>,
323 ) -> crate::err::Result<Self> {
324 let mut defs = HashMap::with_capacity(input_type_defs.len());
325 for (id, schema_ty) in input_type_defs {
326 let name = RawName::new_from_unreserved(id, None).qualify_with(schema_namespace); match defs.entry(name) {
328 Entry::Vacant(ventry) => {
329 ventry.insert(schema_ty);
330 }
331 Entry::Occupied(oentry) => {
332 return Err(SchemaError::DuplicateCommonType(DuplicateCommonTypeError {
333 ty: oentry.key().clone(),
334 }));
335 }
336 }
337 }
338 Ok(Self { defs })
339 }
340
341 pub(crate) fn from_conditionalname_typedef(
348 (id, schema_ty): (UnreservedId, json_schema::Type<ConditionalName>),
349 schema_namespace: Option<&InternalName>,
350 ) -> Self {
351 Self {
352 defs: HashMap::from_iter([(
353 RawName::new_from_unreserved(id, None).qualify_with(schema_namespace),
354 schema_ty,
355 )]),
356 }
357 }
358
359 pub fn fully_qualify_type_references(
366 self,
367 all_defs: &AllDefs,
368 ) -> Result<CommonTypeDefs<InternalName>, TypeNotDefinedError> {
369 Ok(CommonTypeDefs {
370 defs: self
371 .defs
372 .into_iter()
373 .map(|(k, v)| Ok((k, v.fully_qualify_type_references(all_defs)?)))
374 .partition_nonempty()?,
375 })
376 }
377}
378
379#[derive(Debug, Clone)]
391pub struct EntityTypesDef<N> {
392 pub(super) defs: HashMap<EntityType, EntityTypeFragment<N>>,
393}
394
395impl<N> EntityTypesDef<N> {
396 pub fn new() -> Self {
398 Self {
399 defs: HashMap::new(),
400 }
401 }
402}
403
404impl EntityTypesDef<ConditionalName> {
405 pub(crate) fn from_raw_entity_types(
409 schema_files_types: impl IntoIterator<Item = (UnreservedId, json_schema::EntityType<RawName>)>,
410 schema_namespace: Option<&InternalName>,
411 ) -> crate::err::Result<Self> {
412 let mut defs: HashMap<EntityType, _> = HashMap::new();
413 for (id, entity_type) in schema_files_types {
414 let ety = internal_name_to_entity_type(
415 RawName::new_from_unreserved(id, entity_type.loc.clone())
416 .qualify_with(schema_namespace), )?;
418 match defs.entry(ety) {
419 Entry::Vacant(ventry) => {
420 ventry.insert(EntityTypeFragment::from_raw_entity_type(
421 entity_type,
422 schema_namespace,
423 ));
424 }
425 Entry::Occupied(entry) => {
426 return Err(DuplicateEntityTypeError {
427 ty: entry.key().clone(),
428 }
429 .into());
430 }
431 }
432 }
433 Ok(EntityTypesDef { defs })
434 }
435
436 pub fn fully_qualify_type_references(
443 self,
444 all_defs: &AllDefs,
445 ) -> Result<EntityTypesDef<InternalName>, TypeNotDefinedError> {
446 Ok(EntityTypesDef {
447 defs: self
448 .defs
449 .into_iter()
450 .map(|(k, v)| Ok((k, v.fully_qualify_type_references(all_defs)?)))
451 .partition_nonempty()?,
452 })
453 }
454}
455
456#[derive(Debug, Clone)]
464pub enum EntityTypeFragment<N> {
465 Standard {
466 attributes: json_schema::AttributesOrContext<N>,
474 parents: HashSet<N>,
481 tags: Option<json_schema::Type<N>>,
488 },
489 Enum(NonEmpty<SmolStr>),
490}
491
492impl<N> EntityTypeFragment<N> {
493 pub(crate) fn parents(&self) -> Box<dyn Iterator<Item = &N> + '_> {
494 match self {
495 Self::Standard { parents, .. } => Box::new(parents.iter()),
496 Self::Enum(_) => Box::new(std::iter::empty()),
497 }
498 }
499}
500
501impl EntityTypeFragment<ConditionalName> {
502 pub(crate) fn from_raw_entity_type(
506 schema_file_type: json_schema::EntityType<RawName>,
507 schema_namespace: Option<&InternalName>,
508 ) -> Self {
509 match schema_file_type.kind {
510 EntityTypeKind::Enum { choices } => Self::Enum(choices),
511 EntityTypeKind::Standard(ty) => {
512 Self::Standard {
513 attributes: ty
514 .shape
515 .conditionally_qualify_type_references(schema_namespace),
516 parents: ty
517 .member_of_types
518 .into_iter()
519 .map(|raw_name| {
520 raw_name
522 .conditionally_qualify_with(schema_namespace, ReferenceType::Entity)
523 })
524 .collect(),
525 tags: ty
526 .tags
527 .map(|tags| tags.conditionally_qualify_type_references(schema_namespace)),
528 }
529 }
530 }
531 }
532
533 pub fn fully_qualify_type_references(
540 self,
541 all_defs: &AllDefs,
542 ) -> Result<EntityTypeFragment<InternalName>, TypeNotDefinedError> {
543 match self {
544 Self::Enum(choices) => Ok(EntityTypeFragment::Enum(choices)),
545 Self::Standard {
546 attributes,
547 parents,
548 tags,
549 } => {
550 let fully_qual_attributes = attributes.fully_qualify_type_references(all_defs);
552 let parents: HashSet<InternalName> = parents
554 .into_iter()
555 .map(|parent| parent.resolve(all_defs))
556 .partition_nonempty()?;
557 let fully_qual_tags = tags
559 .map(|tags| tags.fully_qualify_type_references(all_defs))
560 .transpose();
561 let undeclared_parents: Option<NonEmpty<ConditionalName>> = NonEmpty::collect(
565 parents
566 .iter()
567 .filter(|ety| !all_defs.is_defined_as_entity(ety))
568 .map(|ety| {
569 ConditionalName::unconditional(ety.clone(), ReferenceType::Entity)
570 }),
571 );
572 match (fully_qual_attributes, fully_qual_tags, undeclared_parents) {
573 (Ok(attributes), Ok(tags), None) => Ok(EntityTypeFragment::Standard {
574 attributes,
575 parents,
576 tags,
577 }),
578 (Ok(_), Ok(_), Some(undeclared_parents)) => Err(TypeNotDefinedError {
579 undefined_types: undeclared_parents,
580 }),
581 (Err(e), Ok(_), None) | (Ok(_), Err(e), None) => Err(e),
582 (Err(e1), Err(e2), None) => {
583 Err(TypeNotDefinedError::join_nonempty(nonempty![e1, e2]))
584 }
585 (Err(e), Ok(_), Some(mut undeclared))
586 | (Ok(_), Err(e), Some(mut undeclared)) => {
587 undeclared.extend(e.undefined_types);
588 Err(TypeNotDefinedError {
589 undefined_types: undeclared,
590 })
591 }
592 (Err(e1), Err(e2), Some(mut undeclared)) => {
593 undeclared.extend(e1.undefined_types);
594 undeclared.extend(e2.undefined_types);
595 Err(TypeNotDefinedError {
596 undefined_types: undeclared,
597 })
598 }
599 }
600 }
601 }
602 }
603}
604
605#[derive(Debug, Clone)]
620pub struct ActionsDef<N, A> {
621 pub(super) actions: HashMap<EntityUID, ActionFragment<N, A>>,
622}
623
624impl<N, A> ActionsDef<N, A> {
625 pub fn new() -> Self {
627 Self {
628 actions: HashMap::new(),
629 }
630 }
631}
632
633#[cfg_attr(not(feature = "extended-schema"), allow(unused_variables))]
634fn create_action_entity_uid_default_type(
635 action_name: &SmolStr,
636 action_type: &json_schema::ActionType<RawName>,
637 schema_namespace: Option<&InternalName>,
638) -> json_schema::ActionEntityUID<InternalName> {
639 let action_id_str = action_name.clone();
640 #[cfg(feature = "extended-schema")]
641 let action_id_loc = action_type.defn_loc.clone();
642 #[cfg(feature = "extended-schema")]
643 return json_schema::ActionEntityUID::default_type_with_loc(action_id_str, action_id_loc)
645 .qualify_with(schema_namespace);
646
647 #[cfg(not(feature = "extended-schema"))]
648 json_schema::ActionEntityUID::default_type(action_id_str).qualify_with(schema_namespace)
650}
651
652impl ActionsDef<ConditionalName, ConditionalName> {
653 pub(crate) fn from_raw_actions(
656 schema_file_actions: impl IntoIterator<Item = (SmolStr, json_schema::ActionType<RawName>)>,
657 schema_namespace: Option<&InternalName>,
658 extensions: &Extensions<'_>,
659 ) -> crate::err::Result<Self> {
660 let mut actions = HashMap::new();
661 for (action_name, action_type) in schema_file_actions {
662 let action_uid =
663 create_action_entity_uid_default_type(&action_name, &action_type, schema_namespace);
664 match actions.entry(action_uid.clone().try_into()?) {
665 Entry::Vacant(ventry) => {
666 let frag = ActionFragment::from_raw_action(
667 ventry.key(),
668 action_type.clone(),
669 schema_namespace,
670 extensions,
671 action_type.loc.as_ref(),
672 )?;
673 ventry.insert(frag);
674 }
675 Entry::Occupied(_) => {
676 return Err(DuplicateActionError(action_name).into());
677 }
678 }
679 }
680 Ok(Self { actions })
681 }
682
683 pub fn fully_qualify_type_references(
690 self,
691 all_defs: &AllDefs,
692 ) -> Result<ActionsDef<InternalName, EntityType>, SchemaError> {
693 Ok(ActionsDef {
694 actions: self
695 .actions
696 .into_iter()
697 .map(|(k, v)| v.fully_qualify_type_references(all_defs).map(|v| (k, v)))
698 .partition_nonempty()?,
699 })
700 }
701}
702
703#[derive(Debug, Clone)]
714pub struct ActionFragment<N, A> {
715 pub(super) context: json_schema::Type<N>,
719 pub(super) applies_to: ValidatorApplySpec<A>,
721 pub(super) parents: HashSet<json_schema::ActionEntityUID<N>>,
727 pub(super) attribute_types: Attributes,
730 pub(super) attributes: BTreeMap<SmolStr, PartialValue>,
734 pub(super) loc: Option<Loc>,
736}
737
738impl ActionFragment<ConditionalName, ConditionalName> {
739 pub(crate) fn from_raw_action(
740 action_uid: &EntityUID,
741 action_type: json_schema::ActionType<RawName>,
742 schema_namespace: Option<&InternalName>,
743 extensions: &Extensions<'_>,
744 loc: Option<&Loc>,
745 ) -> crate::err::Result<Self> {
746 let (principal_types, resource_types, context) = action_type
747 .applies_to
748 .map(|applies_to| {
749 (
750 applies_to.principal_types,
751 applies_to.resource_types,
752 applies_to.context,
753 )
754 })
755 .unwrap_or_default();
756 let (attribute_types, attributes) = Self::convert_attr_jsonval_map_to_attributes(
757 action_type.attributes.unwrap_or_default(),
758 action_uid,
759 extensions,
760 )?;
761 Ok(Self {
762 context: context
763 .into_inner()
764 .conditionally_qualify_type_references(schema_namespace),
765 applies_to: ValidatorApplySpec::<ConditionalName>::new(
766 principal_types
767 .into_iter()
768 .map(|pty| {
769 pty.conditionally_qualify_with(schema_namespace, ReferenceType::Entity)
770 })
771 .collect(),
772 resource_types
773 .into_iter()
774 .map(|rty| {
775 rty.conditionally_qualify_with(schema_namespace, ReferenceType::Entity)
776 })
777 .collect(),
778 ),
779 parents: action_type
780 .member_of
781 .unwrap_or_default()
782 .into_iter()
783 .map(|parent| parent.conditionally_qualify_type_references(schema_namespace))
784 .collect(),
785 attribute_types,
786 attributes,
787 loc: loc.cloned(),
788 })
789 }
790
791 pub fn fully_qualify_type_references(
798 self,
799 all_defs: &AllDefs,
800 ) -> Result<ActionFragment<InternalName, EntityType>, SchemaError> {
801 Ok(ActionFragment {
802 context: self.context.fully_qualify_type_references(all_defs)?,
803 applies_to: self.applies_to.fully_qualify_type_references(all_defs)?,
804 parents: self
805 .parents
806 .into_iter()
807 .map(|parent| parent.fully_qualify_type_references(all_defs))
808 .partition_nonempty()?,
809 attribute_types: self.attribute_types,
810 attributes: self.attributes,
811 loc: self.loc,
812 })
813 }
814
815 fn convert_attr_jsonval_map_to_attributes(
816 m: HashMap<SmolStr, CedarValueJson>,
817 action_id: &EntityUID,
818 extensions: &Extensions<'_>,
819 ) -> crate::err::Result<(Attributes, BTreeMap<SmolStr, PartialValue>)> {
820 let mut attr_types: HashMap<SmolStr, Type> = HashMap::with_capacity(m.len());
821 let mut attr_values: BTreeMap<SmolStr, PartialValue> = BTreeMap::new();
822 let evaluator = RestrictedEvaluator::new(extensions);
823
824 for (k, v) in m {
825 let t = Self::jsonval_to_type_helper(&v, action_id);
826 match t {
827 Ok(ty) => attr_types.insert(k.clone(), ty),
828 Err(e) => return Err(e),
829 };
830
831 #[allow(clippy::expect_used)]
841 let e = v.into_expr(|| JsonDeserializationErrorContext::EntityAttribute { uid: action_id.clone(), attr: k.clone() }).expect("`Self::jsonval_to_type_helper` will always return `Err` for a `CedarValueJson` that might make `into_expr` return `Err`");
842 let pv = evaluator
843 .partial_interpret(e.as_borrowed())
844 .map_err(|err| {
845 ActionAttrEvalError(EntityAttrEvaluationError {
846 uid: action_id.clone(),
847 attr_or_tag: k.clone(),
848 was_attr: true,
849 err,
850 })
851 })?;
852 attr_values.insert(k, pv);
853 }
854 Ok((
855 Attributes::with_required_attributes(attr_types),
856 attr_values,
857 ))
858 }
859
860 fn jsonval_to_type_helper(
866 v: &CedarValueJson,
867 action_id: &EntityUID,
868 ) -> crate::err::Result<Type> {
869 match v {
870 CedarValueJson::Bool(_) => Ok(Type::primitive_boolean()),
871 CedarValueJson::Long(_) => Ok(Type::primitive_long()),
872 CedarValueJson::String(_) => Ok(Type::primitive_string()),
873 CedarValueJson::Record(r) => {
874 let mut required_attrs: HashMap<SmolStr, Type> = HashMap::with_capacity(r.len());
875 for (k, v_prime) in r {
876 let t = Self::jsonval_to_type_helper(v_prime, action_id);
877 match t {
878 Ok(ty) => required_attrs.insert(k.clone(), ty),
879 Err(e) => return Err(e),
880 };
881 }
882 Ok(Type::record_with_required_attributes(
883 required_attrs,
884 OpenTag::ClosedAttributes,
885 ))
886 }
887 CedarValueJson::Set(v) => match v.first() {
888 None => Err(ActionAttributesContainEmptySetError {
890 uid: action_id.clone(),
891 }
892 .into()),
893 Some(element) => {
894 let element_type = Self::jsonval_to_type_helper(element, action_id);
895 match element_type {
896 Ok(t) => Ok(Type::Set {
897 element_type: Some(Box::new(t)),
898 }),
899 Err(_) => element_type,
900 }
901 }
902 },
903 CedarValueJson::EntityEscape { __entity: _ } => Err(UnsupportedActionAttributeError {
904 uid: action_id.clone(),
905 attr: "entity escape (`__entity`)".into(),
906 }
907 .into()),
908 CedarValueJson::ExprEscape { __expr: _ } => Err(UnsupportedActionAttributeError {
909 uid: action_id.clone(),
910 attr: "expression escape (`__expr`)".into(),
911 }
912 .into()),
913 CedarValueJson::ExtnEscape { __extn: _ } => Err(UnsupportedActionAttributeError {
914 uid: action_id.clone(),
915 attr: "extension function escape (`__extn`)".into(),
916 }
917 .into()),
918 CedarValueJson::Null => Err(UnsupportedActionAttributeError {
919 uid: action_id.clone(),
920 attr: "null".into(),
921 }
922 .into()),
923 }
924 }
925}
926
927type ResolveFunc<T> = dyn FnOnce(&HashMap<&InternalName, ValidatorType>) -> crate::err::Result<T>;
928pub(crate) enum WithUnresolvedCommonTypeRefs<T> {
931 WithUnresolved(Box<ResolveFunc<T>>, Option<Loc>),
932 WithoutUnresolved(T, Option<Loc>),
933}
934
935impl<T: 'static> WithUnresolvedCommonTypeRefs<T> {
936 pub fn new(
937 f: impl FnOnce(&HashMap<&InternalName, ValidatorType>) -> crate::err::Result<T> + 'static,
938 loc: Option<Loc>,
939 ) -> Self {
940 Self::WithUnresolved(Box::new(f), loc)
941 }
942
943 pub fn loc(&self) -> Option<&Loc> {
944 match self {
945 WithUnresolvedCommonTypeRefs::WithUnresolved(_, loc) => loc.as_ref(),
946 WithUnresolvedCommonTypeRefs::WithoutUnresolved(_, loc) => loc.as_ref(),
947 }
948 }
949
950 pub fn map<U: 'static>(
951 self,
952 f: impl FnOnce(T) -> U + 'static,
953 ) -> WithUnresolvedCommonTypeRefs<U> {
954 match self {
955 Self::WithUnresolved(_, ref loc) => {
956 let loc = loc.clone();
957 WithUnresolvedCommonTypeRefs::new(
958 |common_type_defs| self.resolve_common_type_refs(common_type_defs).map(f),
959 loc,
960 )
961 }
962 Self::WithoutUnresolved(v, loc) => {
963 WithUnresolvedCommonTypeRefs::WithoutUnresolved(f(v), loc)
964 }
965 }
966 }
967
968 pub fn resolve_common_type_refs(
976 self,
977 common_type_defs: &HashMap<&InternalName, ValidatorType>,
978 ) -> crate::err::Result<T> {
979 match self {
980 WithUnresolvedCommonTypeRefs::WithUnresolved(f, _loc) => f(common_type_defs),
981 WithUnresolvedCommonTypeRefs::WithoutUnresolved(v, _loc) => Ok(v),
982 }
983 }
984}
985
986impl<T: 'static> From<T> for WithUnresolvedCommonTypeRefs<T> {
987 fn from(value: T) -> Self {
988 Self::WithoutUnresolved(value, None)
989 }
990}
991
992impl From<Type> for WithUnresolvedCommonTypeRefs<ValidatorType> {
993 fn from(value: Type) -> Self {
994 Self::WithoutUnresolved(
995 ValidatorType {
996 ty: value,
997 #[cfg(feature = "extended-schema")]
998 loc: None,
999 },
1000 None,
1001 )
1002 }
1003}
1004
1005impl<T: std::fmt::Debug> std::fmt::Debug for WithUnresolvedCommonTypeRefs<T> {
1006 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1007 match self {
1008 WithUnresolvedCommonTypeRefs::WithUnresolved(_, _) => {
1009 f.debug_tuple("WithUnresolved").finish()
1010 }
1011 WithUnresolvedCommonTypeRefs::WithoutUnresolved(v, _) => {
1012 f.debug_tuple("WithoutUnresolved").field(v).finish()
1013 }
1014 }
1015 }
1016}
1017
1018impl TryInto<ValidatorNamespaceDef<ConditionalName, ConditionalName>>
1019 for json_schema::NamespaceDefinition<RawName>
1020{
1021 type Error = SchemaError;
1022
1023 fn try_into(
1024 self,
1025 ) -> crate::err::Result<ValidatorNamespaceDef<ConditionalName, ConditionalName>> {
1026 ValidatorNamespaceDef::from_namespace_definition(
1027 None,
1028 self,
1029 ActionBehavior::default(),
1030 Extensions::all_available(),
1031 )
1032 }
1033}
1034
1035pub(crate) fn try_jsonschema_type_into_validator_type(
1042 schema_ty: json_schema::Type<InternalName>,
1043 extensions: &Extensions<'_>,
1044 loc: Option<Loc>,
1045) -> crate::err::Result<WithUnresolvedCommonTypeRefs<ValidatorType>> {
1046 match schema_ty {
1047 json_schema::Type::Type {
1048 ty: json_schema::TypeVariant::String,
1049 ..
1050 } => Ok(WithUnresolvedCommonTypeRefs::WithoutUnresolved(
1051 ValidatorType {
1052 ty: Type::primitive_string(),
1053 #[cfg(feature = "extended-schema")]
1054 loc: loc.clone(),
1055 },
1056 loc,
1057 )),
1058 json_schema::Type::Type {
1059 ty: json_schema::TypeVariant::Long,
1060 ..
1061 } => Ok(WithUnresolvedCommonTypeRefs::WithoutUnresolved(
1062 ValidatorType {
1063 ty: Type::primitive_long(),
1064 #[cfg(feature = "extended-schema")]
1065 loc: loc.clone(),
1066 },
1067 loc,
1068 )),
1069 json_schema::Type::Type {
1070 ty: json_schema::TypeVariant::Boolean,
1071 ..
1072 } => Ok(WithUnresolvedCommonTypeRefs::WithoutUnresolved(
1073 ValidatorType {
1074 ty: Type::primitive_boolean(),
1075 #[cfg(feature = "extended-schema")]
1076 loc: loc.clone(),
1077 },
1078 loc,
1079 )),
1080 json_schema::Type::Type {
1081 ty: json_schema::TypeVariant::Set { element },
1082 ..
1083 } => Ok(
1084 try_jsonschema_type_into_validator_type(*element, extensions, loc)?.map(|vt| {
1085 ValidatorType {
1086 ty: Type::set(vt.ty),
1087 #[cfg(feature = "extended-schema")]
1088 loc: vt.loc,
1089 }
1090 }),
1091 ),
1092 json_schema::Type::Type {
1093 ty: json_schema::TypeVariant::Record(rty),
1094 ..
1095 } => try_record_type_into_validator_type(rty, extensions, loc),
1096 json_schema::Type::Type {
1097 ty: json_schema::TypeVariant::Entity { name },
1098 ..
1099 } => Ok(WithUnresolvedCommonTypeRefs::WithoutUnresolved(
1100 ValidatorType {
1101 ty: Type::named_entity_reference(internal_name_to_entity_type(name)?),
1102 #[cfg(feature = "extended-schema")]
1103 loc: loc.clone(),
1104 },
1105 loc,
1106 )),
1107 json_schema::Type::Type {
1108 ty: json_schema::TypeVariant::Extension { name },
1109 ..
1110 } => {
1111 let extension_type_name = Name::unqualified_name(name);
1112 if extensions.ext_types().contains(&extension_type_name) {
1113 Ok(Type::extension(extension_type_name).into())
1114 } else {
1115 let suggested_replacement = fuzzy_search(
1116 &extension_type_name.to_string(),
1117 &extensions
1118 .ext_types()
1119 .map(|n| n.to_string())
1120 .collect::<Vec<_>>(),
1121 );
1122 Err(SchemaError::UnknownExtensionType(
1123 UnknownExtensionTypeError {
1124 actual: extension_type_name,
1125 suggested_replacement,
1126 },
1127 ))
1128 }
1129 }
1130 json_schema::Type::CommonTypeRef { type_name, .. } => {
1131 Ok(WithUnresolvedCommonTypeRefs::new(
1132 move |common_type_defs| {
1133 common_type_defs
1134 .get(&type_name)
1135 .cloned()
1136 .ok_or_else(|| CommonTypeInvariantViolationError { name: type_name }.into())
1145 },
1146 loc,
1147 ))
1148 }
1149 json_schema::Type::Type {
1150 ty: json_schema::TypeVariant::EntityOrCommon { type_name },
1151 ..
1152 } => {
1153 let loc_clone = loc.clone();
1154 Ok(WithUnresolvedCommonTypeRefs::new(
1155 move |common_type_defs| {
1156 #[cfg_attr(not(feature = "extended-schema"), allow(unused_variables))]
1157 let loc: Option<Loc> = loc.clone();
1158
1159 match common_type_defs.get(&type_name) {
1163 Some(def) => Ok(def.clone()),
1164 None => {
1165 Ok(ValidatorType {
1171 ty: Type::named_entity_reference(internal_name_to_entity_type(
1172 type_name,
1173 )?),
1174 #[cfg(feature = "extended-schema")]
1175 loc,
1176 })
1177 }
1178 }
1179 },
1180 loc_clone,
1181 ))
1182 }
1183 }
1184}
1185
1186#[cfg_attr(not(feature = "extended-schema"), allow(unused_variables))]
1189pub(crate) fn try_record_type_into_validator_type(
1190 rty: json_schema::RecordType<InternalName>,
1191 extensions: &Extensions<'_>,
1192 loc: Option<Loc>,
1193) -> crate::err::Result<WithUnresolvedCommonTypeRefs<ValidatorType>> {
1194 if cfg!(not(feature = "partial-validate")) && rty.additional_attributes {
1195 Err(UnsupportedFeatureError(UnsupportedFeature::OpenRecordsAndEntities).into())
1196 } else {
1197 #[cfg(feature = "extended-schema")]
1198 let attr_loc = loc.clone();
1199 #[cfg(not(feature = "extended-schema"))]
1200 let attr_loc = None;
1201 Ok(
1202 parse_record_attributes(rty.attributes.into_iter(), extensions, attr_loc)?.map(
1203 move |attrs| ValidatorType {
1204 ty: Type::record_with_attributes(
1205 attrs,
1206 if rty.additional_attributes {
1207 OpenTag::OpenAttributes
1208 } else {
1209 OpenTag::ClosedAttributes
1210 },
1211 ),
1212 #[cfg(feature = "extended-schema")]
1213 loc,
1214 },
1215 ),
1216 )
1217 }
1218}
1219
1220#[cfg_attr(not(feature = "extended-schema"), allow(unused_variables))]
1225fn parse_record_attributes(
1226 attrs: impl IntoIterator<Item = (SmolStr, json_schema::TypeOfAttribute<InternalName>)>,
1227 extensions: &Extensions<'_>,
1228 loc: Option<Loc>,
1229) -> crate::err::Result<WithUnresolvedCommonTypeRefs<Attributes>> {
1230 let attrs_with_common_type_refs = attrs
1231 .into_iter()
1232 .map(|(attr, ty)| -> crate::err::Result<_> {
1233 #[cfg(feature = "extended-schema")]
1234 let loc = ty.loc;
1235 #[cfg(not(feature = "extended-schema"))]
1236 let loc = None;
1237 Ok((
1238 attr,
1239 (
1240 try_jsonschema_type_into_validator_type(ty.ty.clone(), extensions, loc)?,
1241 ty.required,
1242 ),
1243 ))
1244 })
1245 .collect::<crate::err::Result<Vec<_>>>()?;
1246
1247 Ok(WithUnresolvedCommonTypeRefs::new(
1248 |common_type_defs| {
1249 attrs_with_common_type_refs
1250 .into_iter()
1251 .map(|(s, (attr_ty, is_req))| {
1252 let loc = attr_ty.loc().cloned();
1253 attr_ty
1254 .resolve_common_type_refs(common_type_defs)
1255 .map(|ty| {
1256 #[cfg(feature = "extended-schema")]
1257 let ty_pair = (s, AttributeType::new_with_loc(ty.ty, is_req, loc));
1258 #[cfg(not(feature = "extended-schema"))]
1259 let ty_pair = (s, AttributeType::new(ty.ty, is_req));
1260 ty_pair
1261 })
1262 })
1263 .collect::<crate::err::Result<Vec<_>>>()
1264 .map(Attributes::with_attributes)
1265 },
1266 loc,
1267 ))
1268}