1use std::collections::BTreeSet;
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 ConstraintSourceKey, Decimal, DeclaredEntityVersion, EntitySourceKey, FieldSourceKey,
9 IndexSourceKey, MAX_FRAGMENT_CONSTRAINTS, MAX_FRAGMENT_ENTITIES, MAX_FRAGMENT_FIELDS,
10 MAX_FRAGMENT_INDEXES, MAX_FRAGMENT_RELATIONS, MAX_FRAGMENT_TYPES, MAX_RELATION_PATH_STEPS,
11 MAX_SCHEMA_FIELD_TYPE_DEPTH, RelationSourceKey, RuleSourceKey, ScalarKind, ScalarLiteral,
12 SchemaContractError, SchemaName, SourceCheckExpr, SourceRuleOperation, TypeSourceKey,
13};
14
15#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
17pub enum FieldType {
18 Scalar(ScalarType),
20 List(Box<Self>),
22 Named(
24 #[serde(
25 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
26 )]
27 TypeSourceKey,
28 ),
29}
30
31#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
33pub enum ScalarType {
34 Account,
36 Blob {
38 max_len: Option<u32>,
40 },
41 Bool,
43 Date,
45 Decimal {
47 scale: u32,
49 },
50 Duration,
52 Float32,
54 Float64,
56 Int8,
58 Int16,
60 Int32,
62 Int64,
64 Int128,
66 Principal,
68 Subaccount,
70 Text {
72 max_len: Option<u32>,
74 },
75 Timestamp,
77 Nat8,
79 Nat16,
81 Nat32,
83 Nat64,
85 Nat128,
87 U256,
89 Ulid,
91 Unit,
93}
94
95impl ScalarType {
96 #[must_use]
98 pub const fn kind(self) -> ScalarKind {
99 match self {
100 Self::Account => ScalarKind::Account,
101 Self::Blob { .. } => ScalarKind::Blob,
102 Self::Bool => ScalarKind::Bool,
103 Self::Date => ScalarKind::Date,
104 Self::Decimal { .. } => ScalarKind::Decimal,
105 Self::Duration => ScalarKind::Duration,
106 Self::Float32 => ScalarKind::Float32,
107 Self::Float64 => ScalarKind::Float64,
108 Self::Int8 | Self::Int16 | Self::Int32 | Self::Int64 => ScalarKind::Int,
109 Self::Int128 => ScalarKind::Int128,
110 Self::Principal => ScalarKind::Principal,
111 Self::Subaccount => ScalarKind::Subaccount,
112 Self::Text { .. } => ScalarKind::Text,
113 Self::Timestamp => ScalarKind::Timestamp,
114 Self::Nat8 | Self::Nat16 | Self::Nat32 | Self::Nat64 => ScalarKind::Nat,
115 Self::Nat128 => ScalarKind::Nat128,
116 Self::U256 => ScalarKind::U256,
117 Self::Ulid => ScalarKind::Ulid,
118 Self::Unit => ScalarKind::Unit,
119 }
120 }
121
122 pub(crate) const fn validate(self) -> Result<(), SchemaContractError> {
123 match self {
124 Self::Decimal { scale } if scale > Decimal::max_supported_scale() => {
125 Err(SchemaContractError::InvalidFieldType)
126 }
127 _ => Ok(()),
128 }
129 }
130
131 pub(crate) fn accepts_literal(self, literal: &ScalarLiteral) -> bool {
132 match (self, literal) {
133 (Self::Account, ScalarLiteral::Account(_))
134 | (Self::Bool, ScalarLiteral::Bool(_))
135 | (Self::Date, ScalarLiteral::Date(_))
136 | (Self::Duration, ScalarLiteral::Duration(_))
137 | (Self::Float32, ScalarLiteral::Float32(_))
138 | (Self::Float64, ScalarLiteral::Float64(_))
139 | (Self::Int128, ScalarLiteral::Int(_))
140 | (Self::Principal, ScalarLiteral::Principal(_))
141 | (Self::Subaccount, ScalarLiteral::Subaccount(_))
142 | (Self::Timestamp, ScalarLiteral::Timestamp(_))
143 | (Self::Nat128, ScalarLiteral::Nat(_))
144 | (Self::U256, ScalarLiteral::U256(_))
145 | (Self::Ulid, ScalarLiteral::Ulid(_))
146 | (Self::Unit, ScalarLiteral::Unit(_)) => true,
147 (Self::Blob { max_len }, ScalarLiteral::Blob(value)) => {
148 max_len.is_none_or(|max| value.len() <= max as usize)
149 }
150 (Self::Text { max_len }, ScalarLiteral::Text(value)) => {
151 max_len.is_none_or(|max| value.chars().count() <= max as usize)
152 }
153 (Self::Int8, ScalarLiteral::Int(value)) => i8::try_from(*value).is_ok(),
154 (Self::Int16, ScalarLiteral::Int(value)) => i16::try_from(*value).is_ok(),
155 (Self::Int32, ScalarLiteral::Int(value)) => i32::try_from(*value).is_ok(),
156 (Self::Int64, ScalarLiteral::Int(value)) => i64::try_from(*value).is_ok(),
157 (Self::Nat8, ScalarLiteral::Nat(value)) => u8::try_from(*value).is_ok(),
158 (Self::Nat16, ScalarLiteral::Nat(value)) => u16::try_from(*value).is_ok(),
159 (Self::Nat32, ScalarLiteral::Nat(value)) => u32::try_from(*value).is_ok(),
160 (Self::Nat64, ScalarLiteral::Nat(value)) => u64::try_from(*value).is_ok(),
161 (Self::Decimal { scale }, ScalarLiteral::Decimal(value)) => {
162 decimal_fits_scale(*value, scale)
163 }
164 _ => false,
165 }
166 }
167}
168
169impl FieldType {
170 pub fn append_named_type_dependencies(&self, pending: &mut Vec<TypeSourceKey>) {
175 let mut field_type = self;
176 while let Self::List(item) = field_type {
177 field_type = item;
178 }
179 if let Self::Named(source) = field_type {
180 pending.push(source.clone());
181 }
182 }
183
184 pub(crate) const fn validate(&self) -> Result<(), SchemaContractError> {
185 self.validate_at_depth(0)
186 }
187
188 const fn validate_at_depth(&self, depth: usize) -> Result<(), SchemaContractError> {
189 let Some(depth) = depth.checked_add(1) else {
190 return Err(SchemaContractError::FieldTypeDepthExceeded);
191 };
192 if depth > MAX_SCHEMA_FIELD_TYPE_DEPTH {
193 return Err(SchemaContractError::FieldTypeDepthExceeded);
194 }
195 match self {
196 Self::Scalar(scalar) => scalar.validate(),
197 Self::List(item) => item.validate_at_depth(depth),
198 Self::Named(_) => Ok(()),
199 }
200 }
201}
202
203#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
205pub enum FieldInsertPolicy {
206 Required,
208 Nullable,
210 Default(ScalarLiteral),
212 Generated,
214}
215
216#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
218pub enum FieldManagementPolicy {
219 CreatedAt,
221 UpdatedAt,
223}
224
225#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
227pub struct FieldFragment {
228 #[serde(
229 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
230 )]
231 source_key: FieldSourceKey,
232 #[serde(
233 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
234 )]
235 name: SchemaName,
236 field_type: FieldType,
237 nullable: bool,
238 insert_policy: FieldInsertPolicy,
239 management: Option<FieldManagementPolicy>,
240}
241
242impl FieldFragment {
243 #[must_use]
245 pub fn new(
246 name: SchemaName,
247 field_type: FieldType,
248 nullable: bool,
249 insert_policy: FieldInsertPolicy,
250 management: Option<FieldManagementPolicy>,
251 ) -> Self {
252 Self {
253 source_key: FieldSourceKey::from_name(&name),
254 name,
255 field_type,
256 nullable,
257 insert_policy,
258 management,
259 }
260 }
261
262 #[must_use]
264 pub const fn source_key(&self) -> &FieldSourceKey {
265 &self.source_key
266 }
267
268 #[must_use]
270 pub const fn name(&self) -> &SchemaName {
271 &self.name
272 }
273
274 #[must_use]
276 pub const fn field_type(&self) -> &FieldType {
277 &self.field_type
278 }
279
280 #[must_use]
282 pub const fn nullable(&self) -> bool {
283 self.nullable
284 }
285
286 #[must_use]
288 pub const fn insert_policy(&self) -> &FieldInsertPolicy {
289 &self.insert_policy
290 }
291
292 #[must_use]
294 pub const fn management(&self) -> Option<FieldManagementPolicy> {
295 self.management
296 }
297
298 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
299 ensure_current_name_key(self.source_key.as_str(), &self.name)?;
300 self.field_type.validate()?;
301 if let FieldInsertPolicy::Default(literal) = &self.insert_policy {
302 literal.validate()?;
303 match &self.field_type {
304 FieldType::Scalar(scalar) if scalar.accepts_literal(literal) => {}
305 FieldType::Named(_) if matches!(literal, ScalarLiteral::EnumUnit { .. }) => {}
306 FieldType::Scalar(_) | FieldType::List(_) | FieldType::Named(_) => {
307 return Err(SchemaContractError::LiteralTypeMismatch);
308 }
309 }
310 }
311 if matches!(self.insert_policy, FieldInsertPolicy::Nullable) && !self.nullable {
312 return Err(SchemaContractError::InvalidFieldPolicy);
313 }
314 if self.management.is_some()
315 && (!matches!(self.field_type, FieldType::Scalar(ScalarType::Timestamp))
316 || self.nullable
317 || !matches!(self.insert_policy, FieldInsertPolicy::Required))
318 {
319 return Err(SchemaContractError::InvalidFieldPolicy);
320 }
321 Ok(())
322 }
323}
324
325#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
327pub enum IndexKeyFragment {
328 Field(
330 #[serde(
331 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
332 )]
333 FieldSourceKey,
334 ),
335 Lower(
337 #[serde(
338 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
339 )]
340 FieldSourceKey,
341 ),
342 Upper(
344 #[serde(
345 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
346 )]
347 FieldSourceKey,
348 ),
349 Trim(
351 #[serde(
352 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
353 )]
354 FieldSourceKey,
355 ),
356 LowerTrim(
358 #[serde(
359 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
360 )]
361 FieldSourceKey,
362 ),
363 Date(
365 #[serde(
366 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
367 )]
368 FieldSourceKey,
369 ),
370 Year(
372 #[serde(
373 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
374 )]
375 FieldSourceKey,
376 ),
377 Month(
379 #[serde(
380 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
381 )]
382 FieldSourceKey,
383 ),
384 Day(
386 #[serde(
387 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
388 )]
389 FieldSourceKey,
390 ),
391}
392
393impl IndexKeyFragment {
394 #[must_use]
396 pub const fn field(&self) -> &FieldSourceKey {
397 match self {
398 Self::Field(field)
399 | Self::Lower(field)
400 | Self::Upper(field)
401 | Self::Trim(field)
402 | Self::LowerTrim(field)
403 | Self::Date(field)
404 | Self::Year(field)
405 | Self::Month(field)
406 | Self::Day(field) => field,
407 }
408 }
409}
410
411#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
413pub struct IndexFragment {
414 #[serde(
415 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
416 )]
417 source_key: IndexSourceKey,
418 #[serde(
419 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
420 )]
421 name: SchemaName,
422 #[serde(
423 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_INDEX_FIELDS }>"
424 )]
425 key: Vec<IndexKeyFragment>,
426 unique: bool,
427 predicate: Option<SourceCheckExpr>,
428}
429
430impl IndexFragment {
431 pub fn try_new(
437 name: SchemaName,
438 key: Vec<IndexKeyFragment>,
439 unique: bool,
440 predicate: Option<SourceCheckExpr>,
441 ) -> Result<Self, SchemaContractError> {
442 if crate::is_engine_reserved_schema_name(name.as_str()) {
443 return Err(SchemaContractError::InvalidSourceKey);
444 }
445 if key.is_empty() {
446 return Err(SchemaContractError::InvalidReferenceList);
447 }
448 if let Some(predicate) = &predicate {
449 predicate.validate()?;
450 }
451 Ok(Self {
452 source_key: IndexSourceKey::from_name(&name),
453 name,
454 key,
455 unique,
456 predicate,
457 })
458 }
459
460 #[must_use]
462 pub const fn source_key(&self) -> &IndexSourceKey {
463 &self.source_key
464 }
465
466 #[must_use]
468 pub const fn name(&self) -> &SchemaName {
469 &self.name
470 }
471
472 #[must_use]
474 pub fn key(&self) -> &[IndexKeyFragment] {
475 &self.key
476 }
477
478 #[must_use]
480 pub const fn unique(&self) -> bool {
481 self.unique
482 }
483
484 #[must_use]
486 pub const fn predicate(&self) -> Option<&SourceCheckExpr> {
487 self.predicate.as_ref()
488 }
489
490 fn validate(&self) -> Result<(), SchemaContractError> {
491 let rebuilt = Self::try_new(
492 self.name.clone(),
493 self.key.clone(),
494 self.unique,
495 self.predicate.clone(),
496 )?;
497 ensure_canonical_rebuild(self, &rebuilt)
498 }
499}
500
501#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
503pub enum RelationDeleteAction {
504 Restrict,
506}
507
508#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
510pub enum RelationPathStepFragment {
511 EnterNamed { r#type: TypeSourceKey },
513 OptionalSome,
515 RecordMember {
517 #[serde(
519 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
520 )]
521 record: TypeSourceKey,
522 #[serde(
524 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
525 )]
526 field: FieldSourceKey,
527 },
528 EnumVariantPayload {
530 r#enum: TypeSourceKey,
532 #[serde(
534 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
535 )]
536 variant: TypeSourceKey,
537 },
538 ListItems,
540 SetItems,
542 MapValues,
544}
545
546#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
548pub enum RelationSourceFragment {
549 Direct {
551 #[serde(
552 deserialize_with = "crate::codec::deserialize_keys::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
553 )]
554 fields: Vec<FieldSourceKey>,
555 },
556 Nested {
558 #[serde(
560 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
561 )]
562 root: FieldSourceKey,
563 #[serde(
565 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_RELATION_PATH_STEPS }>"
566 )]
567 steps: Vec<RelationPathStepFragment>,
568 },
569}
570
571impl RelationSourceFragment {
572 #[must_use]
574 pub const fn direct(fields: Vec<FieldSourceKey>) -> Self {
575 Self::Direct { fields }
576 }
577
578 pub fn root_fields(&self) -> impl Iterator<Item = &FieldSourceKey> {
580 let direct = match self {
581 Self::Direct { fields } => fields.as_slice(),
582 Self::Nested { .. } => &[],
583 };
584 let nested = match self {
585 Self::Nested { root, .. } => Some(root),
586 Self::Direct { .. } => None,
587 };
588 direct.iter().chain(nested)
589 }
590
591 fn validate(&self, target_field_count: usize) -> Result<(), SchemaContractError> {
592 match self {
593 Self::Direct { fields } => {
594 if fields.is_empty() || fields.len() != target_field_count {
595 return Err(SchemaContractError::InvalidReferenceList);
596 }
597 ensure_unique(fields)
598 }
599 Self::Nested { steps, .. } => {
600 if steps.is_empty()
601 || steps.len() > MAX_RELATION_PATH_STEPS
602 || target_field_count != 1
603 {
604 return Err(SchemaContractError::InvalidReferenceList);
605 }
606 Ok(())
607 }
608 }
609 }
610}
611
612#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
614pub struct RelationFragment {
615 #[serde(
616 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
617 )]
618 source_key: RelationSourceKey,
619 #[serde(
620 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
621 )]
622 name: SchemaName,
623 source: RelationSourceFragment,
624 #[serde(
625 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
626 )]
627 target_entity: EntitySourceKey,
628 #[serde(
629 deserialize_with = "crate::codec::deserialize_keys::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
630 )]
631 target_fields: Vec<FieldSourceKey>,
632 on_delete: RelationDeleteAction,
633}
634
635impl RelationFragment {
636 pub fn try_new(
643 name: SchemaName,
644 source: RelationSourceFragment,
645 target_entity: EntitySourceKey,
646 target_fields: Vec<FieldSourceKey>,
647 on_delete: RelationDeleteAction,
648 ) -> Result<Self, SchemaContractError> {
649 if target_fields.is_empty() {
650 return Err(SchemaContractError::InvalidReferenceList);
651 }
652 source.validate(target_fields.len())?;
653 ensure_unique(&target_fields)?;
654 Ok(Self {
655 source_key: RelationSourceKey::from_name(&name),
656 name,
657 source,
658 target_entity,
659 target_fields,
660 on_delete,
661 })
662 }
663
664 #[must_use]
666 pub const fn source_key(&self) -> &RelationSourceKey {
667 &self.source_key
668 }
669
670 #[must_use]
672 pub const fn name(&self) -> &SchemaName {
673 &self.name
674 }
675
676 #[must_use]
678 pub const fn source(&self) -> &RelationSourceFragment {
679 &self.source
680 }
681
682 #[must_use]
684 pub const fn target_entity(&self) -> &EntitySourceKey {
685 &self.target_entity
686 }
687
688 #[must_use]
690 pub fn target_fields(&self) -> &[FieldSourceKey] {
691 &self.target_fields
692 }
693
694 #[must_use]
696 pub const fn on_delete(&self) -> RelationDeleteAction {
697 self.on_delete
698 }
699
700 fn validate(&self) -> Result<(), SchemaContractError> {
701 let rebuilt = Self::try_new(
702 self.name.clone(),
703 self.source.clone(),
704 self.target_entity.clone(),
705 self.target_fields.clone(),
706 self.on_delete,
707 )?;
708 ensure_canonical_rebuild(self, &rebuilt)
709 }
710}
711
712#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
714pub enum ConstraintFragmentKind {
715 Check(SourceCheckExpr),
717 TargetedRule(TargetedRuleFragment),
719}
720
721impl ConstraintFragmentKind {
722 fn validate(&self) -> Result<(), SchemaContractError> {
723 match self {
724 Self::Check(expression) => expression.validate(),
725 Self::TargetedRule(rule) => rule.validate(),
726 }
727 }
728}
729
730#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
732pub struct TargetedRuleFragment {
733 #[serde(
734 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
735 )]
736 root: FieldSourceKey,
737 #[serde(
738 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
739 )]
740 target_type: TypeSourceKey,
741 #[serde(
742 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
743 )]
744 rule: RuleSourceKey,
745 operation: SourceRuleOperation,
746}
747
748impl TargetedRuleFragment {
749 #[must_use]
751 pub fn new(
752 root: FieldSourceKey,
753 target_type: TypeSourceKey,
754 rule: SchemaName,
755 operation: SourceRuleOperation,
756 ) -> Self {
757 Self {
758 root,
759 target_type,
760 rule: RuleSourceKey::from_name(&rule),
761 operation,
762 }
763 }
764
765 #[must_use]
767 pub const fn root(&self) -> &FieldSourceKey {
768 &self.root
769 }
770
771 #[must_use]
773 pub const fn target_type(&self) -> &TypeSourceKey {
774 &self.target_type
775 }
776
777 #[must_use]
779 pub const fn rule(&self) -> &RuleSourceKey {
780 &self.rule
781 }
782
783 #[must_use]
785 pub const fn operation(&self) -> &SourceRuleOperation {
786 &self.operation
787 }
788
789 fn validate(&self) -> Result<(), SchemaContractError> {
790 self.operation.validate()
791 }
792}
793
794#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
796pub struct ConstraintFragment {
797 #[serde(
798 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
799 )]
800 source_key: ConstraintSourceKey,
801 #[serde(
802 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
803 )]
804 name: SchemaName,
805 kind: ConstraintFragmentKind,
806}
807
808impl ConstraintFragment {
809 #[must_use]
811 pub fn check(name: SchemaName, expression: SourceCheckExpr) -> Self {
812 Self {
813 source_key: ConstraintSourceKey::from_name(&name),
814 name,
815 kind: ConstraintFragmentKind::Check(expression),
816 }
817 }
818
819 #[must_use]
821 pub fn targeted_rule(rule: TargetedRuleFragment) -> Self {
822 let source_key = ConstraintSourceKey::for_targeted_field_rule(
823 rule.root(),
824 rule.target_type(),
825 rule.rule(),
826 );
827 let name = SchemaName::for_targeted_rule(&source_key);
828 Self {
829 source_key,
830 name,
831 kind: ConstraintFragmentKind::TargetedRule(rule),
832 }
833 }
834
835 #[must_use]
837 pub const fn source_key(&self) -> &ConstraintSourceKey {
838 &self.source_key
839 }
840
841 #[must_use]
843 pub const fn name(&self) -> &SchemaName {
844 &self.name
845 }
846
847 #[must_use]
849 pub const fn kind(&self) -> &ConstraintFragmentKind {
850 &self.kind
851 }
852
853 fn validate(&self) -> Result<(), SchemaContractError> {
854 self.kind.validate()?;
855 let rebuilt = match &self.kind {
856 ConstraintFragmentKind::Check(expression) => {
857 if crate::is_engine_reserved_schema_name(self.name.as_str()) {
858 return Err(SchemaContractError::InvalidSourceKey);
859 }
860 Self::check(self.name.clone(), expression.clone())
861 }
862 ConstraintFragmentKind::TargetedRule(rule) => Self::targeted_rule(rule.clone()),
863 };
864 ensure_canonical_rebuild(self, &rebuilt)
865 }
866}
867
868#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
870pub struct EntityFragment {
871 #[serde(
872 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
873 )]
874 source_key: EntitySourceKey,
875 #[serde(
876 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
877 )]
878 name: SchemaName,
879 version: DeclaredEntityVersion,
880 #[serde(
881 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
882 )]
883 fields: Vec<FieldFragment>,
884 #[serde(
885 deserialize_with = "crate::codec::deserialize_keys::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
886 )]
887 primary_key: Vec<FieldSourceKey>,
888 #[serde(
889 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_INDEXES }>"
890 )]
891 indexes: Vec<IndexFragment>,
892 #[serde(
893 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_RELATIONS }>"
894 )]
895 relations: Vec<RelationFragment>,
896 #[serde(
897 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_CONSTRAINTS }>"
898 )]
899 constraints: Vec<ConstraintFragment>,
900}
901
902impl EntityFragment {
903 pub fn try_new(
912 name: SchemaName,
913 version: DeclaredEntityVersion,
914 mut fields: Vec<FieldFragment>,
915 primary_key: Vec<FieldSourceKey>,
916 mut indexes: Vec<IndexFragment>,
917 mut relations: Vec<RelationFragment>,
918 mut constraints: Vec<ConstraintFragment>,
919 ) -> Result<Self, SchemaContractError> {
920 let source_key = EntitySourceKey::from_name(&name);
921 check_len("entity fields", fields.len(), MAX_FRAGMENT_FIELDS)?;
922 check_len("entity indexes", indexes.len(), MAX_FRAGMENT_INDEXES)?;
923 check_len("entity relations", relations.len(), MAX_FRAGMENT_RELATIONS)?;
924 check_len(
925 "entity constraints",
926 constraints.len(),
927 MAX_FRAGMENT_CONSTRAINTS,
928 )?;
929 if primary_key.is_empty() {
930 return Err(SchemaContractError::InvalidReferenceList);
931 }
932 ensure_unique(&primary_key)?;
933 crate::compact_sort_unstable_by(&mut fields, |a, b| a.source_key.cmp(&b.source_key));
936 crate::compact_sort_unstable_by(&mut indexes, |a, b| a.source_key.cmp(&b.source_key));
937 crate::compact_sort_unstable_by(&mut relations, |a, b| a.source_key.cmp(&b.source_key));
938 crate::compact_sort_unstable_by(&mut constraints, |a, b| a.source_key.cmp(&b.source_key));
939 ensure_unique_sorted_by(&fields, FieldFragment::source_key)?;
940 ensure_unique_sorted_by(&indexes, IndexFragment::source_key)?;
941 ensure_unique_sorted_by(&relations, RelationFragment::source_key)?;
942 ensure_unique_sorted_by(&constraints, ConstraintFragment::source_key)?;
943 ensure_unique_names(fields.iter().map(FieldFragment::name))?;
944 ensure_unique_names(indexes.iter().map(IndexFragment::name))?;
945 ensure_unique_names(relations.iter().map(RelationFragment::name))?;
946 ensure_unique_names(constraints.iter().map(ConstraintFragment::name))?;
947 for field in &fields {
948 field.validate()?;
949 }
950 validate_management_cardinality(&fields)?;
951 for index in &indexes {
952 index.validate()?;
953 }
954 for relation in &relations {
955 relation.validate()?;
956 }
957 for constraint in &constraints {
958 constraint.validate()?;
959 }
960 let field_keys = fields
961 .iter()
962 .map(|field| field.source_key.clone())
963 .collect::<BTreeSet<_>>();
964 if primary_key.iter().any(|field| !field_keys.contains(field)) {
965 return Err(SchemaContractError::InvalidLocalReference);
966 }
967 validate_insert_generation(&fields, &primary_key)?;
968 validate_primary_key_field_shapes(&fields, &primary_key)?;
969 for index in &indexes {
970 if index
971 .key()
972 .iter()
973 .any(|component| !field_keys.contains(component.field()))
974 || index.predicate().is_some_and(|predicate| {
975 predicate
976 .dependencies()
977 .iter()
978 .any(|field| !field_keys.contains(field))
979 })
980 {
981 return Err(SchemaContractError::InvalidLocalReference);
982 }
983 }
984 for relation in &relations {
985 if relation
986 .source()
987 .root_fields()
988 .any(|field| !field_keys.contains(field))
989 || (relation.target_entity() == &source_key
990 && relation
991 .target_fields()
992 .iter()
993 .any(|field| !field_keys.contains(field)))
994 {
995 return Err(SchemaContractError::InvalidLocalReference);
996 }
997 }
998 for constraint in &constraints {
999 let invalid = match constraint.kind() {
1000 ConstraintFragmentKind::Check(expression) => expression
1001 .dependencies()
1002 .iter()
1003 .any(|field| !field_keys.contains(field)),
1004 ConstraintFragmentKind::TargetedRule(rule) => !field_keys.contains(rule.root()),
1005 };
1006 if invalid {
1007 return Err(SchemaContractError::InvalidLocalReference);
1008 }
1009 }
1010 Ok(Self {
1011 source_key,
1012 name,
1013 version,
1014 fields,
1015 primary_key,
1016 indexes,
1017 relations,
1018 constraints,
1019 })
1020 }
1021
1022 #[must_use]
1024 pub const fn source_key(&self) -> &EntitySourceKey {
1025 &self.source_key
1026 }
1027
1028 #[must_use]
1030 pub const fn name(&self) -> &SchemaName {
1031 &self.name
1032 }
1033
1034 #[must_use]
1036 pub const fn version(&self) -> DeclaredEntityVersion {
1037 self.version
1038 }
1039
1040 #[must_use]
1042 pub fn fields(&self) -> &[FieldFragment] {
1043 &self.fields
1044 }
1045
1046 #[must_use]
1048 pub fn primary_key(&self) -> &[FieldSourceKey] {
1049 &self.primary_key
1050 }
1051
1052 #[must_use]
1054 pub fn indexes(&self) -> &[IndexFragment] {
1055 &self.indexes
1056 }
1057
1058 #[must_use]
1060 pub fn relations(&self) -> &[RelationFragment] {
1061 &self.relations
1062 }
1063
1064 #[must_use]
1066 pub fn constraints(&self) -> &[ConstraintFragment] {
1067 &self.constraints
1068 }
1069
1070 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
1071 let rebuilt = Self::try_new(
1072 self.name.clone(),
1073 self.version,
1074 self.fields.clone(),
1075 self.primary_key.clone(),
1076 self.indexes.clone(),
1077 self.relations.clone(),
1078 self.constraints.clone(),
1079 )?;
1080 ensure_canonical_rebuild(self, &rebuilt)
1081 }
1082}
1083
1084fn validate_primary_key_field_shapes(
1087 fields: &[FieldFragment],
1088 primary_key: &[FieldSourceKey],
1089) -> Result<(), SchemaContractError> {
1090 if fields.iter().any(|field| {
1091 primary_key.contains(&field.source_key)
1092 && (field.nullable
1093 || !matches!(field.field_type, FieldType::Scalar(scalar)
1094 if scalar.kind().is_primary_key_component_encodable()
1095 && (primary_key.len() == 1 || scalar != ScalarType::Unit)))
1096 }) {
1097 return Err(SchemaContractError::InvalidFieldPolicy);
1098 }
1099 Ok(())
1100}
1101
1102fn validate_insert_generation(
1106 fields: &[FieldFragment],
1107 primary_key: &[FieldSourceKey],
1108) -> Result<(), SchemaContractError> {
1109 for field in fields {
1110 if !matches!(field.insert_policy(), FieldInsertPolicy::Generated) {
1111 continue;
1112 }
1113 if field.nullable() || field.management().is_some() {
1114 return Err(SchemaContractError::InvalidFieldPolicy);
1115 }
1116 match field.field_type() {
1117 FieldType::Scalar(ScalarType::Ulid | ScalarType::Timestamp) => {}
1118 FieldType::Scalar(
1119 ScalarType::Nat8
1120 | ScalarType::Nat16
1121 | ScalarType::Nat32
1122 | ScalarType::Nat64
1123 | ScalarType::Nat128,
1124 ) if primary_key.len() == 1 && primary_key.first() == Some(field.source_key()) => {}
1125 FieldType::Scalar(_) | FieldType::List(_) | FieldType::Named(_) => {
1126 return Err(SchemaContractError::InvalidFieldPolicy);
1127 }
1128 }
1129 }
1130 Ok(())
1131}
1132
1133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1138pub struct RecordFieldFragment {
1139 #[serde(
1140 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1141 )]
1142 source_key: FieldSourceKey,
1143 #[serde(
1144 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1145 )]
1146 name: SchemaName,
1147 field_type: FieldType,
1148 nullable: bool,
1149}
1150
1151impl RecordFieldFragment {
1152 #[must_use]
1154 pub fn new(name: SchemaName, field_type: FieldType, nullable: bool) -> Self {
1155 Self {
1156 source_key: FieldSourceKey::from_name(&name),
1157 name,
1158 field_type,
1159 nullable,
1160 }
1161 }
1162
1163 #[must_use]
1165 pub const fn source_key(&self) -> &FieldSourceKey {
1166 &self.source_key
1167 }
1168
1169 #[must_use]
1171 pub const fn name(&self) -> &SchemaName {
1172 &self.name
1173 }
1174
1175 #[must_use]
1177 pub const fn field_type(&self) -> &FieldType {
1178 &self.field_type
1179 }
1180
1181 #[must_use]
1183 pub const fn nullable(&self) -> bool {
1184 self.nullable
1185 }
1186
1187 fn validate(&self) -> Result<(), SchemaContractError> {
1188 if !current_name_key_matches(self.source_key.as_str(), &self.name) {
1189 return Err(SchemaContractError::NonCanonical);
1190 }
1191 self.field_type.validate()
1192 }
1193}
1194
1195#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1198pub struct TupleElementFragment {
1199 field_type: FieldType,
1200 nullable: bool,
1201}
1202
1203impl TupleElementFragment {
1204 #[must_use]
1206 pub const fn new(field_type: FieldType, nullable: bool) -> Self {
1207 Self {
1208 field_type,
1209 nullable,
1210 }
1211 }
1212
1213 #[must_use]
1215 pub const fn field_type(&self) -> &FieldType {
1216 &self.field_type
1217 }
1218
1219 #[must_use]
1221 pub const fn nullable(&self) -> bool {
1222 self.nullable
1223 }
1224
1225 const fn validate(&self) -> Result<(), SchemaContractError> {
1226 self.field_type.validate()
1227 }
1228}
1229
1230#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1232pub struct RecordTypeFragment {
1233 #[serde(
1234 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1235 )]
1236 source_key: TypeSourceKey,
1237 #[serde(
1238 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1239 )]
1240 name: SchemaName,
1241 #[serde(
1242 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
1243 )]
1244 fields: Vec<RecordFieldFragment>,
1245}
1246
1247impl RecordTypeFragment {
1248 pub fn try_new(
1255 name: SchemaName,
1256 mut fields: Vec<RecordFieldFragment>,
1257 ) -> Result<Self, SchemaContractError> {
1258 check_len("record fields", fields.len(), MAX_FRAGMENT_FIELDS)?;
1259 crate::compact_sort_unstable_by(&mut fields, |left, right| {
1260 left.source_key.cmp(&right.source_key)
1261 });
1262 ensure_unique_sorted_by(&fields, RecordFieldFragment::source_key)?;
1263 ensure_unique_names(fields.iter().map(RecordFieldFragment::name))?;
1264 for field in &fields {
1265 field.validate()?;
1266 }
1267 Ok(Self {
1268 source_key: TypeSourceKey::from_name(&name),
1269 name,
1270 fields,
1271 })
1272 }
1273
1274 #[must_use]
1276 pub const fn source_key(&self) -> &TypeSourceKey {
1277 &self.source_key
1278 }
1279
1280 #[must_use]
1282 pub const fn name(&self) -> &SchemaName {
1283 &self.name
1284 }
1285
1286 #[must_use]
1288 pub fn fields(&self) -> &[RecordFieldFragment] {
1289 &self.fields
1290 }
1291
1292 fn validate(&self) -> Result<(), SchemaContractError> {
1293 let rebuilt = Self::try_new(self.name.clone(), self.fields.clone())?;
1294 if rebuilt != *self {
1295 return Err(SchemaContractError::NonCanonical);
1296 }
1297 Ok(())
1298 }
1299}
1300
1301#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1303pub struct EnumVariantFragment {
1304 #[serde(
1305 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1306 )]
1307 source_key: TypeSourceKey,
1308 #[serde(
1309 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1310 )]
1311 name: SchemaName,
1312 payload: Option<FieldType>,
1313}
1314
1315impl EnumVariantFragment {
1316 #[must_use]
1318 pub fn new(name: SchemaName) -> Self {
1319 Self {
1320 source_key: TypeSourceKey::from_name(&name),
1321 name,
1322 payload: None,
1323 }
1324 }
1325
1326 #[must_use]
1328 pub fn with_payload(name: SchemaName, payload: FieldType) -> Self {
1329 Self {
1330 source_key: TypeSourceKey::from_name(&name),
1331 name,
1332 payload: Some(payload),
1333 }
1334 }
1335
1336 #[must_use]
1338 pub const fn source_key(&self) -> &TypeSourceKey {
1339 &self.source_key
1340 }
1341
1342 #[must_use]
1344 pub const fn name(&self) -> &SchemaName {
1345 &self.name
1346 }
1347
1348 #[must_use]
1350 pub const fn payload(&self) -> Option<&FieldType> {
1351 self.payload.as_ref()
1352 }
1353
1354 fn validate(&self) -> Result<(), SchemaContractError> {
1355 if !current_name_key_matches(self.source_key.as_str(), &self.name) {
1356 return Err(SchemaContractError::NonCanonical);
1357 }
1358 match &self.payload {
1359 Some(payload) => payload.validate(),
1360 None => Ok(()),
1361 }
1362 }
1363}
1364
1365#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1367pub struct EnumTypeFragment {
1368 #[serde(
1369 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1370 )]
1371 source_key: TypeSourceKey,
1372 #[serde(
1373 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1374 )]
1375 name: SchemaName,
1376 #[serde(
1377 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
1378 )]
1379 variants: Vec<EnumVariantFragment>,
1380}
1381
1382impl EnumTypeFragment {
1383 pub fn try_new(
1390 name: SchemaName,
1391 mut variants: Vec<EnumVariantFragment>,
1392 ) -> Result<Self, SchemaContractError> {
1393 if variants.is_empty() {
1394 return Err(SchemaContractError::InvalidReferenceList);
1395 }
1396 check_len("enum variants", variants.len(), MAX_FRAGMENT_FIELDS)?;
1397 crate::compact_sort_unstable_by(&mut variants, |left, right| {
1398 left.source_key.cmp(&right.source_key)
1399 });
1400 ensure_unique_sorted_by(&variants, |variant| &variant.source_key)?;
1401 ensure_unique_names(variants.iter().map(EnumVariantFragment::name))?;
1402 for variant in &variants {
1403 variant.validate()?;
1404 }
1405 Ok(Self {
1406 source_key: TypeSourceKey::from_name(&name),
1407 name,
1408 variants,
1409 })
1410 }
1411
1412 #[must_use]
1414 pub const fn source_key(&self) -> &TypeSourceKey {
1415 &self.source_key
1416 }
1417
1418 #[must_use]
1420 pub const fn name(&self) -> &SchemaName {
1421 &self.name
1422 }
1423
1424 #[must_use]
1426 pub fn variants(&self) -> &[EnumVariantFragment] {
1427 &self.variants
1428 }
1429
1430 fn validate(&self) -> Result<(), SchemaContractError> {
1431 let rebuilt = Self::try_new(self.name.clone(), self.variants.clone())?;
1432 if rebuilt != *self {
1433 return Err(SchemaContractError::NonCanonical);
1434 }
1435 Ok(())
1436 }
1437}
1438
1439#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1441pub enum NamedTypeFragment {
1442 Record(RecordTypeFragment),
1444 Enum(EnumTypeFragment),
1446 Newtype {
1448 #[serde(
1450 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1451 )]
1452 source_key: TypeSourceKey,
1453 #[serde(
1455 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1456 )]
1457 name: SchemaName,
1458 inner: FieldType,
1460 },
1461 List {
1463 #[serde(
1465 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1466 )]
1467 source_key: TypeSourceKey,
1468 #[serde(
1470 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1471 )]
1472 name: SchemaName,
1473 item: FieldType,
1475 },
1476 Set {
1478 #[serde(
1480 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1481 )]
1482 source_key: TypeSourceKey,
1483 #[serde(
1485 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1486 )]
1487 name: SchemaName,
1488 item: FieldType,
1490 },
1491 Map {
1493 #[serde(
1495 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1496 )]
1497 source_key: TypeSourceKey,
1498 #[serde(
1500 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1501 )]
1502 name: SchemaName,
1503 key: FieldType,
1505 value: FieldType,
1507 },
1508 Tuple {
1510 #[serde(
1512 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SOURCE_KEY_BYTES }>"
1513 )]
1514 source_key: TypeSourceKey,
1515 #[serde(
1517 deserialize_with = "crate::codec::deserialize_key::<_, _, { crate::MAX_SCHEMA_NAME_BYTES }>"
1518 )]
1519 name: SchemaName,
1520 #[serde(
1522 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_FIELDS }>"
1523 )]
1524 members: Vec<TupleElementFragment>,
1525 },
1526}
1527
1528impl NamedTypeFragment {
1529 pub fn append_named_type_dependencies(&self, pending: &mut Vec<TypeSourceKey>) {
1535 match self {
1536 Self::Record(record) => {
1537 for field in record.fields() {
1538 field.field_type().append_named_type_dependencies(pending);
1539 }
1540 }
1541 Self::Enum(r#enum) => {
1542 for variant in r#enum.variants() {
1543 if let Some(payload) = variant.payload() {
1544 payload.append_named_type_dependencies(pending);
1545 }
1546 }
1547 }
1548 Self::Newtype { inner, .. }
1549 | Self::List { item: inner, .. }
1550 | Self::Set { item: inner, .. } => {
1551 inner.append_named_type_dependencies(pending);
1552 }
1553 Self::Map { key, value, .. } => {
1554 key.append_named_type_dependencies(pending);
1555 value.append_named_type_dependencies(pending);
1556 }
1557 Self::Tuple { members, .. } => {
1558 for member in members {
1559 member.field_type().append_named_type_dependencies(pending);
1560 }
1561 }
1562 }
1563 }
1564
1565 #[must_use]
1567 pub fn newtype(name: SchemaName, inner: FieldType) -> Self {
1568 Self::Newtype {
1569 source_key: TypeSourceKey::from_name(&name),
1570 name,
1571 inner,
1572 }
1573 }
1574
1575 #[must_use]
1577 pub fn list(name: SchemaName, item: FieldType) -> Self {
1578 Self::List {
1579 source_key: TypeSourceKey::from_name(&name),
1580 name,
1581 item,
1582 }
1583 }
1584
1585 #[must_use]
1587 pub fn set(name: SchemaName, item: FieldType) -> Self {
1588 Self::Set {
1589 source_key: TypeSourceKey::from_name(&name),
1590 name,
1591 item,
1592 }
1593 }
1594
1595 #[must_use]
1597 pub fn map(name: SchemaName, key: FieldType, value: FieldType) -> Self {
1598 Self::Map {
1599 source_key: TypeSourceKey::from_name(&name),
1600 name,
1601 key,
1602 value,
1603 }
1604 }
1605
1606 #[must_use]
1608 pub fn tuple(name: SchemaName, members: Vec<TupleElementFragment>) -> Self {
1609 Self::Tuple {
1610 source_key: TypeSourceKey::from_name(&name),
1611 name,
1612 members,
1613 }
1614 }
1615
1616 #[must_use]
1618 pub const fn source_key(&self) -> &TypeSourceKey {
1619 match self {
1620 Self::Record(record) => record.source_key(),
1621 Self::Enum(r#enum) => r#enum.source_key(),
1622 Self::Newtype { source_key, .. }
1623 | Self::List { source_key, .. }
1624 | Self::Set { source_key, .. }
1625 | Self::Map { source_key, .. }
1626 | Self::Tuple { source_key, .. } => source_key,
1627 }
1628 }
1629
1630 #[must_use]
1632 pub const fn name(&self) -> &SchemaName {
1633 match self {
1634 Self::Record(record) => record.name(),
1635 Self::Enum(r#enum) => r#enum.name(),
1636 Self::Newtype { name, .. }
1637 | Self::List { name, .. }
1638 | Self::Set { name, .. }
1639 | Self::Map { name, .. }
1640 | Self::Tuple { name, .. } => name,
1641 }
1642 }
1643
1644 fn validate(&self) -> Result<(), SchemaContractError> {
1645 ensure_current_name_key(self.source_key().as_str(), self.name())?;
1646 match self {
1647 Self::Record(record) => record.validate(),
1648 Self::Enum(r#enum) => r#enum.validate(),
1649 Self::Newtype { inner, .. }
1650 | Self::List { item: inner, .. }
1651 | Self::Set { item: inner, .. } => inner.validate(),
1652 Self::Map { key, value, .. } => {
1653 key.validate()?;
1654 value.validate()
1655 }
1656 Self::Tuple { members, .. } => {
1657 if members.is_empty() {
1658 return Err(SchemaContractError::InvalidReferenceList);
1659 }
1660 check_len("tuple members", members.len(), MAX_FRAGMENT_FIELDS)?;
1661 members.iter().try_for_each(TupleElementFragment::validate)
1662 }
1663 }
1664 }
1665}
1666
1667#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1669pub struct SchemaFragment {
1670 #[serde(
1671 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_ENTITIES }>"
1672 )]
1673 entities: Vec<EntityFragment>,
1674 #[serde(
1675 deserialize_with = "crate::codec::deserialize_list::<_, _, { crate::MAX_FRAGMENT_TYPES }>"
1676 )]
1677 types: Vec<NamedTypeFragment>,
1678}
1679
1680impl SchemaFragment {
1681 pub fn try_new(
1688 mut entities: Vec<EntityFragment>,
1689 mut types: Vec<NamedTypeFragment>,
1690 ) -> Result<Self, SchemaContractError> {
1691 check_len("fragment entities", entities.len(), MAX_FRAGMENT_ENTITIES)?;
1692 check_len("fragment types", types.len(), MAX_FRAGMENT_TYPES)?;
1693 crate::compact_sort_unstable_by(&mut entities, |left, right| {
1694 left.source_key.cmp(&right.source_key)
1695 });
1696 crate::compact_sort_unstable_by(&mut types, |left, right| {
1697 left.source_key().cmp(right.source_key())
1698 });
1699 ensure_unique_sorted_by(&entities, EntityFragment::source_key)?;
1700 ensure_unique_sorted_by(&types, NamedTypeFragment::source_key)?;
1701 ensure_unique_names(entities.iter().map(EntityFragment::name))?;
1702 ensure_unique_names(types.iter().map(NamedTypeFragment::name))?;
1703 for entity in &entities {
1704 entity.validate()?;
1705 }
1706 for r#type in &types {
1707 r#type.validate()?;
1708 }
1709 Ok(Self { entities, types })
1710 }
1711
1712 #[must_use]
1714 pub fn entities(&self) -> &[EntityFragment] {
1715 &self.entities
1716 }
1717
1718 #[must_use]
1720 pub fn types(&self) -> &[NamedTypeFragment] {
1721 &self.types
1722 }
1723
1724 pub(crate) fn validate(&self) -> Result<(), SchemaContractError> {
1725 for r#type in &self.types {
1726 r#type.validate()?;
1727 }
1728 let rebuilt = Self::try_new(self.entities.clone(), self.types.clone())?;
1729 if rebuilt != *self {
1730 return Err(SchemaContractError::NonCanonical);
1731 }
1732 Ok(())
1733 }
1734}
1735
1736pub(crate) const fn check_len(
1737 kind: &'static str,
1738 len: usize,
1739 max: usize,
1740) -> Result<(), SchemaContractError> {
1741 if len > max {
1742 return Err(SchemaContractError::TooManyItems { kind, len, max });
1743 }
1744 Ok(())
1745}
1746
1747fn current_name_key_matches(source_key: &str, name: &SchemaName) -> bool {
1748 source_key == name.as_str()
1749}
1750
1751fn ensure_current_name_key(source_key: &str, name: &SchemaName) -> Result<(), SchemaContractError> {
1752 if !current_name_key_matches(source_key, name) {
1753 return Err(SchemaContractError::NonCanonical);
1754 }
1755 Ok(())
1756}
1757
1758fn ensure_canonical_rebuild<T: PartialEq>(
1759 current: &T,
1760 rebuilt: &T,
1761) -> Result<(), SchemaContractError> {
1762 if current != rebuilt {
1763 return Err(SchemaContractError::NonCanonical);
1764 }
1765 Ok(())
1766}
1767
1768fn ensure_unique<T>(values: &[T]) -> Result<(), SchemaContractError>
1769where
1770 T: Ord,
1771{
1772 let mut seen = BTreeSet::new();
1773 if values.iter().any(|value| !seen.insert(value)) {
1774 return Err(SchemaContractError::InvalidReferenceList);
1775 }
1776 Ok(())
1777}
1778
1779fn ensure_unique_sorted_by<T, K>(
1780 values: &[T],
1781 key: impl Fn(&T) -> &K,
1782) -> Result<(), SchemaContractError>
1783where
1784 K: Eq,
1785{
1786 if values.windows(2).any(|pair| key(&pair[0]) == key(&pair[1])) {
1787 return Err(SchemaContractError::DuplicateSourceKey);
1788 }
1789 Ok(())
1790}
1791
1792fn ensure_unique_names<'a>(
1793 names: impl IntoIterator<Item = &'a SchemaName>,
1794) -> Result<(), SchemaContractError> {
1795 let mut seen = BTreeSet::new();
1796 if names.into_iter().any(|name| !seen.insert(name)) {
1797 return Err(SchemaContractError::DuplicateName);
1798 }
1799 Ok(())
1800}
1801
1802fn validate_management_cardinality(fields: &[FieldFragment]) -> Result<(), SchemaContractError> {
1803 for policy in [
1804 FieldManagementPolicy::CreatedAt,
1805 FieldManagementPolicy::UpdatedAt,
1806 ] {
1807 if fields
1808 .iter()
1809 .filter(|field| field.management() == Some(policy))
1810 .count()
1811 > 1
1812 {
1813 return Err(SchemaContractError::InvalidFieldPolicy);
1814 }
1815 }
1816 Ok(())
1817}
1818
1819fn decimal_fits_scale(value: Decimal, scale: u32) -> bool {
1820 match value.scale().cmp(&scale) {
1821 std::cmp::Ordering::Equal | std::cmp::Ordering::Greater => true,
1822 std::cmp::Ordering::Less => value.scale_to_integer(scale).is_some(),
1823 }
1824}
1825
1826#[cfg(test)]
1827mod tests {
1828 use super::{
1829 FieldFragment, FieldInsertPolicy, FieldSourceKey, FieldType, ScalarType,
1830 SchemaContractError, SchemaName,
1831 };
1832
1833 #[test]
1834 fn independently_decoded_field_key_and_name_must_match() {
1835 let field = FieldFragment {
1836 source_key: FieldSourceKey::try_new("legacy_name").expect("fixture key should admit"),
1837 name: SchemaName::try_new("current_name").expect("fixture name should admit"),
1838 field_type: FieldType::Scalar(ScalarType::Nat64),
1839 nullable: false,
1840 insert_policy: FieldInsertPolicy::Required,
1841 management: None,
1842 };
1843
1844 assert_eq!(field.validate(), Err(SchemaContractError::NonCanonical));
1845 }
1846}