1use core::fmt;
7use core::marker::PhantomData;
8
9pub use crate::schema::{Schema, SchemaPackage, Unbound, sealed};
10
11#[derive(Clone, Debug, Eq, PartialEq, Default)]
13pub struct ValidationPath {
14 segments: Vec<String>,
15}
16
17impl ValidationPath {
18 #[must_use]
20 pub fn root() -> Self {
21 Self {
22 segments: Vec::new(),
23 }
24 }
25
26 #[must_use]
28 pub fn join(&self, segment: impl Into<String>) -> Self {
29 let mut s = self.segments.clone();
30 s.push(segment.into());
31 Self { segments: s }
32 }
33
34 #[must_use]
36 pub fn join_index(&self, index: usize) -> Self {
37 let mut s = self.segments.clone();
38 if let Some(last) = s.last_mut() {
39 last.push_str(&format!("[{index}]"));
40 } else {
41 s.push(format!("[{index}]"));
42 }
43 Self { segments: s }
44 }
45
46 #[must_use]
48 pub fn path(&self) -> String {
49 self.segments.join(".")
50 }
51}
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct ValidationError {
56 path: String,
57 code: String,
58}
59
60impl ValidationError {
61 #[must_use]
63 pub fn new(path: impl Into<String>, code: impl Into<String>) -> Self {
64 Self {
65 path: path.into(),
66 code: code.into(),
67 }
68 }
69
70 #[must_use]
72 pub fn field(&self) -> &str {
73 &self.path
74 }
75
76 #[must_use]
78 pub fn code(&self) -> &str {
79 &self.code
80 }
81}
82
83impl fmt::Display for ValidationError {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 if self.path.is_empty() {
86 write!(formatter, "{}", self.code)
87 } else {
88 write!(formatter, "{}: {}", self.path, self.code)
89 }
90 }
91}
92
93impl std::error::Error for ValidationError {}
94
95#[derive(Clone, Copy, Debug, Eq, PartialEq)]
97pub struct Cardinality {
98 min: u64,
99 max: Option<u64>,
100}
101
102impl Cardinality {
103 #[must_use]
105 pub const fn new(min: u64, max: Option<u64>) -> Self {
106 Self { min, max }
107 }
108
109 #[must_use]
111 pub const fn min(self) -> u64 {
112 self.min
113 }
114
115 #[must_use]
117 pub const fn max(self) -> Option<u64> {
118 self.max
119 }
120}
121
122#[derive(Clone, Debug, PartialEq)]
124pub struct Required<T>(T);
125
126impl<T> Required<T> {
127 #[must_use]
129 pub const fn new(value: T) -> Self {
130 Self(value)
131 }
132
133 #[must_use]
135 pub const fn get(&self) -> &T {
136 &self.0
137 }
138
139 #[must_use]
141 pub const fn value(&self) -> &T {
142 &self.0
143 }
144}
145
146impl<T> core::ops::Deref for Required<T> {
147 type Target = T;
148 fn deref(&self) -> &Self::Target {
149 &self.0
150 }
151}
152
153#[derive(Clone, Debug, PartialEq)]
155pub struct Optional<T>(Option<T>);
156
157impl<T> Optional<T> {
158 #[must_use]
160 pub const fn new(value: Option<T>) -> Self {
161 Self(value)
162 }
163
164 #[must_use]
166 pub const fn as_ref(&self) -> Option<&T> {
167 self.0.as_ref()
168 }
169}
170
171#[derive(Clone, Debug, PartialEq)]
173pub struct Sequence<T> {
174 values: Vec<T>,
175 cardinality: Cardinality,
176}
177
178impl<T> Sequence<T> {
179 pub fn try_new(
186 values: Vec<T>,
187 cardinality: Cardinality,
188 path: &ValidationPath,
189 ) -> Result<Self, ValidationError> {
190 let length = u64::try_from(values.len())
191 .map_err(|_| ValidationError::new(path.path(), "cardinality_overflow"))?;
192 if length < cardinality.min() || cardinality.max().is_some_and(|maximum| length > maximum) {
193 return Err(ValidationError::new(path.path(), "cardinality_violation"));
194 }
195 Ok(Self {
196 values,
197 cardinality,
198 })
199 }
200
201 #[must_use]
203 pub fn as_slice(&self) -> &[T] {
204 &self.values
205 }
206
207 #[must_use]
209 pub const fn cardinality(&self) -> Cardinality {
210 self.cardinality
211 }
212}
213
214#[derive(Clone, Debug, PartialEq)]
216pub enum Either<L, R> {
217 Left(L),
219 Right(R),
221}
222
223impl<L: sealed::Sealed, R: sealed::Sealed> sealed::Sealed for Either<L, R> {}
224
225impl<L: Model<Schema = S>, R: Model<Schema = S>, S: Schema> Model for Either<L, R> {
226 type Schema = S;
227 const TYPE_ID_JSON: &'static str = "either";
228}
229
230#[derive(Clone, Debug, PartialEq)]
232pub enum Never {}
233
234#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
236pub struct CanonicalDouble(u64);
237
238impl CanonicalDouble {
239 pub fn try_new(value: f64) -> Result<Self, ValidationError> {
245 if value.is_nan() || value.is_infinite() {
246 return Err(ValidationError::new("", "noncanonical_double"));
247 }
248 Ok(Self(value.to_bits()))
249 }
250
251 pub fn try_from_bits(bits: u64) -> Result<Self, ValidationError> {
257 let val = f64::from_bits(bits);
258 if val.is_nan() || val.is_infinite() {
259 return Err(ValidationError::new("", "noncanonical_double"));
260 }
261 Ok(Self(bits))
262 }
263
264 #[must_use]
266 pub fn get(self) -> f64 {
267 f64::from_bits(self.0)
268 }
269
270 #[must_use]
272 pub const fn to_bits(self) -> u64 {
273 self.0
274 }
275}
276
277macro_rules! canonical_scalar {
278 ($name:ident, $doc:literal, $code:expr, $parse_expr:expr) => {
279 #[doc = $doc]
280 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
281 pub struct $name(String);
282
283 impl $name {
284 pub fn try_new(value: impl Into<String>) -> Result<Self, ValidationError> {
291 let s = value.into();
292 if type_bridge_contract::value::CanonicalString::new(&s).is_err() {
293 return Err(ValidationError::new("", "string_limit_exceeded"));
294 }
295 let check_fn: fn(&str) -> bool = $parse_expr;
296 if !check_fn(&s) {
297 return Err(ValidationError::new("", $code));
298 }
299 Ok(Self(s))
300 }
301
302 #[must_use]
304 pub fn as_str(&self) -> &str {
305 &self.0
306 }
307 }
308 };
309}
310
311canonical_scalar!(
312 Decimal,
313 "A decimal value in the shared canonical lexical form.",
314 "noncanonical_decimal",
315 |s| {
316 if let Some(dec) = type_bridge_contract::decimal::parse_decimal(s) {
317 dec.canonical_string() == s
318 } else {
319 false
320 }
321 }
322);
323
324canonical_scalar!(
325 Date,
326 "A calendar date in the shared canonical lexical form.",
327 "noncanonical_date",
328 |s| {
329 if let Ok(d) = s.parse::<type_bridge_contract::temporal::CanonicalDate>() {
330 d.to_string() == s
331 } else {
332 false
333 }
334 }
335);
336
337canonical_scalar!(
338 DateTime,
339 "A timezone-free date-time in the shared canonical lexical form.",
340 "noncanonical_datetime",
341 |s| {
342 if let Ok(dt) = s.parse::<type_bridge_contract::temporal::CanonicalDateTime>() {
343 dt.to_string() == s
344 } else {
345 false
346 }
347 }
348);
349
350#[derive(Clone, Eq, PartialEq, Hash)]
356pub struct DateTimeTz {
357 spelling: String,
358 canonical: type_bridge_contract::temporal::CanonicalDateTimeTz,
359}
360
361impl std::fmt::Debug for DateTimeTz {
362 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363 formatter
365 .debug_tuple("DateTimeTz")
366 .field(&self.spelling)
367 .finish()
368 }
369}
370
371impl DateTimeTz {
372 pub fn try_new(value: impl Into<String>) -> Result<Self, ValidationError> {
379 let spelling = value.into();
380 if type_bridge_contract::value::CanonicalString::new(&spelling).is_err() {
381 return Err(ValidationError::new("", "string_limit_exceeded"));
382 }
383 let canonical = type_bridge_schema::parse_provider_datetime_tz(&spelling)
384 .map_err(|_| ValidationError::new("", "noncanonical_datetime_tz"))?;
385 if canonical.to_string() != spelling {
386 return Err(ValidationError::new("", "noncanonical_datetime_tz"));
387 }
388 Ok(Self {
389 spelling,
390 canonical,
391 })
392 }
393
394 #[must_use]
395 pub(crate) fn from_canonical(
396 canonical: type_bridge_contract::temporal::CanonicalDateTimeTz,
397 ) -> Self {
398 Self {
399 spelling: canonical.to_string(),
400 canonical,
401 }
402 }
403
404 #[must_use]
406 pub fn as_str(&self) -> &str {
407 &self.spelling
408 }
409
410 #[must_use]
411 pub(crate) const fn canonical(&self) -> &type_bridge_contract::temporal::CanonicalDateTimeTz {
412 &self.canonical
413 }
414}
415
416canonical_scalar!(
417 Duration,
418 "A duration in the shared canonical lexical form.",
419 "noncanonical_duration",
420 |s| {
421 if let Ok(dur) = s.parse::<type_bridge_contract::temporal::CanonicalDuration>() {
422 dur.to_string() == s
423 } else {
424 false
425 }
426 }
427);
428
429#[doc(hidden)]
431#[derive(Clone, Copy, Debug, Eq, PartialEq)]
432pub struct HydrationCapability {
433 _private: (),
434}
435
436impl HydrationCapability {
437 #[doc(hidden)]
439 pub const fn new() -> Self {
440 Self { _private: () }
441 }
442}
443
444#[cfg(feature = "test-harness")]
446#[doc(hidden)]
447pub fn materialize_model_for_test<M: MaterializeModel>(
448 row: &HydratedRow,
449) -> Result<M, ValidationError> {
450 let cap = HydrationCapability::new();
451 M::materialize(row, &cap)
452}
453
454#[doc(hidden)]
456pub trait IntoEncodedScalar {
457 #[allow(clippy::wrong_self_convention)]
458 fn into_encoded_scalar(&self) -> EncodedScalar;
459}
460
461impl IntoEncodedScalar for String {
462 fn into_encoded_scalar(&self) -> EncodedScalar {
463 EncodedScalar::String(self.clone())
464 }
465}
466
467impl IntoEncodedScalar for i64 {
468 fn into_encoded_scalar(&self) -> EncodedScalar {
469 EncodedScalar::Long(*self)
470 }
471}
472
473impl IntoEncodedScalar for CanonicalDouble {
474 fn into_encoded_scalar(&self) -> EncodedScalar {
475 EncodedScalar::Double(*self)
476 }
477}
478
479impl IntoEncodedScalar for bool {
480 fn into_encoded_scalar(&self) -> EncodedScalar {
481 EncodedScalar::Boolean(*self)
482 }
483}
484
485impl IntoEncodedScalar for Decimal {
486 fn into_encoded_scalar(&self) -> EncodedScalar {
487 EncodedScalar::Decimal(self.clone())
488 }
489}
490
491impl IntoEncodedScalar for Date {
492 fn into_encoded_scalar(&self) -> EncodedScalar {
493 EncodedScalar::Date(self.clone())
494 }
495}
496
497impl IntoEncodedScalar for DateTime {
498 fn into_encoded_scalar(&self) -> EncodedScalar {
499 EncodedScalar::DateTime(self.clone())
500 }
501}
502
503impl IntoEncodedScalar for DateTimeTz {
504 fn into_encoded_scalar(&self) -> EncodedScalar {
505 EncodedScalar::DateTimeTz(self.clone())
506 }
507}
508
509impl IntoEncodedScalar for Duration {
510 fn into_encoded_scalar(&self) -> EncodedScalar {
511 EncodedScalar::Duration(self.clone())
512 }
513}
514
515impl IntoEncodedScalar for EncodedScalar {
516 fn into_encoded_scalar(&self) -> EncodedScalar {
517 self.clone()
518 }
519}
520
521impl<T: IntoEncodedScalar> IntoEncodedScalar for &T {
522 fn into_encoded_scalar(&self) -> EncodedScalar {
523 (*self).into_encoded_scalar()
524 }
525}
526
527#[doc(hidden)]
533pub trait QueryValued: IntoEncodedScalar {
534 type Domain;
536}
537
538#[doc(hidden)]
541pub trait GroupedQueryValue: QueryValued + Sized {
542 fn from_group_scalar(value: EncodedScalar) -> Result<Self, ValidationError>;
543}
544
545impl QueryValued for String {
546 type Domain = String;
547}
548impl QueryValued for i64 {
549 type Domain = i64;
550}
551impl QueryValued for CanonicalDouble {
552 type Domain = CanonicalDouble;
553}
554impl QueryValued for bool {
555 type Domain = bool;
556}
557impl QueryValued for Decimal {
558 type Domain = Decimal;
559}
560impl QueryValued for Date {
561 type Domain = Date;
562}
563impl QueryValued for DateTime {
564 type Domain = DateTime;
565}
566impl QueryValued for DateTimeTz {
567 type Domain = DateTimeTz;
568}
569impl QueryValued for Duration {
570 type Domain = Duration;
571}
572impl<T: QueryValued> QueryValued for &T {
573 type Domain = T::Domain;
574}
575
576#[derive(Clone, Copy, Debug, Eq, PartialEq)]
578pub enum ThingKind {
579 Entity,
581 Relation,
583}
584
585pub trait Model: sealed::Sealed {
587 type Schema: Schema;
589 const TYPE_ID_JSON: &'static str;
591}
592
593pub trait ThingModel: Model {
595 fn thing_kind() -> ThingKind;
597}
598
599pub trait EntityModel: ThingModel {}
601
602pub trait RelationModel: ThingModel {}
604
605pub trait CompleteModel: ThingModel + MaterializeModel {
607 type Create: IntoEncodedCreate + Clone;
609 fn iid(&self) -> &str;
611}
612
613#[doc(hidden)]
614pub trait SubtypeRootModel: ThingModel {
615 type Subtypes;
616 fn __tb_dispatch_subtype(
617 row: &HydratedRow,
618 cap: &HydrationCapability,
619 ) -> Result<Self::Subtypes, ValidationError>;
620}
621
622pub trait AbstractModel: ThingModel {}
624
625pub trait TextValued {}
627impl TextValued for String {}
628
629pub trait OrderedValued {}
632
633pub trait NumericValued {
636 type Reduced;
638}
639impl NumericValued for i64 {
640 type Reduced = i64;
641}
642impl NumericValued for CanonicalDouble {
643 type Reduced = f64;
644}
645impl OrderedValued for i64 {}
646impl OrderedValued for CanonicalDouble {}
647impl OrderedValued for Date {}
648impl OrderedValued for DateTime {}
649impl OrderedValued for DateTimeTz {}
650impl OrderedValued for Decimal {}
651impl OrderedValued for Duration {}
652
653pub trait ReferenceModel: ThingModel {
655 fn iid(&self) -> Option<&str>;
657}
658
659#[doc(hidden)]
661#[derive(Clone, Debug, PartialEq)]
662pub enum EncodedScalar {
663 String(String),
664 Long(i64),
665 Double(CanonicalDouble),
666 Decimal(Decimal),
667 Boolean(bool),
668 Date(Date),
669 DateTime(DateTime),
670 DateTimeTz(DateTimeTz),
671 Duration(Duration),
672}
673
674impl EncodedScalar {
675 pub fn as_string(&self) -> Option<&str> {
676 match self {
677 Self::String(s) => Some(s.as_str()),
678 _ => None,
679 }
680 }
681
682 pub fn as_long(&self) -> Option<i64> {
683 match self {
684 Self::Long(n) => Some(*n),
685 _ => None,
686 }
687 }
688
689 pub fn as_double(&self) -> Option<CanonicalDouble> {
690 match self {
691 Self::Double(d) => Some(*d),
692 _ => None,
693 }
694 }
695
696 pub fn as_boolean(&self) -> Option<bool> {
697 match self {
698 Self::Boolean(b) => Some(*b),
699 _ => None,
700 }
701 }
702
703 pub fn as_decimal(&self) -> Option<&Decimal> {
704 match self {
705 Self::Decimal(d) => Some(d),
706 _ => None,
707 }
708 }
709
710 pub fn as_date(&self) -> Option<&Date> {
711 match self {
712 Self::Date(d) => Some(d),
713 _ => None,
714 }
715 }
716
717 pub fn as_datetime(&self) -> Option<&DateTime> {
718 match self {
719 Self::DateTime(d) => Some(d),
720 _ => None,
721 }
722 }
723
724 pub fn as_datetime_tz(&self) -> Option<&DateTimeTz> {
725 match self {
726 Self::DateTimeTz(d) => Some(d),
727 _ => None,
728 }
729 }
730
731 pub fn as_duration(&self) -> Option<&Duration> {
732 match self {
733 Self::Duration(d) => Some(d),
734 _ => None,
735 }
736 }
737
738 pub(crate) fn to_canonical_value(
739 &self,
740 path: &ValidationPath,
741 ) -> Result<type_bridge_contract::value::CanonicalValue, ValidationError> {
742 use type_bridge_contract::temporal::{CanonicalDate, CanonicalDateTime, CanonicalDuration};
743 use type_bridge_contract::value::{CanonicalDouble, CanonicalString, CanonicalValue};
744 match self {
745 Self::String(s) => CanonicalString::new(s)
746 .map(CanonicalValue::String)
747 .map_err(|_| ValidationError::new(path.path(), "string_limit_exceeded")),
748 Self::Long(n) => Ok(CanonicalValue::Long(*n)),
749 Self::Double(d) => CanonicalDouble::new(d.get())
750 .map(CanonicalValue::Double)
751 .map_err(|_| ValidationError::new(path.path(), "noncanonical_double")),
752 Self::Boolean(b) => Ok(CanonicalValue::Boolean(*b)),
753 Self::Decimal(d) => {
754 if type_bridge_contract::decimal::parse_decimal(d.as_str()).is_some() {
755 type_bridge_contract::value::DecimalValue::new(d.as_str())
756 .map(CanonicalValue::Decimal)
757 .map_err(|_| ValidationError::new(path.path(), "noncanonical_decimal"))
758 } else {
759 Err(ValidationError::new(path.path(), "noncanonical_decimal"))
760 }
761 }
762 Self::Date(d) => d
763 .as_str()
764 .parse::<CanonicalDate>()
765 .map(CanonicalValue::Date)
766 .map_err(|_| ValidationError::new(path.path(), "noncanonical_date")),
767 Self::DateTime(dt) => dt
768 .as_str()
769 .parse::<CanonicalDateTime>()
770 .map(CanonicalValue::DateTime)
771 .map_err(|_| ValidationError::new(path.path(), "noncanonical_datetime")),
772 Self::DateTimeTz(dtz) => Ok(CanonicalValue::DateTimeTz(dtz.canonical().clone())),
773 Self::Duration(dur) => dur
774 .as_str()
775 .parse::<CanonicalDuration>()
776 .map(CanonicalValue::Duration)
777 .map_err(|_| ValidationError::new(path.path(), "noncanonical_duration")),
778 }
779 }
780}
781
782pub fn validate_canonical_string(
788 value: &str,
789 path: &ValidationPath,
790) -> Result<(), ValidationError> {
791 if type_bridge_contract::value::CanonicalString::new(value).is_err() {
792 return Err(ValidationError::new(path.path(), "string_limit_exceeded"));
793 }
794 Ok(())
795}
796
797pub fn prefix_validation_path(
799 err: ValidationError,
800 parent_path: &ValidationPath,
801) -> ValidationError {
802 let sub_path = err.field();
803 let full_path = if sub_path.is_empty() || sub_path == "value" {
804 parent_path.path()
805 } else {
806 format!("{}.{}", parent_path.path(), sub_path)
807 };
808 ValidationError::new(full_path, err.code())
809}
810
811#[doc(hidden)]
813#[derive(Clone, Debug, PartialEq)]
814pub struct ConstraintDescriptor {
815 range_min: Option<EncodedScalar>,
816 range_max: Option<EncodedScalar>,
817 regex: Option<&'static str>,
818 values: Option<Vec<EncodedScalar>>,
819}
820
821impl ConstraintDescriptor {
822 #[must_use]
823 pub fn new(
824 range_min: Option<EncodedScalar>,
825 range_max: Option<EncodedScalar>,
826 regex: Option<&'static str>,
827 values: Option<Vec<EncodedScalar>>,
828 ) -> Self {
829 Self {
830 range_min,
831 range_max,
832 regex,
833 values,
834 }
835 }
836
837 pub fn validate(
838 &self,
839 value: &EncodedScalar,
840 path: &ValidationPath,
841 ) -> Result<(), ValidationError> {
842 if let Some(pattern) = self.regex {
843 let Some(s) = value.as_string() else {
844 return Err(ValidationError::new(path.path(), "wrong_scalar_domain"));
845 };
846 let re = regex::Regex::new(pattern)
847 .map_err(|_| ValidationError::new(path.path(), "invalid_regex_pattern"))?;
848 if !re.is_match(s) {
849 return Err(ValidationError::new(path.path(), "regex_violation"));
850 }
851 }
852
853 let canonical_val = value.to_canonical_value(path)?;
854
855 if let Some(allowed) = &self.values {
856 let mut found = false;
857 for item in allowed {
858 let allowed_canon = item.to_canonical_value(path)?;
859 if canonical_val.value_type() != allowed_canon.value_type() {
860 return Err(ValidationError::new(path.path(), "wrong_scalar_domain"));
861 }
862 let equal = match canonical_val.semantic_cmp_same_domain(&allowed_canon) {
863 Some(std::cmp::Ordering::Equal) => true,
864 Some(_) => false,
865 None => canonical_val == allowed_canon,
866 };
867 if equal {
868 found = true;
869 break;
870 }
871 }
872 if !found {
873 return Err(ValidationError::new(path.path(), "values_violation"));
874 }
875 }
876 if let Some(min) = &self.range_min {
877 let min_canon = min.to_canonical_value(path)?;
878 let cmp = canonical_val
879 .semantic_cmp_same_domain(&min_canon)
880 .ok_or_else(|| ValidationError::new(path.path(), "wrong_scalar_domain"))?;
881 if cmp == std::cmp::Ordering::Less {
882 return Err(ValidationError::new(path.path(), "range_violation"));
883 }
884 }
885 if let Some(max) = &self.range_max {
886 let max_canon = max.to_canonical_value(path)?;
887 let cmp = canonical_val
888 .semantic_cmp_same_domain(&max_canon)
889 .ok_or_else(|| ValidationError::new(path.path(), "wrong_scalar_domain"))?;
890 if cmp == std::cmp::Ordering::Greater {
891 return Err(ValidationError::new(path.path(), "range_violation"));
892 }
893 }
894 Ok(())
895 }
896}
897
898#[doc(hidden)]
904#[derive(Clone, Default)]
905pub struct ReferenceOrigin {
906 projected: Option<type_bridge_orm::ProjectedReferenceOrigin>,
907 detached_snapshot: bool,
908}
909
910impl ReferenceOrigin {
911 #[must_use]
912 pub(crate) const fn from_projected(
913 origin: Option<type_bridge_orm::ProjectedReferenceOrigin>,
914 ) -> Self {
915 Self {
916 projected: origin,
917 detached_snapshot: false,
918 }
919 }
920
921 #[must_use]
922 pub(crate) fn projected(&self) -> Option<type_bridge_orm::ProjectedReferenceOrigin> {
923 self.projected.clone()
924 }
925
926 #[must_use]
927 pub(crate) const fn detached_snapshot() -> Self {
928 Self {
929 projected: None,
930 detached_snapshot: true,
931 }
932 }
933
934 #[must_use]
935 pub(crate) const fn is_detached_snapshot(&self) -> bool {
936 self.detached_snapshot
937 }
938}
939
940impl fmt::Debug for ReferenceOrigin {
941 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
942 formatter.write_str("ReferenceOrigin([REDACTED])")
943 }
944}
945
946impl PartialEq for ReferenceOrigin {
947 fn eq(&self, _other: &Self) -> bool {
948 true
949 }
950}
951
952impl Eq for ReferenceOrigin {}
953
954#[doc(hidden)]
956#[derive(Clone, Debug, PartialEq)]
957pub struct EncodedReference {
958 type_id_json: &'static str,
959 iid: Option<String>,
960 keys: Vec<(&'static str, EncodedScalar)>,
961 origin: ReferenceOrigin,
962}
963
964impl EncodedReference {
965 pub fn try_new(
966 type_id_json: &'static str,
967 iid: Option<String>,
968 keys: Vec<(&'static str, EncodedScalar)>,
969 path: &ValidationPath,
970 ) -> Result<Self, ValidationError> {
971 Self::try_new_with_origin(type_id_json, iid, keys, ReferenceOrigin::default(), path)
972 }
973
974 #[doc(hidden)]
975 pub fn try_new_with_origin(
976 type_id_json: &'static str,
977 iid: Option<String>,
978 keys: Vec<(&'static str, EncodedScalar)>,
979 origin: ReferenceOrigin,
980 path: &ValidationPath,
981 ) -> Result<Self, ValidationError> {
982 if iid.as_deref().is_some_and(|value| value.trim().is_empty()) {
983 return Err(ValidationError::new(path.join("iid").path(), "empty_iid"));
984 }
985 let mut seen = std::collections::BTreeSet::new();
986 for (index, (token, _)) in keys.iter().enumerate() {
987 if !seen.insert(*token) {
988 return Err(ValidationError::new(
989 path.join("keys").join_index(index).path(),
990 "duplicate_reference_key",
991 ));
992 }
993 }
994 if iid.is_none() && keys.is_empty() {
995 return Err(ValidationError::new(
996 path.path(),
997 "missing_reference_identity",
998 ));
999 }
1000 if iid.is_none() && keys.len() > 1 {
1001 return Err(ValidationError::new(
1002 path.path(),
1003 "multiple_reference_keys_without_iid",
1004 ));
1005 }
1006 Ok(Self {
1007 type_id_json,
1008 iid,
1009 keys,
1010 origin,
1011 })
1012 }
1013
1014 #[must_use]
1015 pub const fn type_id_json(&self) -> &'static str {
1016 self.type_id_json
1017 }
1018
1019 #[must_use]
1020 pub fn iid(&self) -> Option<&str> {
1021 self.iid.as_deref()
1022 }
1023
1024 #[must_use]
1025 pub fn keys(&self) -> &[(&'static str, EncodedScalar)] {
1026 &self.keys
1027 }
1028
1029 #[doc(hidden)]
1030 #[must_use]
1031 pub const fn origin(&self) -> &ReferenceOrigin {
1032 &self.origin
1033 }
1034}
1035
1036#[doc(hidden)]
1038#[derive(Clone, Debug, PartialEq)]
1039pub struct EncodedCreate {
1040 type_id_json: &'static str,
1041 fields: Vec<(&'static str, Vec<EncodedScalar>)>,
1042 roles: Vec<(&'static str, Vec<EncodedReference>)>,
1043}
1044
1045impl EncodedCreate {
1046 #[must_use]
1047 pub const fn new(
1048 type_id_json: &'static str,
1049 fields: Vec<(&'static str, Vec<EncodedScalar>)>,
1050 roles: Vec<(&'static str, Vec<EncodedReference>)>,
1051 ) -> Self {
1052 Self {
1053 type_id_json,
1054 fields,
1055 roles,
1056 }
1057 }
1058
1059 #[must_use]
1060 pub const fn type_id_json(&self) -> &'static str {
1061 self.type_id_json
1062 }
1063
1064 #[must_use]
1065 pub fn fields(&self) -> &[(&'static str, Vec<EncodedScalar>)] {
1066 &self.fields
1067 }
1068
1069 #[must_use]
1070 pub fn roles(&self) -> &[(&'static str, Vec<EncodedReference>)] {
1071 &self.roles
1072 }
1073}
1074
1075#[doc(hidden)]
1077#[derive(Clone, Debug, PartialEq)]
1078pub struct HydratedPlayer {
1079 type_id_json: String,
1080 iid: Option<String>,
1081 keys: Vec<(String, EncodedScalar)>,
1082 fields: Option<Vec<(String, Vec<EncodedScalar>)>>,
1083 exact_reference: bool,
1084 origin: ReferenceOrigin,
1085}
1086
1087impl HydratedPlayer {
1088 #[must_use]
1089 pub fn new(
1090 type_id_json: &'static str,
1091 iid: Option<String>,
1092 keys: Vec<(&'static str, EncodedScalar)>,
1093 ) -> Self {
1094 Self {
1095 type_id_json: type_id_json.to_owned(),
1096 iid,
1097 keys: keys
1098 .into_iter()
1099 .map(|(identity, value)| (identity.to_owned(), value))
1100 .collect(),
1101 fields: None,
1102 exact_reference: false,
1103 origin: ReferenceOrigin::default(),
1104 }
1105 }
1106
1107 #[must_use]
1108 #[allow(dead_code)]
1109 pub(crate) fn from_owned(
1110 type_id_json: String,
1111 iid: Option<String>,
1112 keys: Vec<(String, EncodedScalar)>,
1113 ) -> Self {
1114 Self {
1115 type_id_json,
1116 iid,
1117 keys,
1118 fields: None,
1119 exact_reference: false,
1120 origin: ReferenceOrigin::default(),
1121 }
1122 }
1123
1124 #[must_use]
1125 pub(crate) fn from_reference_owned(
1126 type_id_json: String,
1127 iid: Option<String>,
1128 keys: Vec<(String, EncodedScalar)>,
1129 ) -> Self {
1130 Self {
1131 type_id_json,
1132 iid,
1133 keys,
1134 fields: None,
1135 exact_reference: true,
1136 origin: ReferenceOrigin::default(),
1137 }
1138 }
1139
1140 #[must_use]
1142 pub fn from_complete_row(row: HydratedRow) -> Self {
1143 Self::from_complete_row_with_keys(row, Vec::new())
1144 }
1145
1146 #[must_use]
1147 pub(crate) fn from_complete_row_with_keys(
1148 row: HydratedRow,
1149 keys: Vec<(String, EncodedScalar)>,
1150 ) -> Self {
1151 debug_assert!(row.roles.is_empty());
1152 Self {
1153 type_id_json: row.type_id_json,
1154 iid: Some(row.iid),
1155 keys,
1156 fields: Some(row.fields),
1157 exact_reference: false,
1158 origin: row.origin,
1159 }
1160 }
1161
1162 #[must_use]
1163 pub(crate) fn from_owned_with_origin(
1164 type_id_json: String,
1165 iid: Option<String>,
1166 keys: Vec<(String, EncodedScalar)>,
1167 origin: ReferenceOrigin,
1168 ) -> Self {
1169 Self {
1170 type_id_json,
1171 iid,
1172 keys,
1173 fields: None,
1174 exact_reference: true,
1175 origin,
1176 }
1177 }
1178
1179 #[must_use]
1180 pub fn type_id_json(&self) -> &str {
1181 &self.type_id_json
1182 }
1183
1184 #[must_use]
1185 pub fn iid(&self) -> Option<&str> {
1186 self.iid.as_deref()
1187 }
1188
1189 #[must_use]
1190 pub fn keys(&self) -> &[(String, EncodedScalar)] {
1191 &self.keys
1192 }
1193
1194 #[must_use]
1196 pub fn fields(&self) -> Option<&[(String, Vec<EncodedScalar>)]> {
1197 self.fields.as_deref()
1198 }
1199
1200 #[must_use]
1201 pub(crate) const fn is_exact_reference(&self) -> bool {
1202 self.exact_reference
1203 }
1204
1205 #[must_use]
1207 pub fn complete_row(&self) -> Option<HydratedRow> {
1208 Some(HydratedRow {
1209 type_id_json: self.type_id_json.clone(),
1210 iid: self.iid.clone()?,
1211 fields: self.fields.clone()?,
1212 roles: Vec::new(),
1213 origin: self.origin.clone(),
1214 })
1215 }
1216
1217 #[doc(hidden)]
1218 #[must_use]
1219 pub const fn origin(&self) -> &ReferenceOrigin {
1220 &self.origin
1221 }
1222}
1223
1224#[doc(hidden)]
1226#[derive(Clone, Debug, PartialEq)]
1227pub struct HydratedRow {
1228 type_id_json: String,
1229 iid: String,
1230 fields: Vec<(String, Vec<EncodedScalar>)>,
1231 roles: Vec<(String, Vec<HydratedPlayer>)>,
1232 origin: ReferenceOrigin,
1233}
1234
1235impl HydratedRow {
1236 #[must_use]
1237 pub fn new(
1238 type_id_json: &'static str,
1239 iid: String,
1240 fields: Vec<(&'static str, Vec<EncodedScalar>)>,
1241 roles: Vec<(&'static str, Vec<HydratedPlayer>)>,
1242 ) -> Self {
1243 Self {
1244 type_id_json: type_id_json.to_owned(),
1245 iid,
1246 fields: fields
1247 .into_iter()
1248 .map(|(identity, values)| (identity.to_owned(), values))
1249 .collect(),
1250 roles: roles
1251 .into_iter()
1252 .map(|(identity, players)| (identity.to_owned(), players))
1253 .collect(),
1254 origin: ReferenceOrigin::default(),
1255 }
1256 }
1257
1258 #[must_use]
1259 #[allow(dead_code)]
1260 pub(crate) fn from_owned(
1261 type_id_json: String,
1262 iid: String,
1263 fields: Vec<(String, Vec<EncodedScalar>)>,
1264 roles: Vec<(String, Vec<HydratedPlayer>)>,
1265 ) -> Self {
1266 Self {
1267 type_id_json,
1268 iid,
1269 fields,
1270 roles,
1271 origin: ReferenceOrigin::default(),
1272 }
1273 }
1274
1275 #[must_use]
1276 pub(crate) fn from_owned_with_origin(
1277 type_id_json: String,
1278 iid: String,
1279 fields: Vec<(String, Vec<EncodedScalar>)>,
1280 roles: Vec<(String, Vec<HydratedPlayer>)>,
1281 origin: ReferenceOrigin,
1282 ) -> Self {
1283 Self {
1284 type_id_json,
1285 iid,
1286 fields,
1287 roles,
1288 origin,
1289 }
1290 }
1291
1292 pub(crate) fn mark_detached_snapshot(&mut self) {
1293 self.origin = ReferenceOrigin::detached_snapshot();
1294 }
1295
1296 #[must_use]
1297 pub fn type_id_json(&self) -> &str {
1298 &self.type_id_json
1299 }
1300
1301 #[must_use]
1302 pub fn iid(&self) -> &str {
1303 &self.iid
1304 }
1305
1306 #[must_use]
1307 pub fn fields(&self) -> &[(String, Vec<EncodedScalar>)] {
1308 &self.fields
1309 }
1310
1311 #[must_use]
1312 pub fn roles(&self) -> &[(String, Vec<HydratedPlayer>)] {
1313 &self.roles
1314 }
1315
1316 #[doc(hidden)]
1317 #[must_use]
1318 pub const fn origin(&self) -> &ReferenceOrigin {
1319 &self.origin
1320 }
1321
1322 pub fn validate_shape(
1323 &self,
1324 expected_type_id: &'static str,
1325 expected_fields: &[&'static str],
1326 expected_roles: &[&'static str],
1327 path: &ValidationPath,
1328 ) -> Result<(), ValidationError> {
1329 if self.type_id_json != expected_type_id {
1330 return Err(ValidationError::new(
1331 path.path(),
1332 "wrong_concrete_model_type",
1333 ));
1334 }
1335 let mut seen_fields = std::collections::BTreeSet::new();
1336 for (k, _) in &self.fields {
1337 if !seen_fields.insert(k.as_str()) {
1338 return Err(ValidationError::new(
1339 path.path(),
1340 "duplicate_scalar_evidence",
1341 ));
1342 }
1343 if !expected_fields.contains(&k.as_str()) {
1344 return Err(ValidationError::new(
1345 path.path(),
1346 "unexpected_field_evidence",
1347 ));
1348 }
1349 }
1350 let mut seen_roles = std::collections::BTreeSet::new();
1351 for (r, _) in &self.roles {
1352 if !seen_roles.insert(r.as_str()) {
1353 return Err(ValidationError::new(path.path(), "duplicate_role_evidence"));
1354 }
1355 if !expected_roles.contains(&r.as_str()) {
1356 return Err(ValidationError::new(
1357 path.path(),
1358 "unexpected_role_evidence",
1359 ));
1360 }
1361 }
1362 Ok(())
1363 }
1364}
1365
1366#[doc(hidden)]
1368pub trait IntoEncodedCreate: sealed::Sealed {
1369 fn into_encoded_create(self) -> Result<EncodedCreate, ValidationError>;
1370}
1371
1372#[doc(hidden)]
1374pub trait MaterializeCreate: IntoEncodedCreate + Sized {
1375 type Schema: crate::schema::Schema;
1376 fn materialize_create(
1377 value: &DecodedCreate,
1378 path: &ValidationPath,
1379 ) -> Result<Self, ValidationError>;
1380}
1381
1382#[doc(hidden)]
1384pub trait MaterializeReference: IntoEncodedReference + Sized {
1385 type Schema: crate::schema::Schema;
1386 fn materialize_reference(
1387 value: &HydratedPlayer,
1388 path: &ValidationPath,
1389 ) -> Result<Self, ValidationError>;
1390}
1391
1392#[doc(hidden)]
1394#[derive(Clone, Debug, PartialEq)]
1395pub struct DecodedCreate {
1396 type_id_json: String,
1397 fields: Vec<(String, Vec<EncodedScalar>)>,
1398 roles: Vec<(String, Vec<HydratedPlayer>)>,
1399}
1400
1401impl DecodedCreate {
1402 #[must_use]
1403 pub fn new(
1404 type_id_json: String,
1405 fields: Vec<(String, Vec<EncodedScalar>)>,
1406 roles: Vec<(String, Vec<HydratedPlayer>)>,
1407 ) -> Self {
1408 Self {
1409 type_id_json,
1410 fields,
1411 roles,
1412 }
1413 }
1414
1415 #[must_use]
1416 pub fn type_id_json(&self) -> &str {
1417 &self.type_id_json
1418 }
1419
1420 #[must_use]
1421 pub fn fields(&self) -> &[(String, Vec<EncodedScalar>)] {
1422 &self.fields
1423 }
1424
1425 #[must_use]
1426 pub fn roles(&self) -> &[(String, Vec<HydratedPlayer>)] {
1427 &self.roles
1428 }
1429}
1430
1431#[doc(hidden)]
1434#[derive(Clone, Debug, PartialEq)]
1435pub enum UnconstructibleCreate {}
1436
1437impl sealed::Sealed for UnconstructibleCreate {}
1438
1439impl IntoEncodedCreate for UnconstructibleCreate {
1440 fn into_encoded_create(self) -> Result<EncodedCreate, ValidationError> {
1441 match self {}
1442 }
1443}
1444
1445#[doc(hidden)]
1447pub trait IntoEncodedReference: sealed::Sealed {
1448 fn into_encoded_reference(self) -> Result<EncodedReference, ValidationError>;
1449}
1450
1451#[doc(hidden)]
1453pub trait MaterializeModel: Model + Sized {
1454 fn materialize(row: &HydratedRow, cap: &HydrationCapability) -> Result<Self, ValidationError>;
1455}
1456
1457#[doc(hidden)]
1459pub trait IntoHydratedSnapshot: Model + Sized {
1460 fn into_hydrated_snapshot(self) -> Result<HydratedRow, ValidationError>;
1461}
1462
1463#[doc(hidden)]
1465pub fn hydrated_player_from_encoded_reference(value: EncodedReference) -> HydratedPlayer {
1466 HydratedPlayer::from_reference_owned(
1467 value.type_id_json().to_owned(),
1468 value.iid().map(str::to_owned),
1469 value
1470 .keys()
1471 .iter()
1472 .map(|(identity, scalar)| ((*identity).to_owned(), scalar.clone()))
1473 .collect(),
1474 )
1475}
1476
1477pub trait ModelFamily: sealed::Sealed {
1479 type Root: ThingModel;
1481 type Schema: Schema;
1483 fn iid(&self) -> &str;
1485}
1486
1487pub trait StructValue: sealed::Sealed {
1489 type Schema: Schema;
1491 const STRUCT_ID_JSON: &'static str;
1493}
1494
1495#[doc(hidden)]
1497pub trait IntoEncodedStruct: StructValue + Sized {
1498 fn into_encoded_struct(self) -> EncodedStruct;
1499}
1500
1501#[doc(hidden)]
1503pub trait MaterializeStruct: IntoEncodedStruct + Sized {
1504 fn materialize_struct(
1505 value: &DecodedStruct,
1506 path: &ValidationPath,
1507 ) -> Result<Self, ValidationError>;
1508}
1509
1510#[doc(hidden)]
1512#[derive(Clone, Debug, PartialEq)]
1513pub struct EncodedStruct {
1514 type_id_json: &'static str,
1515 members: Vec<Option<EncodedScalar>>,
1516}
1517
1518impl EncodedStruct {
1519 #[must_use]
1520 pub fn new(type_id_json: &'static str, members: Vec<Option<EncodedScalar>>) -> Self {
1521 Self {
1522 type_id_json,
1523 members,
1524 }
1525 }
1526
1527 #[must_use]
1528 pub const fn type_id_json(&self) -> &'static str {
1529 self.type_id_json
1530 }
1531
1532 #[must_use]
1533 pub fn members(&self) -> &[Option<EncodedScalar>] {
1534 &self.members
1535 }
1536}
1537
1538#[doc(hidden)]
1540#[derive(Clone, Debug, PartialEq)]
1541pub struct DecodedStruct {
1542 type_id_json: String,
1543 members: Vec<Option<EncodedScalar>>,
1544}
1545
1546impl DecodedStruct {
1547 #[must_use]
1548 pub fn new(type_id_json: String, members: Vec<Option<EncodedScalar>>) -> Self {
1549 Self {
1550 type_id_json,
1551 members,
1552 }
1553 }
1554
1555 #[must_use]
1556 pub fn type_id_json(&self) -> &str {
1557 &self.type_id_json
1558 }
1559
1560 #[must_use]
1561 pub fn members(&self) -> &[Option<EncodedScalar>] {
1562 &self.members
1563 }
1564}
1565
1566pub trait NominalUpcast<Target: Model>: Model {}
1568
1569pub trait RoleUpcast<ActiveRole, AncestorRole>: Model {}
1571
1572pub trait RoleTokenCompatible<Owner: RelationModel, Players>: RelationModel {}
1578
1579pub trait RolePlayer<Player: ThingModel> {}
1582
1583pub trait RolePlayerBinding<Player: ThingModel, Mode> {}
1585
1586impl<Players, Player> RolePlayerBinding<Player, crate::query::Exact> for Players
1587where
1588 Player: ThingModel,
1589 Players: RolePlayer<Player>,
1590{
1591}
1592
1593#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1595pub struct TypeToken<Owner: Model> {
1596 type_id_json: &'static str,
1597 metadata_json: &'static str,
1598 marker: PhantomData<fn() -> Owner>,
1599}
1600
1601impl<Owner: Model> TypeToken<Owner> {
1602 #[must_use]
1604 pub const fn new(type_id_json: &'static str, metadata_json: &'static str) -> Self {
1605 Self {
1606 type_id_json,
1607 metadata_json,
1608 marker: PhantomData,
1609 }
1610 }
1611
1612 #[must_use]
1614 pub const fn type_id_json(self) -> &'static str {
1615 self.type_id_json
1616 }
1617
1618 #[must_use]
1620 pub const fn metadata_json(self) -> &'static str {
1621 self.metadata_json
1622 }
1623}
1624
1625#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1627pub struct FieldToken<Owner: Model, Value> {
1628 owns_id_json: &'static str,
1629 metadata_json: &'static str,
1630 marker: PhantomData<fn() -> (Owner, Value)>,
1631}
1632
1633impl<Owner: Model, Value> FieldToken<Owner, Value> {
1634 #[must_use]
1636 pub const fn new(owns_id_json: &'static str, metadata_json: &'static str) -> Self {
1637 Self {
1638 owns_id_json,
1639 metadata_json,
1640 marker: PhantomData,
1641 }
1642 }
1643
1644 #[must_use]
1646 pub const fn owns_id_json(self) -> &'static str {
1647 self.owns_id_json
1648 }
1649
1650 #[must_use]
1652 pub const fn metadata_json(self) -> &'static str {
1653 self.metadata_json
1654 }
1655
1656 pub(crate) const fn evidence_json(&self) -> (&'static str, &'static str) {
1658 (self.owns_id_json, self.metadata_json)
1659 }
1660}
1661
1662#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1664pub struct RoleToken<Owner: Model, Players> {
1665 role_id_json: &'static str,
1666 metadata_json: &'static str,
1667 marker: PhantomData<fn() -> (Owner, Players)>,
1668}
1669
1670impl<Owner: Model, Players> RoleToken<Owner, Players> {
1671 #[must_use]
1673 pub const fn new(role_id_json: &'static str, metadata_json: &'static str) -> Self {
1674 Self {
1675 role_id_json,
1676 metadata_json,
1677 marker: PhantomData,
1678 }
1679 }
1680
1681 #[must_use]
1683 pub const fn role_id_json(self) -> &'static str {
1684 self.role_id_json
1685 }
1686
1687 #[must_use]
1689 pub const fn metadata_json(self) -> &'static str {
1690 self.metadata_json
1691 }
1692}
1693
1694#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1696pub struct PlaysToken<Player: Model, Owner: Model, Players> {
1697 plays_id_json: &'static str,
1698 metadata_json: &'static str,
1699 #[allow(clippy::type_complexity)]
1700 marker: PhantomData<fn() -> (Player, Owner, Players)>,
1701}
1702
1703impl<Player: Model, Owner: Model, Players> PlaysToken<Player, Owner, Players> {
1704 #[must_use]
1706 pub const fn new(plays_id_json: &'static str, metadata_json: &'static str) -> Self {
1707 Self {
1708 plays_id_json,
1709 metadata_json,
1710 marker: PhantomData,
1711 }
1712 }
1713
1714 #[must_use]
1716 pub const fn plays_id_json(self) -> &'static str {
1717 self.plays_id_json
1718 }
1719
1720 #[must_use]
1722 pub const fn metadata_json(self) -> &'static str {
1723 self.metadata_json
1724 }
1725}
1726
1727#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1729pub struct FunctionToken<S: Schema, Arguments, Output> {
1730 function_id: &'static str,
1731 metadata_json: &'static str,
1732 marker: PhantomData<fn(Arguments) -> (S, Output)>,
1733}
1734
1735impl<S: Schema, Arguments, Output> FunctionToken<S, Arguments, Output> {
1736 #[must_use]
1738 pub const fn new(function_id: &'static str, metadata_json: &'static str) -> Self {
1739 Self {
1740 function_id,
1741 metadata_json,
1742 marker: PhantomData,
1743 }
1744 }
1745
1746 #[doc(hidden)]
1751 #[must_use]
1752 pub const fn __function_id(self) -> &'static str {
1753 self.function_id
1754 }
1755
1756 #[must_use]
1758 pub const fn metadata_json(self) -> &'static str {
1759 self.metadata_json
1760 }
1761}
1762
1763#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1765pub struct Stream<T>(PhantomData<fn() -> T>);
1766
1767#[cfg(test)]
1768mod tests {
1769 use super::*;
1770
1771 #[test]
1772 fn canonical_scalar_wrappers_do_not_expose_lexical_ordering() {
1773 let source = include_str!("__codegen.rs");
1774 let (_, macro_and_invocations) = source
1775 .split_once("macro_rules! canonical_scalar")
1776 .expect("canonical scalar macro remains present");
1777 let (macro_body, _) = macro_and_invocations
1778 .split_once("canonical_scalar!(\n Decimal")
1779 .expect("canonical scalar invocations remain present");
1780 assert!(macro_body.contains("#[derive(Clone, Debug, Eq, PartialEq, Hash)]"));
1781 assert!(!macro_body.contains("Ord"));
1782 assert!(!macro_body.contains("PartialOrd"));
1783 }
1784
1785 #[test]
1786 fn scalar_domain_wrappers_and_validation() {
1787 let path = ValidationPath::root().join("test");
1788
1789 let double_ok = CanonicalDouble::try_new(3.125).unwrap();
1790 assert_eq!(double_ok.get(), 3.125);
1791 assert_eq!(
1792 CanonicalDouble::try_new(f64::NAN).unwrap_err().code(),
1793 "noncanonical_double"
1794 );
1795 assert_eq!(
1796 CanonicalDouble::try_new(f64::INFINITY).unwrap_err().code(),
1797 "noncanonical_double"
1798 );
1799 let neg_zero = CanonicalDouble::try_new(-0.0).unwrap();
1800 let pos_zero = CanonicalDouble::try_new(0.0).unwrap();
1801 assert_ne!(neg_zero.to_bits(), pos_zero.to_bits());
1802
1803 let dec = Decimal::try_new("123.45").unwrap();
1804 assert_eq!(dec.as_str(), "123.45");
1805 assert_eq!(
1806 Decimal::try_new("123.4500").unwrap_err().code(),
1807 "noncanonical_decimal"
1808 );
1809
1810 let date = Date::try_new("2026-07-28").unwrap();
1811 assert_eq!(date.as_str(), "2026-07-28");
1812 assert_eq!(
1813 Date::try_new("2026-7-28").unwrap_err().code(),
1814 "noncanonical_date"
1815 );
1816
1817 let dt = DateTime::try_new("2026-07-28T03:55:00").unwrap();
1818 assert_eq!(dt.as_str(), "2026-07-28T03:55:00");
1819
1820 let dtz = DateTimeTz::try_new("2026-07-28T03:55:00Z").unwrap();
1821 assert_eq!(dtz.as_str(), "2026-07-28T03:55:00Z");
1822 let named = DateTimeTz::try_new("2026-07-28T03:55:00[Europe/Amsterdam]").unwrap();
1823 assert_eq!(named.as_str(), "2026-07-28T03:55:00[Europe/Amsterdam]");
1824
1825 let overlap_earlier = type_bridge_schema::parse_provider_datetime_tz_evidence(
1826 "2024-10-27T01:30:00+01:00[Europe/London]",
1827 )
1828 .unwrap();
1829 let overlap_later = type_bridge_schema::parse_provider_datetime_tz_evidence(
1830 "2024-10-27T01:30:00Z[Europe/London]",
1831 )
1832 .unwrap();
1833 let earlier = DateTimeTz::from_canonical(overlap_earlier.clone());
1834 let later = DateTimeTz::from_canonical(overlap_later.clone());
1835 assert_eq!(earlier.as_str(), later.as_str());
1836 assert_ne!(earlier, later);
1837 assert_eq!(
1838 EncodedScalar::DateTimeTz(earlier)
1839 .to_canonical_value(&path)
1840 .unwrap(),
1841 type_bridge_contract::value::CanonicalValue::DateTimeTz(overlap_earlier)
1842 );
1843 assert_eq!(
1844 EncodedScalar::DateTimeTz(later)
1845 .to_canonical_value(&path)
1846 .unwrap(),
1847 type_bridge_contract::value::CanonicalValue::DateTimeTz(overlap_later)
1848 );
1849
1850 let dur = Duration::try_new("P1D").unwrap();
1851 assert_eq!(dur.as_str(), "P1D");
1852
1853 let seq = Sequence::try_new(vec![1, 2], Cardinality::new(1, Some(3)), &path).unwrap();
1854 assert_eq!(seq.as_slice(), &[1, 2]);
1855
1856 let seq_err = Sequence::try_new(vec![1, 2, 3, 4], Cardinality::new(1, Some(3)), &path);
1857 assert_eq!(seq_err.unwrap_err().code(), "cardinality_violation");
1858
1859 let constraint = ConstraintDescriptor::new(
1860 Some(EncodedScalar::String("a".to_owned())),
1861 Some(EncodedScalar::String("z".to_owned())),
1862 Some("^a.*z$"),
1863 None,
1864 );
1865 let s_val = EncodedScalar::String("abcz".to_owned());
1866 assert_eq!(constraint.validate(&s_val, &path), Ok(()));
1867 }
1868}