1#![deny(rust_2018_idioms)]
2#![deny(clippy::all)]
3
4use educe::Educe;
5use indexmap::IndexSet;
6use itertools::Itertools;
7use miette::Diagnostic;
8use partiql_common::node::{AutoNodeIdGenerator, NodeId, NodeIdGenerator, NullIdGenerator};
9use std::fmt::{Debug, Display, Formatter};
10use std::hash::{Hash, Hasher};
11use std::sync::OnceLock;
12use thiserror::Error;
13
14#[derive(Debug, Clone, Eq, PartialEq, Hash, Error, Diagnostic)]
15#[error("ShapeResult Error")]
16#[non_exhaustive]
17pub enum ShapeResultError {
18 #[error("Unexpected type `{0:?}` for static type bool")]
19 UnexpectedType(String),
20}
21
22pub type ShapeResult<T> = Result<T, ShapeResultError>;
24
25pub trait Type {}
26
27impl Type for StaticType {}
28
29fn indexset_hash<H, T>(set: &IndexSet<T>, state: &mut H)
30where
31 H: Hasher,
32 T: Hash,
33{
34 for val in set {
35 val.hash(state)
36 }
37}
38
39#[macro_export]
40macro_rules! type_dynamic {
41 ($bld:expr) => {
42 $bld.new_dynamic()
43 };
44}
45
46#[macro_export]
47macro_rules! type_int {
48 ($bld:expr) => {
49 $bld.new_static($crate::Static::Int)
50 };
51}
52
53#[macro_export]
54macro_rules! type_int8 {
55 ($bld:expr) => {
56 $bld.new_static($crate::Static::Int8)
57 };
58}
59
60#[macro_export]
61macro_rules! type_int16 {
62 ($bld:expr) => {
63 $bld.new_static($crate::Static::Int16)
64 };
65}
66
67#[macro_export]
68macro_rules! type_int32 {
69 ($bld:expr) => {
70 $bld.new_static($crate::Static::Int32)
71 };
72}
73
74#[macro_export]
75macro_rules! type_int64 {
76 ($bld:expr) => {
77 $bld.new_static($crate::Static::Int64)
78 };
79}
80
81#[macro_export]
82macro_rules! type_decimal {
83 ($bld:expr) => {
84 $bld.new_static($crate::Static::Decimal)
85 };
86}
87
88#[macro_export]
91macro_rules! type_float32 {
92 ($bld:expr) => {
93 $bld.new_static($crate::Static::Float32)
94 };
95}
96
97#[macro_export]
98macro_rules! type_float64 {
99 ($bld:expr) => {
100 $bld.new_static($crate::Static::Float64)
101 };
102}
103
104#[macro_export]
105macro_rules! type_string {
106 ($bld:expr) => {
107 $bld.new_static($crate::Static::String)
108 };
109}
110
111#[macro_export]
112macro_rules! type_bool {
113 ($bld:expr) => {
114 $bld.new_static($crate::Static::Bool)
115 };
116}
117
118#[macro_export]
119macro_rules! type_numeric {
120 ($bld:expr) => {
121 [
122 $crate::type_int!($bld),
123 $crate::type_float32!($bld),
124 $crate::type_float64!($bld),
125 $crate::type_decimal!($bld),
126 ]
127 .into_any_of($bld)
128 };
129}
130
131#[macro_export]
132macro_rules! type_datetime {
133 ($bld:expr) => {
134 $bld.new_static($crate::Static::DateTime)
135 };
136}
137
138#[macro_export]
139macro_rules! type_struct {
140 ($bld:expr) => {
141 $bld.new_struct(StructType::new_any())
142 };
143 ($bld:expr, $elem:expr) => {{
144 let elem = $elem;
145 $bld.new_struct(StructType::new(elem))
146 }};
147}
148
149#[macro_export]
150macro_rules! type_graph {
151 ($bld:expr) => {
152 $bld.new_graph()
153 };
154}
155
156#[macro_export]
157macro_rules! struct_fields {
158 ($(($x:expr, $y:expr)),+ $(,)?) => (
159 $crate::StructConstraint::Fields([$(($x, $y).into()),+].into())
160 );
161}
162
163#[macro_export]
164macro_rules! type_bag {
165 ($bld:expr) => {
166 $bld.new_bag(BagType::new_any());
167 };
168 ($bld:expr, $elem:expr) => {{
169 let elem = $elem;
170 $bld.new_bag($crate::BagType::new(Box::new(elem)))
171 }};
172}
173
174#[macro_export]
175macro_rules! type_array {
176 ($bld:expr) => {
177 $bld.new_array(ArrayType::new_any());
178 };
179 ($bld:expr, $elem:expr) => {{
180 let elem = $elem;
181 $bld.new_array($crate::ArrayType::new(Box::new(elem)))
182 }};
183}
184
185#[derive(Debug, Clone, Eq, PartialEq, Hash)]
187pub enum PartiqlShape {
191 Dynamic,
192 AnyOf(AnyOf),
193 Static(StaticType),
194 Undefined,
195}
196
197#[allow(dead_code)]
198impl PartiqlShape {
199 #[inline]
200 pub fn static_type_id(&self) -> Option<NodeId> {
201 if let PartiqlShape::Static(StaticType { id, .. }) = self {
202 Some(*id)
203 } else {
204 None
205 }
206 }
207
208 #[inline]
209 pub fn is_string(&self) -> bool {
210 matches!(
211 &self,
212 PartiqlShape::Static(StaticType {
213 ty: Static::String,
214 nullable: true,
215 ..
216 })
217 )
218 }
219
220 #[inline]
221 pub fn is_struct(&self) -> bool {
222 matches!(
223 *self,
224 PartiqlShape::Static(StaticType {
225 ty: Static::Struct(_),
226 nullable: true,
227 ..
228 })
229 )
230 }
231
232 #[inline]
233 pub fn is_collection(&self) -> bool {
234 matches!(
235 *self,
236 PartiqlShape::Static(StaticType {
237 ty: Static::Bag(_),
238 nullable: true,
239 ..
240 })
241 ) || matches!(
242 *self,
243 PartiqlShape::Static(StaticType {
244 ty: Static::Array(_),
245 nullable: true,
246 ..
247 })
248 )
249 }
250
251 #[inline]
252 pub fn is_unordered_collection(&self) -> bool {
253 !self.is_ordered_collection()
254 }
255
256 #[inline]
257 pub fn is_ordered_collection(&self) -> bool {
258 matches!(
260 *self,
261 PartiqlShape::Static(StaticType {
262 ty: Static::Array(_),
263 nullable: true,
264 ..
265 })
266 )
267 }
268
269 #[inline]
270 pub fn is_graph(&self) -> bool {
271 matches!(
272 *self,
273 PartiqlShape::Static(StaticType {
274 ty: Static::Graph(),
275 nullable: true,
276 ..
277 })
278 )
279 }
280
281 #[inline]
282 pub fn is_bag(&self) -> bool {
283 matches!(
284 *self,
285 PartiqlShape::Static(StaticType {
286 ty: Static::Bag(_),
287 nullable: true,
288 ..
289 })
290 )
291 }
292
293 #[inline]
294 pub fn is_array(&self) -> bool {
295 matches!(
296 *self,
297 PartiqlShape::Static(StaticType {
298 ty: Static::Array(_),
299 nullable: true,
300 ..
301 })
302 )
303 }
304
305 #[inline]
306 pub fn is_dynamic(&self) -> bool {
307 matches!(*self, PartiqlShape::Dynamic)
308 }
309
310 #[inline]
311 pub fn is_undefined(&self) -> bool {
312 matches!(*self, PartiqlShape::Undefined)
313 }
314
315 pub fn expect_bool(&self) -> ShapeResult<StaticType> {
316 if let PartiqlShape::Static(StaticType {
317 id,
318 ty: Static::Bool,
319 nullable: n,
320 }) = self
321 {
322 Ok(StaticType {
323 id: *id,
324 ty: Static::Bool,
325 nullable: *n,
326 })
327 } else {
328 Err(ShapeResultError::UnexpectedType(format!("{self}")))
329 }
330 }
331
332 pub fn expect_bag(&self) -> ShapeResult<BagType> {
333 if let PartiqlShape::Static(StaticType {
334 ty: Static::Bag(bag),
335 ..
336 }) = self
337 {
338 Ok(bag.clone())
339 } else {
340 Err(ShapeResultError::UnexpectedType(format!("{self}")))
341 }
342 }
343
344 pub fn expect_struct(&self) -> ShapeResult<StructType> {
345 if let PartiqlShape::Static(StaticType {
346 ty: Static::Struct(stct),
347 ..
348 }) = self
349 {
350 Ok(stct.clone())
351 } else {
352 Err(ShapeResultError::UnexpectedType(format!("{self}")))
353 }
354 }
355
356 pub fn expect_static(&self) -> ShapeResult<StaticType> {
357 if let PartiqlShape::Static(s) = self {
358 Ok(s.clone())
359 } else {
360 Err(ShapeResultError::UnexpectedType(format!("{self}")))
361 }
362 }
363
364 pub fn expect_dynamic_type(&self) -> ShapeResult<PartiqlShape> {
365 if let PartiqlShape::Dynamic = self {
366 Ok(PartiqlShape::Dynamic)
367 } else {
368 Err(ShapeResultError::UnexpectedType(format!("{self}")))
369 }
370 }
371
372 pub fn expect_undefined(&self) -> ShapeResult<PartiqlShape> {
373 if let PartiqlShape::Undefined = self {
374 Ok(PartiqlShape::Undefined)
375 } else {
376 Err(ShapeResultError::UnexpectedType(format!("{self}")))
377 }
378 }
379}
380
381impl Display for PartiqlShape {
382 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
383 let x = match self {
384 PartiqlShape::Dynamic => "Dynamic".to_string(),
385 PartiqlShape::AnyOf(anyof) => {
386 format!("AnyOf({})", anyof.types.iter().cloned().join(", "))
387 }
388 PartiqlShape::Static(s) => format!("{s}"),
389 PartiqlShape::Undefined => "Undefined".to_string(),
390 };
391 write!(f, "{x}")
392 }
393}
394
395#[derive(Default, Debug)]
396pub struct ShapeBuilder<Id: NodeIdGenerator> {
397 id_gen: Id,
398}
399
400pub type PartiqlShapeBuilder = ShapeBuilder<AutoNodeIdGenerator>;
401pub type PartiqlNoIdShapeBuilder = ShapeBuilder<NullIdGenerator>;
402
403impl<Id: NodeIdGenerator + Default> ShapeBuilder<Id> {
404 #[track_caller]
407 pub fn singleton() -> &'static PartiqlShapeBuilder {
408 static SHAPE_BUILDER: OnceLock<PartiqlShapeBuilder> = OnceLock::new();
409 SHAPE_BUILDER.get_or_init(PartiqlShapeBuilder::default)
410 }
411
412 #[track_caller]
413 pub fn dummy_singleton() -> &'static PartiqlNoIdShapeBuilder {
414 static SHAPE_BUILDER: OnceLock<PartiqlNoIdShapeBuilder> = OnceLock::new();
415 SHAPE_BUILDER.get_or_init(PartiqlNoIdShapeBuilder::default)
416 }
417}
418
419impl<Id: NodeIdGenerator> ShapeBuilder<Id> {
420 #[inline]
421 pub fn new_static(&mut self, ty: Static) -> PartiqlShape {
422 let id = self.id_gen.next_id();
423 PartiqlShape::Static(StaticType {
424 id,
425 ty,
426 nullable: true,
427 })
428 }
429
430 #[inline]
431 pub fn new_non_nullable_static(&mut self, ty: Static) -> PartiqlShape {
432 let id = self.id_gen.next_id();
433 PartiqlShape::Static(StaticType {
434 id,
435 ty,
436 nullable: false,
437 })
438 }
439
440 #[inline]
441 pub fn new_dynamic(&mut self) -> PartiqlShape {
442 PartiqlShape::Dynamic
443 }
444
445 #[inline]
446 pub fn new_undefined(&mut self) -> PartiqlShape {
447 PartiqlShape::Undefined
448 }
449
450 #[inline]
451 pub fn new_struct(&mut self, s: StructType) -> PartiqlShape {
452 self.new_static(Static::Struct(s))
453 }
454
455 #[inline]
456 pub fn new_graph(&mut self) -> PartiqlShape {
457 self.new_static(Static::Graph())
458 }
459
460 #[inline]
461 pub fn new_struct_of_dyn(&mut self) -> PartiqlShape {
462 self.new_struct(StructType::new_any())
463 }
464
465 #[inline]
466 pub fn new_bag(&mut self, b: BagType) -> PartiqlShape {
467 self.new_static(Static::Bag(b))
468 }
469
470 #[inline]
471 pub fn new_bag_of<E>(&mut self, element_type: E) -> PartiqlShape
472 where
473 E: Into<PartiqlShape>,
474 {
475 self.new_bag(BagType::new_of(element_type))
476 }
477
478 #[inline]
479 pub fn new_bag_of_dyn(&mut self) -> PartiqlShape {
480 let element_type = self.new_dynamic();
481 self.new_bag_of(element_type)
482 }
483
484 #[inline]
485 pub fn new_bag_of_static(&mut self, ty: Static) -> PartiqlShape {
486 let element_type = self.new_static(ty);
487 self.new_bag_of(element_type)
488 }
489
490 #[inline]
491 pub fn new_bag_any_of<I>(&mut self, types: I) -> PartiqlShape
492 where
493 I: IntoIterator<Item = PartiqlShape>,
494 {
495 let shape = self.any_of(types);
496 let bag_type = BagType::new(Box::new(shape));
497 self.new_bag(bag_type)
498 }
499
500 #[inline]
501 pub fn new_array(&mut self, a: ArrayType) -> PartiqlShape {
502 self.new_static(Static::Array(a))
503 }
504
505 #[inline]
506 pub fn new_array_of<E>(&mut self, element_type: E) -> PartiqlShape
507 where
508 E: Into<PartiqlShape>,
509 {
510 self.new_array(ArrayType::new_of(element_type))
511 }
512
513 #[inline]
514 pub fn new_array_of_dyn(&mut self) -> PartiqlShape {
515 let element_type = self.new_dynamic();
516 self.new_array_of(element_type)
517 }
518
519 #[inline]
520 pub fn new_array_of_static(&mut self, ty: Static) -> PartiqlShape {
521 let element_type = self.new_static(ty);
522 self.new_array_of(element_type)
523 }
524
525 #[inline]
526 pub fn new_array_any_of<I>(&mut self, types: I) -> PartiqlShape
527 where
528 I: IntoIterator<Item = PartiqlShape>,
529 {
530 let shape = self.any_of(types);
531 let array_type = ArrayType::new(Box::new(shape));
532 self.new_array(array_type)
533 }
534
535 pub fn any_of<I>(&mut self, types: I) -> PartiqlShape
543 where
544 I: IntoIterator<Item = PartiqlShape>,
545 {
546 let any_of = AnyOf::from_iter(types);
547 match any_of.types.len() {
548 0 => self.new_dynamic(),
549 1 => {
550 let AnyOf { types } = any_of;
551 types.into_iter().next().unwrap()
552 }
553 _ => PartiqlShape::AnyOf(any_of),
555 }
556 }
557
558 #[inline]
559 pub fn union(&mut self, lhs: PartiqlShape, rhs: PartiqlShape) -> PartiqlShape {
560 match (lhs, rhs) {
561 (PartiqlShape::Dynamic, _) | (_, PartiqlShape::Dynamic) => PartiqlShape::Dynamic,
562 (PartiqlShape::AnyOf(lhs), PartiqlShape::AnyOf(rhs)) => {
563 self.any_of(lhs.types.into_iter().chain(rhs.types))
564 }
565 (PartiqlShape::AnyOf(anyof), other) | (other, PartiqlShape::AnyOf(anyof)) => {
566 let mut types = anyof.types;
567 types.insert(other);
568 self.any_of(types)
569 }
570 (l, r) => {
571 let types = [l, r];
572 self.any_of(types)
573 }
574 }
575 }
576
577 pub fn union_of<I>(&mut self, types: I) -> PartiqlShape
578 where
579 I: IntoIterator<Item = PartiqlShape>,
580 {
581 let types: Vec<_> = types.into_iter().collect();
582 match types.len() {
583 0 => self.new_undefined(),
584 1 => types.into_iter().next().unwrap(),
585 _ => types.into_iter().reduce(|l, r| self.union(l, r)).unwrap(),
586 }
587 }
588
589 #[inline]
590 pub fn as_non_nullable(&mut self, shape: &PartiqlShape) -> Option<PartiqlShape> {
591 if let PartiqlShape::Static(stype) = shape {
592 Some(self.new_non_nullable_static(stype.ty.clone()))
593 } else {
594 None
595 }
596 }
597}
598
599pub trait ShapeBuilderExtensions<Id: NodeIdGenerator> {
600 fn into_union(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape;
601
602 fn into_any_of(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape;
603
604 fn into_array(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape;
605
606 fn into_bag(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape;
607}
608
609impl<const N: usize, Id: NodeIdGenerator, E: Into<PartiqlShape>> ShapeBuilderExtensions<Id>
610 for [E; N]
611{
612 #[inline]
613 fn into_union(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
614 bld.union_of(self.into_iter().map(|e| e.into()))
615 }
616
617 #[inline]
618 fn into_any_of(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
619 bld.any_of(self.into_iter().map(|e| e.into()))
620 }
621
622 #[inline]
623 fn into_array(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
624 let ty = self.into_any_of(bld);
625 bld.new_array_of(ty)
626 }
627
628 #[inline]
629 fn into_bag(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
630 let ty = self.into_any_of(bld);
631 bld.new_bag_of(ty)
632 }
633}
634
635impl<Id: NodeIdGenerator, E: Into<PartiqlShape>> ShapeBuilderExtensions<Id> for E {
636 #[inline]
637 fn into_union(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
638 [self].into_union(bld)
639 }
640
641 #[inline]
642 fn into_any_of(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
643 [self].into_any_of(bld)
644 }
645
646 #[inline]
647 fn into_array(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
648 bld.new_array_of(self)
649 }
650
651 #[inline]
652 fn into_bag(self, bld: &mut ShapeBuilder<Id>) -> PartiqlShape {
653 bld.new_bag_of(self)
654 }
655}
656
657#[derive(Educe, Eq, Debug, Clone)]
658#[educe(PartialEq, Hash)]
659#[allow(dead_code)]
660pub struct AnyOf {
661 #[educe(Hash(method(indexset_hash)))]
662 types: IndexSet<PartiqlShape>,
663}
664
665impl AnyOf {
666 #[inline]
667 pub const fn new(types: IndexSet<PartiqlShape>) -> Self {
668 AnyOf { types }
669 }
670
671 pub fn types(&self) -> impl Iterator<Item = &PartiqlShape> {
672 self.types.iter()
673 }
674}
675
676impl FromIterator<PartiqlShape> for AnyOf {
677 fn from_iter<T: IntoIterator<Item = PartiqlShape>>(iter: T) -> Self {
678 AnyOf {
679 types: iter.into_iter().collect(),
680 }
681 }
682}
683
684#[derive(Debug, Clone, Eq, PartialEq, Hash)]
685pub struct StaticType {
686 id: NodeId,
687 ty: Static,
688 nullable: bool,
689}
690
691impl StaticType {
692 #[inline]
693 pub fn ty(&self) -> &Static {
694 &self.ty
695 }
696
697 pub fn ty_id(&self) -> &NodeId {
698 &self.id
699 }
700
701 #[inline]
702 pub fn is_nullable(&self) -> bool {
703 self.nullable
704 }
705
706 #[inline]
707 pub fn is_not_nullable(&self) -> bool {
708 !self.nullable
709 }
710
711 pub fn is_scalar(&self) -> bool {
712 self.ty.is_scalar()
713 }
714
715 pub fn is_sequence(&self) -> bool {
716 self.ty.is_sequence()
717 }
718
719 pub fn is_struct(&self) -> bool {
720 self.ty.is_struct()
721 }
722}
723
724impl From<StaticType> for PartiqlShape {
725 fn from(value: StaticType) -> Self {
726 PartiqlShape::Static(value)
727 }
728}
729
730impl Display for StaticType {
731 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
732 let ty = &self.ty;
733 if self.nullable {
734 write!(f, "{ty}")
735 } else {
736 write!(f, "NOT NULL {ty}")
737 }
738 }
739}
740
741#[derive(Debug, Clone, Eq, PartialEq, Hash)]
742pub enum Static {
743 Int,
745 Int8,
746 Int16,
747 Int32,
748 Int64,
749 Bool,
750 Decimal,
751 DecimalP(usize, usize),
752
753 Float32,
754 Float64,
755
756 String,
757 StringFixed(usize),
758 StringVarying(usize),
759
760 DateTime,
761
762 Struct(StructType),
764 Bag(BagType),
765 Array(ArrayType),
766
767 Graph(
768 ),
770 }
772
773pub enum StaticCategory<'a> {
774 Graph(),
775 Tuple(),
776 Sequence(&'a PartiqlShape),
777 Scalar(&'a Static),
778}
779
780impl Static {
781 pub fn is_scalar(&self) -> bool {
782 !matches!(self, Static::Struct(_) | Static::Bag(_) | Static::Array(_))
783 }
784
785 pub fn is_sequence(&self) -> bool {
786 matches!(self, Static::Bag(_) | Static::Array(_))
787 }
788
789 pub fn is_struct(&self) -> bool {
790 matches!(self, Static::Struct(_))
791 }
792
793 pub fn category(&self) -> StaticCategory<'_> {
794 match self {
795 Static::Struct(_) => StaticCategory::Tuple(),
796 Static::Bag(b) => StaticCategory::Sequence(b.element_type()),
797 Static::Array(b) => StaticCategory::Sequence(b.element_type()),
798 Static::Graph() => StaticCategory::Graph(),
799 _ => StaticCategory::Scalar(self),
800 }
801 }
802}
803
804impl Display for Static {
806 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
807 match self {
808 Static::Int => write!(f, "Int"),
809 Static::Int8 => write!(f, "Int8"),
810 Static::Int16 => write!(f, "Int16"),
811 Static::Int32 => write!(f, "Int32"),
812 Static::Int64 => write!(f, "Int64"),
813 Static::Bool => write!(f, "Bool"),
814 Static::Decimal => write!(f, "Decimal"),
815 Static::DecimalP(p, s) => {
816 write!(f, "Decimal({p},{s})")
817 }
818 Static::Float32 => write!(f, "Float32"),
819 Static::Float64 => write!(f, "Float64"),
820 Static::String => write!(f, "String"),
821 Static::StringFixed(_) => {
822 todo!()
823 }
824 Static::StringVarying(_) => {
825 todo!()
826 }
827 Static::DateTime => write!(f, "DateTime"),
828 Static::Struct(inner) => std::fmt::Display::fmt(inner, f),
829 Static::Bag(inner) => std::fmt::Display::fmt(inner, f),
830 Static::Array(inner) => std::fmt::Display::fmt(inner, f),
831 Static::Graph() => write!(f, "Graph"),
832 }
833 }
834}
835
836pub const TYPE_DYNAMIC: PartiqlShape = PartiqlShape::Dynamic;
837
838#[derive(Educe, Eq, Debug, Clone)]
839#[educe(PartialEq, Hash)]
840#[allow(dead_code)]
841pub struct StructType {
842 #[educe(Hash(method(indexset_hash)))]
843 constraints: IndexSet<StructConstraint>,
844}
845
846impl StructType {
847 #[inline]
848 pub fn new(constraints: IndexSet<StructConstraint>) -> Self {
849 StructType { constraints }
850 }
851
852 #[inline]
853 pub fn new_any() -> Self {
854 StructType {
855 constraints: Default::default(),
856 }
857 }
858
859 pub fn fields_set(&self) -> IndexSet<StructField> {
860 self.constraints
861 .iter()
862 .flat_map(|c| {
863 if let StructConstraint::Fields(fields) = c.clone() {
864 fields
865 } else {
866 Default::default()
867 }
868 })
869 .collect()
870 }
871
872 pub fn fields(&self) -> impl Iterator<Item = &StructField> {
873 self.constraints
874 .iter()
875 .filter_map(|c| {
876 if let StructConstraint::Fields(fields) = c {
877 Some(fields)
878 } else {
879 None
880 }
881 })
882 .flat_map(|f| f.iter())
883 }
884
885 #[inline]
886 pub fn is_partial(&self) -> bool {
887 !self.is_closed()
888 }
889
890 #[inline]
891 pub fn is_closed(&self) -> bool {
892 self.constraints.contains(&StructConstraint::Open(false))
893 }
894}
895
896impl Display for StructType {
897 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
898 let partial = self.is_partial();
899 write!(f, "{{")?;
900 let mut first = true;
901 for StructField { name, ty, optional } in self.fields() {
902 if !first {
903 write!(f, ", ")?;
904 }
905 if *optional {
906 write!(f, "{name}?: {ty}")?;
907 } else {
908 write!(f, "{name}: {ty}")?;
909 }
910 first = false
911 }
912 if partial {
913 if !first {
914 write!(f, ", ")?;
915 }
916 write!(f, "...")?;
917 }
918 write!(f, "}}")
919 }
920}
921
922#[derive(Educe, Eq, Debug, Clone)]
923#[educe(PartialEq, Hash)]
924#[allow(dead_code)]
925#[non_exhaustive]
926pub enum StructConstraint {
927 Open(bool),
928 Ordered(bool),
929 DuplicateAttrs(bool),
930 Fields(#[educe(Hash(method(indexset_hash)))] IndexSet<StructField>),
931}
932
933#[derive(Debug, Clone, Hash, Eq, PartialEq)]
934#[allow(dead_code)]
935pub struct StructField {
936 optional: bool,
937 name: String,
938 ty: PartiqlShape,
939}
940
941impl StructField {
942 #[inline]
943 pub fn new(name: &str, ty: PartiqlShape) -> Self {
944 StructField {
945 name: name.to_string(),
946 ty,
947 optional: false,
948 }
949 }
950
951 #[inline]
952 pub fn new_optional(name: &str, ty: PartiqlShape) -> Self {
953 StructField {
954 name: name.to_string(),
955 ty,
956 optional: true,
957 }
958 }
959
960 #[inline]
961 pub fn name(&self) -> &str {
962 self.name.as_str()
963 }
964
965 #[inline]
966 pub fn ty(&self) -> &PartiqlShape {
967 &self.ty
968 }
969
970 #[inline]
971 pub fn is_optional(&self) -> bool {
972 self.optional
973 }
974}
975
976impl From<(&str, PartiqlShape)> for StructField {
977 fn from(value: (&str, PartiqlShape)) -> Self {
978 StructField {
979 name: value.0.to_string(),
980 ty: value.1,
981 optional: false,
982 }
983 }
984}
985
986impl From<(&str, PartiqlShape, bool)> for StructField {
987 fn from(value: (&str, PartiqlShape, bool)) -> Self {
988 StructField {
989 name: value.0.to_string(),
990 ty: value.1,
991 optional: value.2,
992 }
993 }
994}
995
996#[derive(Educe, Eq, Debug, Clone)]
997#[educe(PartialEq, Hash)]
998#[allow(dead_code)]
999pub struct BagType {
1000 element_type: Box<PartiqlShape>,
1001}
1002
1003impl BagType {
1004 #[inline]
1005 pub fn new(typ: Box<PartiqlShape>) -> Self {
1006 BagType { element_type: typ }
1007 }
1008
1009 #[inline]
1010 pub fn new_of<E>(element_type: E) -> Self
1011 where
1012 E: Into<PartiqlShape>,
1013 {
1014 Self::new(Box::new(element_type.into()))
1015 }
1016
1017 #[inline]
1018 pub fn new_any() -> Self {
1019 Self::new_of(PartiqlShape::Dynamic)
1020 }
1021
1022 pub fn element_type(&self) -> &PartiqlShape {
1023 &self.element_type
1024 }
1025}
1026
1027impl Display for BagType {
1028 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1029 let ty = &self.element_type;
1030 write!(f, "<<{ty}>>")
1031 }
1032}
1033
1034#[derive(Educe, Eq, Debug, Clone)]
1035#[educe(PartialEq, Hash)]
1036#[allow(dead_code)]
1037pub struct ArrayType {
1038 element_type: Box<PartiqlShape>,
1039 }
1042
1043impl ArrayType {
1044 #[inline]
1045 pub fn new(typ: Box<PartiqlShape>) -> Self {
1046 ArrayType { element_type: typ }
1047 }
1048
1049 #[inline]
1050 pub fn new_of<E>(element_type: E) -> Self
1051 where
1052 E: Into<PartiqlShape>,
1053 {
1054 Self::new(Box::new(element_type.into()))
1055 }
1056
1057 #[inline]
1058 pub fn new_any() -> Self {
1059 Self::new_of(PartiqlShape::Dynamic)
1060 }
1061
1062 #[inline]
1063 pub fn element_type(&self) -> &PartiqlShape {
1064 &self.element_type
1065 }
1066}
1067
1068impl Display for ArrayType {
1069 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1070 let ty = &self.element_type;
1071 write!(f, "[{ty}]")
1072 }
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use crate::{
1078 PartiqlNoIdShapeBuilder, PartiqlShape, PartiqlShapeBuilder, ShapeBuilderExtensions, Static,
1079 StructConstraint, StructField, StructType,
1080 };
1081 use indexmap::IndexSet;
1082
1083 #[test]
1084 fn union() {
1085 let mut bld = PartiqlNoIdShapeBuilder::default();
1086
1087 let expect_int = bld.new_static(Static::Int);
1088 assert_eq!(
1089 expect_int,
1090 [type_int!(bld), type_int!(bld)].into_union(&mut bld)
1091 );
1092
1093 let numbers = [bld.new_static(Static::Int), bld.new_static(Static::Float32)];
1094 let expect_nums = bld.any_of(numbers);
1095 assert_eq!(
1096 expect_nums,
1097 [type_int!(bld), type_float32!(bld)].into_union(&mut bld)
1098 );
1099 assert_eq!(
1100 expect_nums,
1101 [
1102 [type_int!(bld), type_float32!(bld)].into_union(&mut bld),
1103 [type_int!(bld), type_float32!(bld)].into_union(&mut bld),
1104 ]
1105 .into_any_of(&mut bld)
1106 );
1107 assert_eq!(
1108 expect_nums,
1109 [
1110 [type_int!(bld), type_float32!(bld)].into_union(&mut bld),
1111 [type_int!(bld), type_float32!(bld)].into_union(&mut bld),
1112 [
1113 [type_int!(bld), type_float32!(bld)].into_union(&mut bld),
1114 [type_int!(bld), type_float32!(bld)].into_union(&mut bld)
1115 ]
1116 .into_any_of(&mut bld)
1117 ]
1118 .into_any_of(&mut bld)
1119 );
1120 }
1121
1122 #[test]
1123 fn unique_node_ids() {
1124 let mut bld = PartiqlShapeBuilder::default();
1125 let age_field = struct_fields![("age", type_int!(bld))];
1126 let details = type_struct![bld, IndexSet::from([age_field])];
1127
1128 let fields = [
1129 StructField::new("id", type_int!(bld)),
1130 StructField::new("name", type_string!(bld)),
1131 StructField::new("details", details.clone()),
1132 ];
1133
1134 let row = type_struct![
1135 bld,
1136 IndexSet::from([
1137 StructConstraint::Fields(IndexSet::from(fields)),
1138 StructConstraint::Open(false)
1139 ])
1140 ];
1141
1142 let shape = type_bag![bld, row.clone()];
1143
1144 let mut ids = collect_ids(shape);
1145 ids.sort_unstable();
1146 assert!(ids.windows(2).all(|w| w[0] != w[1]));
1147 }
1148
1149 fn collect_ids(row: PartiqlShape) -> Vec<u32> {
1150 let mut out = vec![];
1151 match row {
1152 PartiqlShape::Dynamic => {}
1153 PartiqlShape::AnyOf(anyof) => {
1154 for shape in anyof.types {
1155 out.push(collect_ids(shape));
1156 }
1157 }
1158 PartiqlShape::Static(static_type) => {
1159 out.push(vec![static_type.id.0]);
1160 match static_type.ty {
1161 Static::Struct(struct_type) => {
1162 for f in struct_type.fields() {
1163 out.push(collect_ids(f.ty.clone()));
1164 }
1165 }
1166 Static::Bag(bag_type) => out.push(collect_ids(*bag_type.element_type)),
1167 Static::Array(array_type) => out.push(collect_ids(*array_type.element_type)),
1168 _ => {}
1169 }
1170 }
1171 PartiqlShape::Undefined => {}
1172 }
1173 out.into_iter().flatten().collect()
1174 }
1175}