1use super::TypeExpr;
6use super::argument::{
7 EnumOptions as ParsedEnumOptions, EnumOptionsError as ParsedEnumOptionsError,
8};
9use super::extensions::TypeContext;
10use super::type_ast::TypeExprParam;
11use crate::parse::Parse;
12use crate::parse::text::simple_extensions::type_ast::TypeParseError;
13use crate::text::simple_extensions::{
14 EnumOptions as RawEnumOptions, SimpleExtensionsTypesItem, Type as RawType, TypeParamDefs,
15 TypeParamDefsItem, TypeParamDefsItemType,
16};
17use indexmap::IndexMap;
18use serde_json::Value;
19use std::convert::TryFrom;
20use std::fmt;
21use std::ops::RangeInclusive;
22use thiserror::Error;
23
24fn write_separated<I, T>(
29 f: &mut fmt::Formatter<'_>,
30 iter: I,
31 start: &str,
32 end: &str,
33 sep: &str,
34) -> fmt::Result
35where
36 I: IntoIterator<Item = T>,
37 T: fmt::Display,
38{
39 let mut it = iter.into_iter();
40 if let Some(first) = it.next() {
41 f.write_str(start)?;
42 write!(f, "{first}")?;
43 for item in it {
44 f.write_str(sep)?;
45 write!(f, "{item}")?;
46 }
47 f.write_str(end)
48 } else {
49 Ok(())
50 }
51}
52
53struct KeyValueDisplay<K, V>(K, V, &'static str);
55
56impl<K, V> fmt::Display for KeyValueDisplay<K, V>
57where
58 K: fmt::Display,
59 V: fmt::Display,
60{
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "{}{}{}", self.0, self.2, self.1)
63 }
64}
65
66#[derive(Clone, Debug, PartialEq)]
68pub enum TypeKind {
69 Boolean,
71 I8,
73 I16,
75 I32,
77 I64,
79 Fp32,
81 Fp64,
83 String,
85 Binary,
87 Date,
89 IntervalYear,
91 Uuid,
93 FixedChar {
95 length: IntegerParameter,
97 },
98 VarChar {
100 length: IntegerParameter,
102 },
103 FixedBinary {
105 length: IntegerParameter,
107 },
108 Decimal {
110 precision: IntegerParameter,
112 scale: IntegerParameter,
114 },
115 PrecisionTime {
117 precision: IntegerParameter,
119 },
120 PrecisionTimestamp {
122 precision: IntegerParameter,
124 },
125 PrecisionTimestampTz {
127 precision: IntegerParameter,
129 },
130 IntervalDay {
132 precision: IntegerParameter,
134 },
135 IntervalCompound {
137 precision: IntegerParameter,
139 },
140 Extension {
142 name: String,
144 parameters: Vec<TypeParameter>,
146 },
147 List(Box<Type>),
149 Map {
151 key: Box<Type>,
153 value: Box<Type>,
155 },
156 Struct(Vec<Type>),
158 NamedStruct {
160 fields: IndexMap<String, Type>,
163 },
164 Func {
166 parameters: Vec<Type>,
168 return_type: Box<Type>,
170 },
171 Any,
173 Variable {
175 id: u32,
177 },
178}
179
180impl TypeKind {
181 pub fn is_name(name: &str) -> bool {
183 let lower = name.to_ascii_lowercase();
184 primitive_builtin(&lower).is_some()
185 || matches!(
186 lower.as_str(),
187 "fixedchar"
188 | "varchar"
189 | "fixedbinary"
190 | "decimal"
191 | "precision_time"
192 | "precision_timestamp"
193 | "precision_timestamp_tz"
194 | "interval_day"
195 | "interval_compound"
196 )
197 }
198
199 fn fmt_with_nullability(&self, f: &mut fmt::Formatter<'_>, nullable: bool) -> fmt::Result {
200 let nullability = if nullable { "?" } else { "" };
201 let name = match self {
202 Self::Boolean => "bool",
203 Self::I8 => "i8",
204 Self::I16 => "i16",
205 Self::I32 => "i32",
206 Self::I64 => "i64",
207 Self::Fp32 => "fp32",
208 Self::Fp64 => "fp64",
209 Self::String => "string",
210 Self::Binary => "binary",
211 Self::Date => "date",
212 Self::IntervalYear => "interval_year",
213 Self::Uuid => "uuid",
214 Self::FixedChar { .. } => "fixedchar",
215 Self::VarChar { .. } => "varchar",
216 Self::FixedBinary { .. } => "fixedbinary",
217 Self::Decimal { .. } => "decimal",
218 Self::PrecisionTime { .. } => "precision_time",
219 Self::PrecisionTimestamp { .. } => "precision_timestamp",
220 Self::PrecisionTimestampTz { .. } => "precision_timestamp_tz",
221 Self::IntervalDay { .. } => "interval_day",
222 Self::IntervalCompound { .. } => "interval_compound",
223 Self::List(_) => "list",
224 Self::Map { .. } => "map",
225 Self::Struct(_) => "struct",
226 Self::Func { .. } => "func",
227 Self::Extension { name, parameters } => {
228 write!(f, "u!{name}{nullability}")?;
229 return write_separated(f, parameters.iter(), "<", ">", ", ");
230 }
231 Self::NamedStruct { fields } => {
232 let fields = fields
233 .iter()
234 .map(|(name, field)| KeyValueDisplay(name, field, ": "));
235 write_separated(f, fields, "{", "}", ", ")?;
236 return f.write_str(nullability);
237 }
238 Self::Any => return write!(f, "any{nullability}"),
239 Self::Variable { id } => return write!(f, "any{id}{nullability}"),
240 };
241 write!(f, "{name}{nullability}")?;
242 match self {
243 Self::FixedChar { length }
244 | Self::VarChar { length }
245 | Self::FixedBinary { length } => {
246 write!(f, "<{length}>")
247 }
248 Self::Decimal { precision, scale } => write!(f, "<{precision}, {scale}>"),
249 Self::PrecisionTime { precision }
250 | Self::PrecisionTimestamp { precision }
251 | Self::PrecisionTimestampTz { precision }
252 | Self::IntervalDay { precision }
253 | Self::IntervalCompound { precision } => write!(f, "<{precision}>"),
254 Self::List(element) => write!(f, "<{element}>"),
255 Self::Map { key, value } => write!(f, "<{key}, {value}>"),
256 Self::Struct(fields) => {
257 f.write_str("<")?;
258 write_separated(f, fields.iter(), "", "", ", ")?;
259 f.write_str(">")
260 }
261 Self::Func {
262 parameters,
263 return_type,
264 } => {
265 if parameters.len() == 1 {
266 write!(f, "<{} -> {return_type}>", parameters[0])
267 } else {
268 f.write_str("<(")?;
269 write_separated(f, parameters.iter(), "", "", ", ")?;
270 write!(f, ") -> {return_type}>")
271 }
272 }
273 _ => Ok(()),
274 }
275 }
276}
277
278impl fmt::Display for TypeKind {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 self.fmt_with_nullability(f, false)
283 }
284}
285
286#[derive(Clone, Debug, PartialEq)]
288pub enum IntegerParameter {
289 Literal(i64),
291 Variable(String),
293}
294
295impl IntegerParameter {
296 pub fn is_concrete(&self) -> bool {
298 matches!(self, Self::Literal(_))
299 }
300}
301
302impl fmt::Display for IntegerParameter {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 match self {
305 Self::Literal(value) => write!(f, "{value}"),
306 Self::Variable(name) => f.write_str(name),
307 }
308 }
309}
310
311#[derive(Clone, Debug, PartialEq)]
313pub enum TypeParameter {
314 Integer(IntegerParameter),
316 Type(Type),
318 Name(String),
320 }
323
324impl TypeParameter {
325 pub fn is_concrete(&self) -> bool {
327 match self {
328 Self::Integer(parameter) => parameter.is_concrete(),
329 Self::Type(type_) => type_.is_concrete(),
330 Self::Name(_) => false,
331 }
332 }
333}
334
335impl fmt::Display for TypeParameter {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 match self {
338 TypeParameter::Integer(i) => write!(f, "{i}"),
339 TypeParameter::Type(t) => write!(f, "{t}"),
340 TypeParameter::Name(name) => f.write_str(name),
341 }
342 }
343}
344
345fn primitive_builtin(lower_name: &str) -> Option<TypeKind> {
347 match lower_name {
348 "bool" | "boolean" => Some(TypeKind::Boolean),
349 "i8" => Some(TypeKind::I8),
350 "i16" => Some(TypeKind::I16),
351 "i32" => Some(TypeKind::I32),
352 "i64" => Some(TypeKind::I64),
353 "fp32" => Some(TypeKind::Fp32),
354 "fp64" => Some(TypeKind::Fp64),
355 "string" => Some(TypeKind::String),
356 "binary" => Some(TypeKind::Binary),
357 "date" => Some(TypeKind::Date),
358 "interval_year" => Some(TypeKind::IntervalYear),
359 "uuid" => Some(TypeKind::Uuid),
360 _ => None,
361 }
362}
363
364#[derive(Clone, Debug, PartialEq)]
366pub enum ParameterConstraint {
367 DataType,
369 Integer {
371 min: Option<i64>,
373 max: Option<i64>,
375 },
376 Enumeration {
378 options: ParsedEnumOptions,
380 },
381 Boolean,
383 String,
385}
386
387impl ParameterConstraint {
388 fn raw_type(&self) -> TypeParamDefsItemType {
390 match self {
391 ParameterConstraint::DataType => TypeParamDefsItemType::DataType,
392 ParameterConstraint::Boolean => TypeParamDefsItemType::Boolean,
393 ParameterConstraint::Integer { .. } => TypeParamDefsItemType::Integer,
394 ParameterConstraint::Enumeration { .. } => TypeParamDefsItemType::Enumeration,
395 ParameterConstraint::String => TypeParamDefsItemType::String,
396 }
397 }
398
399 fn raw_bounds(&self) -> (Option<f64>, Option<f64>) {
401 match self {
402 ParameterConstraint::Integer { min, max } => {
403 (min.map(|i| i as f64), max.map(|i| i as f64))
404 }
405 _ => (None, None),
406 }
407 }
408
409 fn raw_options(&self) -> Option<RawEnumOptions> {
411 match self {
412 ParameterConstraint::Enumeration { options } => Some(options.clone().into()),
413 _ => None,
414 }
415 }
416
417 pub fn is_valid_value(&self, value: &Value) -> bool {
419 match (self, value) {
420 (ParameterConstraint::DataType, Value::String(_)) => true,
421 (ParameterConstraint::Integer { min, max }, Value::Number(n)) => {
422 if let Some(i) = n.as_i64() {
423 min.is_none_or(|min_val| i >= min_val) && max.is_none_or(|max_val| i <= max_val)
424 } else {
425 false
426 }
427 }
428 (ParameterConstraint::Enumeration { options }, Value::String(s)) => options.contains(s),
429 (ParameterConstraint::Boolean, Value::Bool(_)) => true,
430 (ParameterConstraint::String, Value::String(_)) => true,
431 _ => false,
432 }
433 }
434
435 fn from_raw(
436 t: TypeParamDefsItemType,
437 opts: Option<RawEnumOptions>,
438 min: Option<f64>,
439 max: Option<f64>,
440 ) -> Result<Self, TypeParamError> {
441 Ok(match t {
442 TypeParamDefsItemType::DataType => Self::DataType,
443 TypeParamDefsItemType::Boolean => Self::Boolean,
444 TypeParamDefsItemType::Integer => {
445 match (min, max) {
446 (Some(min_f), _) if min_f.fract() != 0.0 => {
447 return Err(TypeParamError::InvalidIntegerBounds { min, max });
448 }
449 (_, Some(max_f)) if max_f.fract() != 0.0 => {
450 return Err(TypeParamError::InvalidIntegerBounds { min, max });
451 }
452 _ => (),
453 }
454
455 let min_i = min.map(|v| v as i64);
456 let max_i = max.map(|v| v as i64);
457 Self::Integer {
458 min: min_i,
459 max: max_i,
460 }
461 }
462 TypeParamDefsItemType::Enumeration => {
463 let options: ParsedEnumOptions =
464 opts.ok_or(TypeParamError::MissingEnumOptions)?.try_into()?;
465 Self::Enumeration { options }
466 }
467 TypeParamDefsItemType::String => Self::String,
468 })
469 }
470}
471
472#[derive(Clone, Debug, PartialEq)]
474pub struct TypeParam {
475 pub name: String,
477 pub param_type: ParameterConstraint,
479 pub description: Option<String>,
481}
482
483impl TypeParam {
484 pub fn new(name: String, param_type: ParameterConstraint, description: Option<String>) -> Self {
486 Self {
487 name,
488 param_type,
489 description,
490 }
491 }
492
493 pub fn is_valid_value(&self, value: &Value) -> bool {
495 self.param_type.is_valid_value(value)
496 }
497}
498
499impl TryFrom<TypeParamDefsItem> for TypeParam {
500 type Error = TypeParamError;
501
502 fn try_from(item: TypeParamDefsItem) -> Result<Self, Self::Error> {
503 let name = item.name.ok_or(TypeParamError::MissingName)?;
504 let param_type =
505 ParameterConstraint::from_raw(item.type_, item.options, item.min, item.max)?;
506
507 Ok(Self {
508 name,
509 param_type,
510 description: item.description,
511 })
512 }
513}
514
515#[derive(Debug, Error, PartialEq)]
517pub enum ExtensionTypeError {
518 #[error("{0}")]
520 InvalidName(#[from] InvalidTypeName),
521 #[error("Any type variable is invalid for concrete types: any{}{}", id, nullability.then_some("?").unwrap_or(""))]
523 InvalidAnyTypeVariable {
524 id: u32,
526 nullability: bool,
528 },
529 #[error("Type '{type_string}' must be concrete in this context")]
531 ExpectedConcreteType {
532 type_string: String,
534 },
535 #[error(
537 "Unknown type name: '{}'. Extension types must use the u! prefix (e.g., u!{})",
538 name,
539 name
540 )]
541 UnknownTypeName {
542 name: String,
544 },
545 #[error("Invalid parameter: {0}")]
547 InvalidParameter(#[from] TypeParamError),
548 #[error("Invalid structure field type: {0}")]
550 InvalidFieldType(String),
551 #[error("Duplicate struct field '{field_name}'")]
553 DuplicateFieldName {
554 field_name: String,
556 },
557 #[error("Type '{type_name}' expects {expected} parameters, got {actual}")]
559 InvalidParameterCount {
560 type_name: String,
562 expected: usize,
564 actual: usize,
566 },
567 #[error("Type '{type_name}' parameter {index} must be {expected}")]
569 InvalidParameterKind {
570 type_name: String,
572 index: usize,
574 expected: &'static str,
576 },
577 #[error("Type '{type_name}' parameter {index} value {value} is not within {expected}")]
579 InvalidParameterValue {
580 type_name: String,
582 index: usize,
584 value: i64,
586 expected: &'static str,
588 },
589 #[error("Type '{type_name}' parameter {index} value {value} is out of range {expected:?}")]
591 InvalidParameterRange {
592 type_name: String,
594 index: usize,
596 value: i64,
598 expected: RangeInclusive<i32>,
600 },
601 #[error("Structure representation cannot be nullable: {type_string}")]
603 StructureCannotBeNullable {
604 type_string: String,
606 },
607 #[error("Error parsing type: {0}")]
609 ParseType(#[from] TypeParseError),
610}
611
612#[derive(Debug, Error, PartialEq)]
614pub enum TypeParamError {
615 #[error("Parameter name is required")]
617 MissingName,
618 #[error("Integer parameter has invalid min/max values: min={min:?}, max={max:?}")]
620 InvalidIntegerBounds {
621 min: Option<f64>,
623 max: Option<f64>,
625 },
626 #[error("Enumeration parameter is missing options")]
628 MissingEnumOptions,
629 #[error("Enumeration parameter has invalid options: {0}")]
631 InvalidEnumOptions(#[from] ParsedEnumOptionsError),
632}
633
634#[derive(Clone, Debug, PartialEq)]
636pub struct CustomType {
637 pub name: String,
639 pub parameters: Vec<TypeParam>,
641 pub structure: Option<Type>,
643 pub variadic: Option<bool>,
645 pub description: Option<String>,
647}
648
649impl CustomType {
650 pub fn validate_name(name: &str) -> Result<(), InvalidTypeName> {
658 let mut chars = name.chars();
659 let first = chars
660 .next()
661 .ok_or_else(|| InvalidTypeName(name.to_string()))?;
662 if !(first.is_ascii_alphabetic() || first == '_' || first == '$') {
663 return Err(InvalidTypeName(name.to_string()));
664 }
665
666 if !chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$') {
667 return Err(InvalidTypeName(name.to_string()));
668 }
669
670 Ok(())
671 }
672
673 pub fn new(
675 name: String,
676 parameters: Vec<TypeParam>,
677 structure: Option<Type>,
678 variadic: Option<bool>,
679 description: Option<String>,
680 ) -> Result<Self, ExtensionTypeError> {
681 Self::validate_name(&name)?;
682
683 Ok(Self {
684 name,
685 parameters,
686 structure,
687 variadic,
688 description,
689 })
690 }
691}
692
693impl From<CustomType> for SimpleExtensionsTypesItem {
694 fn from(value: CustomType) -> Self {
695 let parameters = if value.parameters.is_empty() {
697 None
698 } else {
699 Some(TypeParamDefs(
700 value
701 .parameters
702 .into_iter()
703 .map(|param| {
704 let (min, max) = param.param_type.raw_bounds();
705 TypeParamDefsItem {
706 name: Some(param.name),
707 description: param.description,
708 type_: param.param_type.raw_type(),
709 min,
710 max,
711 options: param.param_type.raw_options(),
712 optional: None,
716 }
717 })
718 .collect(),
719 ))
720 };
721
722 let structure = value.structure.map(Into::into);
724
725 SimpleExtensionsTypesItem {
726 name: value.name,
727 description: value.description,
728 metadata: Default::default(),
729 parameters,
730 structure,
731 variadic: value.variadic,
732 deprecated: None,
734 }
735 }
736}
737
738impl Parse<TypeContext> for SimpleExtensionsTypesItem {
739 type Parsed = CustomType;
740 type Error = ExtensionTypeError;
741
742 fn parse(self, ctx: &mut TypeContext) -> Result<Self::Parsed, Self::Error> {
743 let name = self.name;
744 CustomType::validate_name(&name)?;
745
746 ctx.found(&name);
748
749 let parameters = if let Some(param_defs) = self.parameters {
750 param_defs
751 .0
752 .into_iter()
753 .map(TypeParam::try_from)
754 .collect::<Result<Vec<_>, _>>()?
755 } else {
756 Vec::new()
757 };
758
759 let structure = match self.structure {
761 Some(structure_data) => {
762 let parsed = Parse::parse(structure_data, ctx)?;
763 Some(parsed)
768 }
769 None => None,
770 };
771
772 Ok(CustomType {
773 name,
774 parameters,
775 structure,
776 variadic: self.variadic,
777 description: self.description,
778 })
779 }
780}
781
782impl Parse<TypeContext> for RawType {
783 type Parsed = Type;
784 type Error = ExtensionTypeError;
785
786 fn parse(self, ctx: &mut TypeContext) -> Result<Self::Parsed, Self::Error> {
787 match self {
788 RawType::String(type_string) => {
789 let parsed_type = TypeExpr::parse(&type_string)?;
790 let mut link = |name: &str| ctx.linked(name);
791 parsed_type.visit_references(&mut link);
792 expect_concrete_type(parsed_type)
793 }
794 RawType::Object(field_map) => {
795 let mut fields = IndexMap::new();
800
801 for (field_name, field_type_value) in field_map {
802 let type_string = match field_type_value {
803 serde_json::Value::String(s) => s,
804 _ => {
805 return Err(ExtensionTypeError::InvalidFieldType(
806 "Struct field types must be strings".to_string(),
807 ));
808 }
809 };
810
811 let parsed_field_type = TypeExpr::parse(&type_string)?;
812 let mut link = |name: &str| ctx.linked(name);
813 parsed_field_type.visit_references(&mut link);
814 let field_type = expect_concrete_type(parsed_field_type)?;
815
816 if fields.insert(field_name.clone(), field_type).is_some() {
817 return Err(ExtensionTypeError::DuplicateFieldName { field_name });
818 }
819 }
820
821 Ok(Type {
822 kind: TypeKind::NamedStruct { fields },
823 nullable: false,
824 })
825 }
826 }
827 }
828}
829
830#[derive(Debug, Error, PartialEq)]
832#[error("invalid type name `{0}`")]
833pub struct InvalidTypeName(String);
834
835#[derive(Clone, Debug, PartialEq)]
837pub struct Type {
838 pub kind: TypeKind,
840 pub nullable: bool,
842}
843
844impl Type {
845 pub fn new(kind: TypeKind, nullable: bool) -> Type {
847 Type { kind, nullable }
848 }
849
850 pub fn extension(name: String, nullable: bool) -> Type {
852 Type {
853 kind: TypeKind::Extension {
854 name,
855 parameters: Vec::new(),
856 },
857 nullable,
858 }
859 }
860
861 pub fn extension_with_params(
863 name: String,
864 parameters: Vec<TypeParameter>,
865 nullable: bool,
866 ) -> Type {
867 Type {
868 kind: TypeKind::Extension { name, parameters },
869 nullable,
870 }
871 }
872
873 pub fn list(element_type: Type, nullable: bool) -> Type {
875 Type {
876 kind: TypeKind::List(Box::new(element_type)),
877 nullable,
878 }
879 }
880
881 pub fn r#struct(field_types: Vec<Type>, nullable: bool) -> Type {
883 Type {
884 kind: TypeKind::Struct(field_types),
885 nullable,
886 }
887 }
888
889 pub fn map(key_type: Type, value_type: Type, nullable: bool) -> Type {
891 Type {
892 kind: TypeKind::Map {
893 key: Box::new(key_type),
894 value: Box::new(value_type),
895 },
896 nullable,
897 }
898 }
899
900 pub fn named_struct(fields: IndexMap<String, Type>, nullable: bool) -> Type {
902 Type {
903 kind: TypeKind::NamedStruct { fields },
904 nullable,
905 }
906 }
907
908 pub fn is_concrete(&self) -> bool {
910 match &self.kind {
911 TypeKind::Any | TypeKind::Variable { .. } => false,
912 TypeKind::FixedChar { length }
913 | TypeKind::VarChar { length }
914 | TypeKind::FixedBinary { length } => length.is_concrete(),
915 TypeKind::Decimal { precision, scale } => {
916 precision.is_concrete() && scale.is_concrete()
917 }
918 TypeKind::PrecisionTime { precision }
919 | TypeKind::PrecisionTimestamp { precision }
920 | TypeKind::PrecisionTimestampTz { precision }
921 | TypeKind::IntervalDay { precision }
922 | TypeKind::IntervalCompound { precision } => precision.is_concrete(),
923 TypeKind::List(element) => element.is_concrete(),
924 TypeKind::Map { key, value } => key.is_concrete() && value.is_concrete(),
925 TypeKind::Struct(fields) => fields.iter().all(Type::is_concrete),
926 TypeKind::NamedStruct { fields } => fields.values().all(Type::is_concrete),
927 TypeKind::Func {
928 parameters,
929 return_type,
930 } => parameters.iter().all(Type::is_concrete) && return_type.is_concrete(),
931 TypeKind::Extension { parameters, .. } => {
932 parameters.iter().all(TypeParameter::is_concrete)
933 }
934 _ => true,
935 }
936 }
937
938 pub fn is_compatible_with(&self, other: &Type) -> bool {
948 self.kind == other.kind && (self.nullable || !other.nullable)
950 }
951}
952
953impl fmt::Display for Type {
955 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956 self.kind.fmt_with_nullability(f, self.nullable)
957 }
958}
959
960impl From<Type> for RawType {
961 fn from(val: Type) -> Self {
962 match val.kind {
963 TypeKind::NamedStruct { fields } => RawType::Object(
964 fields
965 .into_iter()
966 .map(|(name, ty)| (name, serde_json::Value::String(ty.to_string())))
967 .collect(),
968 ),
969 _ => RawType::String(val.to_string()),
970 }
971 }
972}
973
974fn parse_integer_parameter(
983 type_name: &str,
984 index: usize,
985 param: &TypeExprParam<'_>,
986 range: Option<RangeInclusive<i32>>,
987) -> Result<IntegerParameter, ExtensionTypeError> {
988 let value = match param {
989 TypeExprParam::Type(TypeExpr::Simple(name, parameters, false))
990 if parameters.is_empty() && CustomType::validate_name(name).is_ok() =>
991 {
992 return Ok(IntegerParameter::Variable(name.to_string()));
993 }
994 TypeExprParam::Integer(value) => {
995 i32::try_from(*value).map_err(|_| ExtensionTypeError::InvalidParameterValue {
996 type_name: type_name.to_string(),
997 index,
998 value: *value,
999 expected: "an i32",
1000 })
1001 }
1002 _ => Err(ExtensionTypeError::InvalidParameterKind {
1003 type_name: type_name.to_string(),
1004 index,
1005 expected: "an integer",
1006 }),
1007 }?;
1008
1009 if let Some(range) = range {
1010 if range.contains(&value) {
1011 return Ok(IntegerParameter::Literal(i64::from(value)));
1012 }
1013 return Err(ExtensionTypeError::InvalidParameterRange {
1014 type_name: type_name.to_string(),
1015 index,
1016 value: i64::from(value),
1017 expected: range,
1018 });
1019 }
1020
1021 Ok(IntegerParameter::Literal(i64::from(value)))
1022}
1023
1024fn expect_param_len(
1027 type_name: &str,
1028 params: &[TypeExprParam<'_>],
1029 expected: usize,
1030) -> Result<(), ExtensionTypeError> {
1031 if params.len() != expected {
1032 return Err(ExtensionTypeError::InvalidParameterCount {
1033 type_name: type_name.to_string(),
1034 expected,
1035 actual: params.len(),
1036 });
1037 }
1038 Ok(())
1039}
1040
1041fn expect_concrete_type(expression: TypeExpr<'_>) -> Result<Type, ExtensionTypeError> {
1044 match expression {
1045 TypeExpr::TypeVariable(id, nullability) => {
1046 Err(ExtensionTypeError::InvalidAnyTypeVariable { id, nullability })
1047 }
1048 expression => {
1049 let parsed = Type::try_from(expression)?;
1050 if parsed.is_concrete() {
1051 Ok(parsed)
1052 } else {
1053 Err(ExtensionTypeError::ExpectedConcreteType {
1054 type_string: parsed.to_string(),
1055 })
1056 }
1057 }
1058 }
1059}
1060
1061fn expect_type_argument<'a>(
1064 type_name: &str,
1065 index: usize,
1066 param: TypeExprParam<'a>,
1067) -> Result<Type, ExtensionTypeError> {
1068 match param {
1069 TypeExprParam::Type(t) => Type::try_from(t),
1070 TypeExprParam::Integer(_) => Err(ExtensionTypeError::InvalidParameterKind {
1071 type_name: type_name.to_string(),
1072 index,
1073 expected: "a type",
1074 }),
1075 }
1076}
1077
1078impl<'a> TryFrom<TypeExprParam<'a>> for TypeParameter {
1079 type Error = ExtensionTypeError;
1080
1081 fn try_from(param: TypeExprParam<'a>) -> Result<Self, Self::Error> {
1082 Ok(match param {
1083 TypeExprParam::Integer(v) => TypeParameter::Integer(IntegerParameter::Literal(v)),
1084 TypeExprParam::Type(expression) => {
1085 let is_bare_name = matches!(
1086 &expression,
1087 TypeExpr::Simple(_, parameters, false) if parameters.is_empty()
1088 );
1089 match Type::try_from(expression) {
1090 Err(ExtensionTypeError::UnknownTypeName { name }) if is_bare_name => {
1091 CustomType::validate_name(&name)?;
1092 TypeParameter::Name(name)
1093 }
1094 result => TypeParameter::Type(result?),
1095 }
1096 }
1097 })
1098 }
1099}
1100
1101fn max_literal_scale(precision: &IntegerParameter) -> i32 {
1104 match precision {
1105 IntegerParameter::Literal(precision) => *precision as i32,
1106 IntegerParameter::Variable(_) => 38,
1108 }
1109}
1110
1111fn parse_builtin<'a>(
1115 display_name: &str,
1116 lower_name: &str,
1117 params: &[TypeExprParam<'a>],
1118) -> Result<Option<TypeKind>, ExtensionTypeError> {
1119 if let Some(builtin) = primitive_builtin(lower_name) {
1120 expect_param_len(display_name, params, 0)?;
1121 return Ok(Some(builtin));
1122 }
1123
1124 match lower_name {
1125 "fixedchar" => {
1127 expect_param_len(display_name, params, 1)?;
1128 let length = parse_integer_parameter(display_name, 0, ¶ms[0], None)?;
1129 Ok(Some(TypeKind::FixedChar { length }))
1130 }
1131 "varchar" => {
1132 expect_param_len(display_name, params, 1)?;
1133 let length = parse_integer_parameter(display_name, 0, ¶ms[0], None)?;
1134 Ok(Some(TypeKind::VarChar { length }))
1135 }
1136 "fixedbinary" => {
1137 expect_param_len(display_name, params, 1)?;
1138 let length = parse_integer_parameter(display_name, 0, ¶ms[0], None)?;
1139 Ok(Some(TypeKind::FixedBinary { length }))
1140 }
1141 "decimal" => {
1142 expect_param_len(display_name, params, 2)?;
1143 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], Some(1..=38))?;
1144 let scale = parse_integer_parameter(
1145 display_name,
1146 1,
1147 ¶ms[1],
1148 Some(0..=max_literal_scale(&precision)),
1149 )?;
1150 Ok(Some(TypeKind::Decimal { precision, scale }))
1151 }
1152 "precision_time" => {
1153 expect_param_len(display_name, params, 1)?;
1154 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], Some(0..=12))?;
1155 Ok(Some(TypeKind::PrecisionTime { precision }))
1156 }
1157 "precision_timestamp" => {
1158 expect_param_len(display_name, params, 1)?;
1159 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], Some(0..=12))?;
1160 Ok(Some(TypeKind::PrecisionTimestamp { precision }))
1161 }
1162 "precision_timestamp_tz" => {
1163 expect_param_len(display_name, params, 1)?;
1164 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], Some(0..=12))?;
1165 Ok(Some(TypeKind::PrecisionTimestampTz { precision }))
1166 }
1167 "interval_day" => {
1168 expect_param_len(display_name, params, 1)?;
1169 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], Some(0..=9))?;
1170 Ok(Some(TypeKind::IntervalDay { precision }))
1171 }
1172 "interval_compound" => {
1173 expect_param_len(display_name, params, 1)?;
1174 let precision = parse_integer_parameter(display_name, 0, ¶ms[0], None)?;
1175 Ok(Some(TypeKind::IntervalCompound { precision }))
1176 }
1177 _ => Ok(None),
1178 }
1179}
1180
1181impl<'a> TryFrom<TypeExpr<'a>> for Type {
1182 type Error = ExtensionTypeError;
1183
1184 fn try_from(parsed_type: TypeExpr<'a>) -> Result<Self, Self::Error> {
1185 match parsed_type {
1186 TypeExpr::Func(parameters, return_type, nullable) => {
1187 let parameters = parameters
1188 .into_iter()
1189 .enumerate()
1190 .map(|(index, parameter)| expect_type_argument("func", index, parameter))
1191 .collect::<Result<Vec<_>, _>>()?;
1192 let return_type = Box::new(Type::try_from(*return_type)?);
1193 Ok(Type::new(
1194 TypeKind::Func {
1195 parameters,
1196 return_type,
1197 },
1198 nullable,
1199 ))
1200 }
1201 TypeExpr::Simple(name, params, nullable) => {
1202 let lower = name.to_ascii_lowercase();
1203
1204 match lower.as_str() {
1205 "any" => {
1206 expect_param_len(name, ¶ms, 0)?;
1207 return Ok(Type::new(TypeKind::Any, nullable));
1208 }
1209 "list" => {
1210 expect_param_len(name, ¶ms, 1)?;
1211 let element =
1212 expect_type_argument(name, 0, params.into_iter().next().unwrap())?;
1213 return Ok(Type::list(element, nullable));
1214 }
1215 "map" => {
1216 expect_param_len(name, ¶ms, 2)?;
1217 let mut iter = params.into_iter();
1218 let key = expect_type_argument(name, 0, iter.next().unwrap())?;
1219 let value = expect_type_argument(name, 1, iter.next().unwrap())?;
1220 return Ok(Type::map(key, value, nullable));
1221 }
1222 "struct" => {
1223 let field_types = params
1224 .into_iter()
1225 .enumerate()
1226 .map(|(idx, param)| expect_type_argument(name, idx, param))
1227 .collect::<Result<Vec<_>, _>>()?;
1228 return Ok(Type::r#struct(field_types, nullable));
1229 }
1230 _ => {}
1231 }
1232
1233 if let Some(builtin) = parse_builtin(name, lower.as_str(), ¶ms)? {
1234 return Ok(Type::new(builtin, nullable));
1235 }
1236
1237 Err(ExtensionTypeError::UnknownTypeName {
1240 name: name.to_string(),
1241 })
1242 }
1243 TypeExpr::UserDefined(name, params, nullable) => {
1244 let parameters = params
1245 .into_iter()
1246 .map(TypeParameter::try_from)
1247 .collect::<Result<Vec<_>, _>>()?;
1248 Ok(Type::extension_with_params(
1249 name.to_string(),
1250 parameters,
1251 nullable,
1252 ))
1253 }
1254 TypeExpr::TypeVariable(id, nullable) => {
1255 Ok(Type::new(TypeKind::Variable { id }, nullable))
1256 }
1257 }
1258 }
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263 use super::super::extensions::TypeContext;
1264 use super::*;
1265 use crate::parse::text::simple_extensions::TypeExpr;
1266 use crate::parse::text::simple_extensions::argument::EnumOptions as ParsedEnumOptions;
1267 use crate::text::simple_extensions;
1268 use std::iter::FromIterator;
1269
1270 fn required_type(kind: TypeKind) -> Type {
1272 Type::new(kind, false)
1273 }
1274
1275 fn parse_type(expr: &str) -> Type {
1277 let parsed = TypeExpr::parse(expr).unwrap();
1278 Type::try_from(parsed).unwrap()
1279 }
1280
1281 fn parse_type_result(expr: &str) -> Result<Type, ExtensionTypeError> {
1283 let parsed = TypeExpr::parse(expr).unwrap();
1284 Type::try_from(parsed)
1285 }
1286
1287 fn parse_simple(s: &str) -> Type {
1290 let parsed = TypeExpr::parse(s).unwrap();
1291
1292 let mut refs = Vec::new();
1293 parsed.visit_references(&mut |name| refs.push(name.to_string()));
1294 assert!(refs.is_empty(), "{s} should not add an extension reference");
1295
1296 Type::try_from(parsed).unwrap()
1297 }
1298
1299 #[test]
1300 fn test_literal_and_symbolic_lengths_share_a_type_kind() {
1301 for (length, expected, is_concrete) in [
1302 (IntegerParameter::Literal(20), "varchar<20>", true),
1303 (IntegerParameter::Variable("L".into()), "varchar<L>", false),
1304 ] {
1305 let type_ = Type::new(
1306 TypeKind::VarChar {
1307 length: length.clone(),
1308 },
1309 false,
1310 );
1311 assert_eq!(type_.to_string(), expected);
1312 assert_eq!(type_.is_concrete(), is_concrete);
1313
1314 let extension = Type::extension_with_params(
1315 "sized".into(),
1316 vec![TypeParameter::Integer(length)],
1317 false,
1318 );
1319 assert_eq!(extension.is_concrete(), is_concrete);
1320 }
1321 }
1322
1323 #[test]
1324 fn test_concreteness_is_recursive() {
1325 let variable = parse_type("any1?");
1326 assert_eq!(variable.to_string(), "any1?");
1327 for field in [
1328 parse_type("i32"),
1329 variable,
1330 Type::new(
1331 TypeKind::VarChar {
1332 length: IntegerParameter::Variable("L".into()),
1333 },
1334 false,
1335 ),
1336 ] {
1337 let expected = field.is_concrete();
1338 for nested in [
1339 Type::list(field.clone(), false),
1340 Type::map(parse_type("string"), field.clone(), false),
1341 Type::r#struct(vec![field.clone()], false),
1342 Type::named_struct(IndexMap::from([("field".into(), field.clone())]), false),
1343 Type::extension_with_params(
1344 "wrapper".into(),
1345 vec![TypeParameter::Type(field)],
1346 false,
1347 ),
1348 ] {
1349 assert_eq!(
1350 nested.is_concrete(),
1351 expected,
1352 "unexpected concreteness for {nested}"
1353 );
1354 }
1355 }
1356 }
1357
1358 #[test]
1359 fn test_concreteness_does_not_replace_value_validation() {
1360 let type_ = Type::new(
1361 TypeKind::Decimal {
1362 precision: IntegerParameter::Literal(10),
1363 scale: IntegerParameter::Literal(20),
1364 },
1365 false,
1366 );
1367 assert!(type_.is_concrete());
1368 assert!(matches!(
1369 parse_type_result(&type_.to_string()),
1370 Err(ExtensionTypeError::InvalidParameterRange { .. })
1371 ));
1372 }
1373
1374 #[test]
1375 fn test_structure_parsing_still_requires_concrete_types() {
1376 for raw in [
1377 RawType::String("any1?".into()),
1378 RawType::Object(IndexMap::from([(
1379 "field".into(),
1380 Value::String("any1?".into()),
1381 )])),
1382 ] {
1383 assert!(matches!(
1384 raw.parse(&mut TypeContext::default()),
1385 Err(ExtensionTypeError::InvalidAnyTypeVariable {
1386 id: 1,
1387 nullability: true
1388 })
1389 ));
1390 }
1391 }
1392
1393 #[test]
1394 fn test_named_numeric_parameters_remain_unsupported_in_type_structures() {
1395 for raw in [
1396 RawType::String("varchar<L>".into()),
1397 RawType::Object(IndexMap::from([(
1398 "field".into(),
1399 Value::String("decimal<P, S>".into()),
1400 )])),
1401 ] {
1402 assert!(matches!(
1403 raw.parse(&mut TypeContext::default()),
1404 Err(ExtensionTypeError::ExpectedConcreteType { .. })
1405 ));
1406 }
1407 }
1408
1409 #[test]
1410 fn test_extension_integer_parameters_preserve_i64_range() {
1411 let input = format!("u!wide<{}>", i64::MAX);
1412 let parsed = parse_type(&input);
1413 assert!(parsed.is_concrete());
1414 assert_eq!(parsed.to_string(), input);
1415 assert!(matches!(
1416 parse_type_result(&format!("varchar<{}>", i64::MAX)),
1417 Err(ExtensionTypeError::InvalidParameterValue { .. })
1418 ));
1419 }
1420
1421 fn type_param(expr: &str) -> TypeParameter {
1423 TypeParameter::Type(parse_type(expr))
1424 }
1425
1426 fn extension(name: &str, parameters: Vec<TypeParameter>, nullable: bool) -> Type {
1428 Type::extension_with_params(name.to_string(), parameters, nullable)
1429 }
1430
1431 fn round_trip(custom: &CustomType) {
1433 let item: simple_extensions::SimpleExtensionsTypesItem = custom.clone().into();
1434 let mut ctx = TypeContext::default();
1435 let parsed = Parse::parse(item, &mut ctx).unwrap();
1436 assert_eq!(&parsed, custom);
1437 }
1438
1439 fn raw_named_struct(fields: &[(&str, &str)]) -> RawType {
1442 RawType::Object(
1443 fields
1444 .iter()
1445 .map(|(name, ty)| ((*name).into(), serde_json::Value::String((*ty).into())))
1446 .collect(),
1447 )
1448 }
1449
1450 #[test]
1451 fn test_builtin_scalar_parsing() {
1452 let cases = vec![
1453 ("bool", Some(TypeKind::Boolean)),
1454 ("i8", Some(TypeKind::I8)),
1455 ("i16", Some(TypeKind::I16)),
1456 ("i32", Some(TypeKind::I32)),
1457 ("i64", Some(TypeKind::I64)),
1458 ("fp32", Some(TypeKind::Fp32)),
1459 ("fp64", Some(TypeKind::Fp64)),
1460 ("STRING", Some(TypeKind::String)),
1461 ("binary", Some(TypeKind::Binary)),
1462 ("uuid", Some(TypeKind::Uuid)),
1463 ("date", Some(TypeKind::Date)),
1464 ("interval_year", Some(TypeKind::IntervalYear)),
1465 ("invalid", None),
1466 ];
1467
1468 for (input, expected) in cases {
1469 let result = parse_builtin(input, input.to_ascii_lowercase().as_str(), &[]).unwrap();
1470 match expected {
1471 Some(expected_type) => {
1472 assert_eq!(
1473 result,
1474 Some(expected_type),
1475 "expected builtin type for {input}"
1476 );
1477 }
1478 None => {
1479 assert!(result.is_none(), "expected parsing {input} to fail");
1480 }
1481 }
1482 }
1483 }
1484
1485 #[test]
1486 fn test_nested_symbolic_types() {
1487 assert_eq!(
1488 parse_type("List<any>"),
1489 Type::list(required_type(TypeKind::Any), false)
1490 );
1491 assert_eq!(
1492 parse_type("Map<string, any?>"),
1493 Type::map(parse_type("string"), Type::new(TypeKind::Any, true), false)
1494 );
1495 assert_eq!(
1496 parse_type("List?<DECIMAL<P, S>>"),
1497 Type::list(parse_type("decimal<P, S>"), true)
1498 );
1499 assert_eq!(
1500 parse_type("Map<string, precision_timestamp<P>>"),
1501 Type::map(
1502 parse_type("string"),
1503 parse_type("precision_timestamp<P>"),
1504 false
1505 )
1506 );
1507 assert_eq!(
1508 parse_type("Struct?<any1, List<Map<string, VARCHAR<L>>>>"),
1509 Type::r#struct(
1510 vec![
1511 parse_type("any1"),
1512 parse_type("List<Map<string, VARCHAR<L>>>")
1513 ],
1514 true
1515 )
1516 );
1517 assert_eq!(parse_type("any").to_string(), "any");
1518 assert_eq!(parse_type("Any?").to_string(), "any?");
1519 }
1520
1521 #[test]
1522 fn test_nested_type_concreteness_follows_elements() {
1523 assert!(parse_type("list<decimal<10, 2>>").is_concrete());
1524 assert!(!parse_type("list<any>").is_concrete());
1525 }
1526
1527 #[test]
1528 fn test_function_types() {
1529 for (input, expected) in [
1530 ("func<i32 -> i64>", "func<i32 -> i64>"),
1531 ("FUNC?<any1 -> any2>", "func?<any1 -> any2>"),
1532 (
1533 "func<(i32, list<any1>) -> i64?>",
1534 "func<(i32, list<any1>) -> i64?>",
1535 ),
1536 (
1537 "func<i32 -> func<i64 -> boolean?>>",
1538 "func<i32 -> func<i64 -> bool?>>",
1539 ),
1540 ] {
1541 let ty = parse_type(input);
1542 assert_eq!(ty.to_string(), expected, "unexpected spelling for {input}");
1543 assert_eq!(
1544 parse_type(&ty.to_string()),
1545 ty,
1546 "round trip failed for {input}"
1547 );
1548 }
1549
1550 assert!(parse_type("func<i32 -> i64>").is_concrete());
1551 assert!(!parse_type("func<any1 -> i64>").is_concrete());
1552 assert!(!parse_type("func<i32 -> any1>").is_concrete());
1553 }
1554
1555 #[test]
1556 fn test_function_parameter_can_be_a_function() {
1557 assert_eq!(
1558 parse_type("func<func<i32 -> i64> -> bool>"),
1559 required_type(TypeKind::Func {
1560 parameters: vec![parse_type("func<i32 -> i64>")],
1561 return_type: Box::new(parse_type("bool")),
1562 }),
1563 );
1564 }
1565
1566 #[test]
1567 fn test_list_elements_can_be_functions() {
1568 assert_eq!(
1569 parse_type("list<func<i32 -> i64>>"),
1570 Type::list(parse_type("func<i32 -> i64>"), false),
1571 );
1572 }
1573
1574 #[test]
1575 fn test_function_type_validation() {
1576 for input in ["func<5 -> i64>", "func<i32 -> missing>"] {
1577 assert!(
1578 parse_type_result(input).is_err(),
1579 "{input} should not be accepted as a signature type"
1580 );
1581 }
1582 }
1583
1584 #[test]
1585 fn test_symbolic_user_defined_type_parameters() {
1586 let ty = parse_type("u!Box?<List<decimal<P, S>>, 5, N>");
1587 assert_eq!(
1588 ty,
1589 Type::extension_with_params(
1590 "Box".to_string(),
1591 vec![
1592 TypeParameter::Type(parse_type("List<decimal<P, S>>")),
1593 TypeParameter::Integer(IntegerParameter::Literal(5)),
1594 TypeParameter::Name("N".to_string()),
1595 ],
1596 true,
1597 )
1598 );
1599 assert!(!ty.is_concrete());
1600 assert!(!parse_type("u!Box<5, N>").is_concrete());
1601 assert_eq!(ty.to_string(), "u!Box?<list<decimal<P, S>>, 5, N>");
1602 }
1603
1604 #[test]
1605 fn test_invalid_nested_signature_types() {
1606 for input in [
1607 "List<any1, any2>",
1608 "Map<any1>",
1609 "Struct<1, any1>",
1610 "List<missing_type?>",
1611 "List<decimal<any1>>",
1612 "List<i32<P>>",
1613 ] {
1614 assert!(
1615 Type::try_from(TypeExpr::parse(input).unwrap()).is_err(),
1616 "{input} should not be accepted as a signature type"
1617 );
1618 }
1619 }
1620
1621 #[test]
1622 fn test_named_integer_parameters() {
1623 let parameter = IntegerParameter::Variable("N".to_string());
1624 for (name, expected) in [
1625 (
1626 "fixedchar",
1627 TypeKind::FixedChar {
1628 length: parameter.clone(),
1629 },
1630 ),
1631 (
1632 "varchar",
1633 TypeKind::VarChar {
1634 length: parameter.clone(),
1635 },
1636 ),
1637 (
1638 "fixedbinary",
1639 TypeKind::FixedBinary {
1640 length: parameter.clone(),
1641 },
1642 ),
1643 (
1644 "precision_time",
1645 TypeKind::PrecisionTime {
1646 precision: parameter.clone(),
1647 },
1648 ),
1649 (
1650 "precision_timestamp",
1651 TypeKind::PrecisionTimestamp {
1652 precision: parameter.clone(),
1653 },
1654 ),
1655 (
1656 "precision_timestamp_tz",
1657 TypeKind::PrecisionTimestampTz {
1658 precision: parameter.clone(),
1659 },
1660 ),
1661 (
1662 "interval_day",
1663 TypeKind::IntervalDay {
1664 precision: parameter.clone(),
1665 },
1666 ),
1667 (
1668 "interval_compound",
1669 TypeKind::IntervalCompound {
1670 precision: parameter,
1671 },
1672 ),
1673 ] {
1674 let input = format!("{}?<N>", name.to_ascii_uppercase());
1675 let expression = Type::try_from(TypeExpr::parse(&input).unwrap()).unwrap();
1676 assert_eq!(
1677 expression,
1678 Type::new(expected, true),
1679 "unexpected type for {input}"
1680 );
1681 }
1682
1683 for (input, precision, scale) in [
1684 (
1685 "decimal<P, S>",
1686 IntegerParameter::Variable("P".into()),
1687 IntegerParameter::Variable("S".into()),
1688 ),
1689 (
1690 "decimal<10, S>",
1691 IntegerParameter::Literal(10),
1692 IntegerParameter::Variable("S".into()),
1693 ),
1694 (
1695 "decimal<P, 2>",
1696 IntegerParameter::Variable("P".into()),
1697 IntegerParameter::Literal(2),
1698 ),
1699 ] {
1700 assert_eq!(
1701 Type::try_from(TypeExpr::parse(input).unwrap()).unwrap(),
1702 Type::new(TypeKind::Decimal { precision, scale }, false)
1703 );
1704 }
1705 }
1706
1707 #[test]
1708 fn test_named_numeric_parameters_preserve_builtin_validation() {
1709 for input in [
1710 "i32<P>",
1711 "varchar<L, 2>",
1712 "decimal<any1, S>",
1713 "decimal<P?, S>",
1714 "decimal<P<1>, S>",
1715 "decimal<u!P, S>",
1716 "decimal<39, S>",
1717 "decimal<P, 39>",
1718 "precisiontime<P>",
1719 "missing<P>",
1720 ] {
1721 assert!(
1722 Type::try_from(TypeExpr::parse(input).unwrap()).is_err(),
1723 "{input} should not be accepted as a signature type"
1724 );
1725 }
1726 }
1727
1728 #[test]
1729 fn test_parameterized_builtin_types() {
1730 let cases = vec![
1731 (
1732 "precision_time<2>",
1733 required_type(TypeKind::PrecisionTime {
1734 precision: IntegerParameter::Literal(2),
1735 }),
1736 ),
1737 (
1738 "precision_timestamp<1>",
1739 required_type(TypeKind::PrecisionTimestamp {
1740 precision: IntegerParameter::Literal(1),
1741 }),
1742 ),
1743 (
1744 "precision_timestamp_tz<5>",
1745 required_type(TypeKind::PrecisionTimestampTz {
1746 precision: IntegerParameter::Literal(5),
1747 }),
1748 ),
1749 (
1750 "DECIMAL<10,2>",
1751 required_type(TypeKind::Decimal {
1752 precision: IntegerParameter::Literal(10),
1753 scale: IntegerParameter::Literal(2),
1754 }),
1755 ),
1756 (
1757 "fixedchar<12>",
1758 required_type(TypeKind::FixedChar {
1759 length: IntegerParameter::Literal(12),
1760 }),
1761 ),
1762 (
1763 "VarChar<42>",
1764 required_type(TypeKind::VarChar {
1765 length: IntegerParameter::Literal(42),
1766 }),
1767 ),
1768 (
1769 "fixedbinary<8>",
1770 required_type(TypeKind::FixedBinary {
1771 length: IntegerParameter::Literal(8),
1772 }),
1773 ),
1774 (
1775 "interval_day<7>",
1776 required_type(TypeKind::IntervalDay {
1777 precision: IntegerParameter::Literal(7),
1778 }),
1779 ),
1780 (
1781 "interval_compound<6>",
1782 required_type(TypeKind::IntervalCompound {
1783 precision: IntegerParameter::Literal(6),
1784 }),
1785 ),
1786 ];
1787
1788 for (expr, expected) in cases {
1789 let found = parse_simple(expr);
1790 assert_eq!(found, expected, "unexpected type for {expr}");
1791 }
1792 }
1793
1794 #[test]
1795 fn test_parameterized_builtin_range_errors() {
1796 use ExtensionTypeError::InvalidParameterRange;
1797
1798 let cases = vec![
1799 ("precision_time<13>", "precision_time", 0, 13, 0..=12),
1800 ("precision_time<-1>", "precision_time", 0, -1, 0..=12),
1801 (
1802 "precision_timestamp<13>",
1803 "precision_timestamp",
1804 0,
1805 13,
1806 0..=12,
1807 ),
1808 (
1809 "precision_timestamp<-1>",
1810 "precision_timestamp",
1811 0,
1812 -1,
1813 0..=12,
1814 ),
1815 (
1816 "precision_timestamp_tz<20>",
1817 "precision_timestamp_tz",
1818 0,
1819 20,
1820 0..=12,
1821 ),
1822 ("interval_day<10>", "interval_day", 0, 10, 0..=9),
1823 ("DECIMAL<39,0>", "DECIMAL", 0, 39, 1..=38),
1824 ("DECIMAL<0,0>", "DECIMAL", 0, 0, 1..=38),
1825 ("DECIMAL<10,-1>", "DECIMAL", 1, -1, 0..=10),
1826 ("DECIMAL<10,12>", "DECIMAL", 1, 12, 0..=10),
1827 ];
1828
1829 for (expr, expected_type, expected_index, expected_value, expected_range) in cases {
1830 match parse_type_result(expr) {
1831 Ok(value) => panic!("expected error parsing {expr}, got {value:?}"),
1832 Err(InvalidParameterRange {
1833 type_name,
1834 index,
1835 value,
1836 expected,
1837 }) => {
1838 assert_eq!(type_name, expected_type, "unexpected type for {expr}");
1839 assert_eq!(index, expected_index, "unexpected index for {expr}");
1840 assert_eq!(
1841 value,
1842 i64::from(expected_value),
1843 "unexpected value for {expr}"
1844 );
1845 assert_eq!(expected, expected_range, "unexpected range for {expr}");
1846 }
1847 Err(other) => panic!("expected InvalidParameterRange for {expr}, got {other:?}"),
1848 }
1849 }
1850 }
1851
1852 #[test]
1853 fn test_container_types() {
1854 let cases = vec![
1855 (
1856 "List<i32>",
1857 Type::list(Type::new(TypeKind::I32, false), false),
1858 ),
1859 (
1860 "List<fp64?>",
1861 Type::list(Type::new(TypeKind::Fp64, true), false),
1862 ),
1863 (
1864 "Map?<i64, string?>",
1865 Type::map(
1866 Type::new(TypeKind::I64, false),
1867 Type::new(TypeKind::String, true),
1868 true,
1869 ),
1870 ),
1871 (
1872 "Struct?<i8, string?>",
1873 Type::r#struct(
1874 vec![
1875 Type::new(TypeKind::I8, false),
1876 Type::new(TypeKind::String, true),
1877 ],
1878 true,
1879 ),
1880 ),
1881 ];
1882
1883 for (expr, expected) in cases {
1884 assert_eq!(parse_type(expr), expected, "unexpected parse for {expr}");
1885 }
1886 }
1887
1888 #[test]
1889 fn test_extension_types() {
1890 let cases = vec![
1891 (
1892 "u!geo<List<i32>, 10>",
1893 extension(
1894 "geo",
1895 vec![
1896 type_param("List<i32>"),
1897 TypeParameter::Integer(IntegerParameter::Literal(10)),
1898 ],
1899 false,
1900 ),
1901 ),
1902 (
1903 "u!Geo?<List<i32?>>",
1904 extension("Geo", vec![type_param("List<i32?>")], true),
1905 ),
1906 (
1907 "u!Custom<string?, bool>",
1908 extension(
1909 "Custom",
1910 vec![
1911 type_param("string?"),
1912 TypeParameter::Type(Type::new(TypeKind::Boolean, false)),
1913 ],
1914 false,
1915 ),
1916 ),
1917 ];
1918
1919 for (expr, expected) in cases {
1920 assert_eq!(
1921 parse_type(expr),
1922 expected,
1923 "unexpected extension for {expr}"
1924 );
1925 }
1926 }
1927
1928 #[test]
1929 fn test_parameter_type_validation() {
1930 let int_param = ParameterConstraint::Integer {
1931 min: Some(1),
1932 max: Some(10),
1933 };
1934 let enum_param = ParameterConstraint::Enumeration {
1935 options: ParsedEnumOptions::try_from(simple_extensions::EnumOptions(vec![
1936 "OVERFLOW".to_string(),
1937 "ERROR".to_string(),
1938 ]))
1939 .unwrap(),
1940 };
1941
1942 let cases = vec![
1943 (&int_param, Value::Number(5.into()), true),
1944 (&int_param, Value::Number(0.into()), false),
1945 (&int_param, Value::Number(11.into()), false),
1946 (&int_param, Value::String("not a number".into()), false),
1947 (&enum_param, Value::String("OVERFLOW".into()), true),
1948 (&enum_param, Value::String("INVALID".into()), false),
1949 ];
1950
1951 for (param, value, expected) in cases {
1952 assert_eq!(
1953 param.is_valid_value(&value),
1954 expected,
1955 "unexpected validation result for {value:?}"
1956 );
1957 }
1958 }
1959
1960 #[test]
1961 fn test_builtin_type_display_lowercase() {
1962 for input in [
1963 "I32",
1964 "FIXEDCHAR<5>",
1965 "VARCHAR<10>",
1966 "FIXEDBINARY<8>",
1967 "DeCiMaL<10, 2>",
1968 "PRECISION_TIME<3>",
1969 "PRECISION_TIMESTAMP<3>",
1970 "PRECISION_TIMESTAMP_TZ<3>",
1971 "INTERVAL_DAY<3>",
1972 "INTERVAL_COMPOUND<3>",
1973 ] {
1974 let concrete = parse_type(input);
1975 assert_eq!(parse_type(&input.to_ascii_lowercase()), concrete);
1976
1977 let output = concrete.to_string();
1978 assert_eq!(output, output.to_ascii_lowercase(), "display for {input}");
1979 }
1980 }
1981
1982 #[test]
1983 fn test_precision_types_require_underscores() {
1984 for name in [
1985 "precisiontime",
1986 "precisiontimestamp",
1987 "precisiontimestamptz",
1988 ] {
1989 for spelling in [name.to_string(), name.to_ascii_uppercase()] {
1990 assert!(!TypeKind::is_name(&spelling));
1991 assert_eq!(
1992 parse_type_result(&format!("{spelling}<3>")),
1993 Err(ExtensionTypeError::UnknownTypeName { name: spelling })
1994 );
1995 }
1996 }
1997 }
1998
1999 #[test]
2000 fn test_precision_type_round_trip_display() {
2001 for (input, expected) in [
2002 ("PRECISION_TIME<3>", "precision_time<3>"),
2003 ("PRECISION_TIMESTAMP<3>", "precision_timestamp<3>"),
2004 ("PRECISION_TIMESTAMP_TZ<3>", "precision_timestamp_tz<3>"),
2005 ] {
2006 let concrete = parse_type(input);
2007 let output = concrete.to_string();
2008 assert_eq!(output, expected);
2009 assert_eq!(parse_type(&output), concrete);
2010
2011 let raw: RawType = concrete.clone().into();
2012 let mut ctx = TypeContext::default();
2013 assert_eq!(raw.parse(&mut ctx).unwrap(), concrete);
2014 }
2015 }
2016
2017 #[test]
2018 fn test_type_round_trip_display() {
2019 let cases = vec![
2021 ("i32", "i32"),
2022 ("I64?", "i64?"),
2023 ("list<string>", "list<string>"),
2024 ("List<STRING?>", "list<string?>"),
2025 ("map<i32, list<string>>", "map<i32, list<string>>"),
2026 ("struct<i8, string?>", "struct<i8, string?>"),
2027 (
2028 "Struct<List<i32>, Map<string, list<i64?>>>",
2029 "struct<list<i32>, map<string, list<i64?>>>",
2030 ),
2031 (
2032 "Map<List<I32?>, Struct<string, list<i64?>>>",
2033 "map<list<i32?>, struct<string, list<i64?>>>",
2034 ),
2035 ("List?<i32>", "list?<i32>"),
2036 ("Map?<string, List?<i32?>>", "map?<string, list?<i32?>>"),
2037 ("Struct?<i32, string?>", "struct?<i32, string?>"),
2038 ("struct<>", "struct<>"),
2039 ("FIXEDCHAR?<5>", "fixedchar?<5>"),
2040 ("VARCHAR?<10>", "varchar?<10>"),
2041 ("FIXEDBINARY?<8>", "fixedbinary?<8>"),
2042 ("DeCiMaL?<10,2>", "decimal?<10, 2>"),
2043 ("precision_time?<3>", "precision_time?<3>"),
2044 ("precision_timestamp?<3>", "precision_timestamp?<3>"),
2045 ("precision_timestamp_tz?<3>", "precision_timestamp_tz?<3>"),
2046 ("InTeRvAl_DaY?<3>", "interval_day?<3>"),
2047 ("INTERVAL_COMPOUND?<3>", "interval_compound?<3>"),
2048 ("u!geometry?", "u!geometry?"),
2049 ("u!CustomType?<i32>", "u!CustomType?<i32>"),
2050 ("u!custom<i32>", "u!custom<i32>"),
2051 ("u!custom?<i32, 5>", "u!custom?<i32, 5>"),
2052 ("list?<u!geometry?>", "list?<u!geometry?>"),
2053 ];
2054
2055 for (input, expected) in cases {
2056 let parsed = TypeExpr::parse(input).unwrap();
2057 let concrete = Type::try_from(parsed).unwrap();
2058 let actual = concrete.to_string();
2059
2060 assert_eq!(actual, expected, "unexpected display for {input}");
2061 assert_eq!(
2062 parse_type(&actual),
2063 concrete,
2064 "round trip failed for {input}"
2065 );
2066
2067 let raw: RawType = concrete.clone().into();
2068 let mut ctx = TypeContext::default();
2069 assert_eq!(
2070 raw.parse(&mut ctx).unwrap(),
2071 concrete,
2072 "raw type round trip failed for {input}"
2073 );
2074 }
2075 }
2076
2077 #[test]
2080 fn test_named_struct_field_order_stability() -> Result<(), ExtensionTypeError> {
2081 let raw = RawType::Object(
2082 [("beta", "i32"), ("alpha", "string?")]
2083 .into_iter()
2084 .map(|(name, ty)| (name.to_string(), Value::String(ty.to_string())))
2085 .collect(),
2086 );
2087 let mut ctx = TypeContext::default();
2088 let concrete = Parse::parse(raw, &mut ctx)?;
2089
2090 let round_tripped: RawType = concrete.into();
2091 match round_tripped {
2092 RawType::Object(result_map) => {
2093 let keys: Vec<_> = result_map.keys().collect();
2094 assert_eq!(
2095 keys,
2096 vec!["beta", "alpha"],
2097 "field order should be preserved"
2098 );
2099 }
2100 other => panic!("expected Object, got {other:?}"),
2101 }
2102
2103 Ok(())
2104 }
2105
2106 #[test]
2107 fn test_integer_param_bounds_round_trip() {
2108 let cases = vec![
2109 (
2110 "bounded",
2111 simple_extensions::TypeParamDefsItem {
2112 name: Some("K".to_string()),
2113 description: None,
2114 type_: simple_extensions::TypeParamDefsItemType::Integer,
2115 min: Some(1.0),
2116 max: Some(10.0),
2117 options: None,
2118 optional: None,
2119 },
2120 Ok((Some(1), Some(10))),
2121 ),
2122 (
2123 "fractional_min",
2124 simple_extensions::TypeParamDefsItem {
2125 name: Some("K".to_string()),
2126 description: None,
2127 type_: simple_extensions::TypeParamDefsItemType::Integer,
2128 min: Some(1.5),
2129 max: None,
2130 options: None,
2131 optional: None,
2132 },
2133 Err(TypeParamError::InvalidIntegerBounds {
2134 min: Some(1.5),
2135 max: None,
2136 }),
2137 ),
2138 (
2139 "fractional_max",
2140 simple_extensions::TypeParamDefsItem {
2141 name: Some("K".to_string()),
2142 description: None,
2143 type_: simple_extensions::TypeParamDefsItemType::Integer,
2144 min: None,
2145 max: Some(9.9),
2146 options: None,
2147 optional: None,
2148 },
2149 Err(TypeParamError::InvalidIntegerBounds {
2150 min: None,
2151 max: Some(9.9),
2152 }),
2153 ),
2154 ];
2155
2156 for (label, item, expected) in cases {
2157 match (TypeParam::try_from(item), expected) {
2158 (Ok(tp), Ok((expected_min, expected_max))) => match tp.param_type {
2159 ParameterConstraint::Integer { min, max } => {
2160 assert_eq!(min, expected_min, "min mismatch for {label}");
2161 assert_eq!(max, expected_max, "max mismatch for {label}");
2162 }
2163 _ => panic!("expected integer param type for {label}"),
2164 },
2165 (Err(actual_err), Err(expected_err)) => {
2166 assert_eq!(actual_err, expected_err, "unexpected error for {label}");
2167 }
2168 (result, expected) => {
2169 panic!("unexpected result for {label}: got {result:?}, expected {expected:?}")
2170 }
2171 }
2172 }
2173 }
2174
2175 #[test]
2176 fn test_custom_type_round_trip() -> Result<(), ExtensionTypeError> {
2177 let fields = IndexMap::from_iter([
2178 ("x".to_string(), Type::new(TypeKind::Fp64, false)),
2179 ("y".to_string(), Type::new(TypeKind::Fp64, false)),
2180 ]);
2181
2182 let cases = vec![
2183 CustomType::new(
2184 "AliasType".to_string(),
2185 vec![],
2186 Some(Type::new(TypeKind::I32, false)),
2187 None,
2188 Some("a test alias type".to_string()),
2189 )?,
2190 CustomType::new(
2191 "Point".to_string(),
2192 vec![TypeParam::new(
2193 "T".to_string(),
2194 ParameterConstraint::DataType,
2195 Some("a numeric value".to_string()),
2196 )],
2197 Some(Type::named_struct(fields, false)),
2198 None,
2199 None,
2200 )?,
2201 ];
2202
2203 for custom in cases {
2204 round_trip(&custom);
2205 }
2206
2207 Ok(())
2208 }
2209
2210 #[test]
2211 fn test_invalid_type_names() {
2212 let cases = vec![
2213 ("", false),
2214 ("bad name", false),
2215 ("9bad", false),
2216 ("bad-name", false),
2217 ("bad.name", false),
2218 ("GoodName", true),
2219 ("also_good", true),
2220 ("_underscore", true),
2221 ("$dollar", true),
2222 ("CamelCase123", true),
2223 ];
2224
2225 for (name, expected_ok) in cases {
2226 let result = CustomType::validate_name(name);
2227 assert_eq!(
2228 result.is_ok(),
2229 expected_ok,
2230 "unexpected validation for {name}"
2231 );
2232 }
2233 }
2234
2235 #[test]
2236 fn test_ext_type_to_concrete_type() -> Result<(), ExtensionTypeError> {
2237 let cases = vec![
2238 (
2239 "alias",
2240 RawType::String("i32".to_string()),
2241 Type::new(TypeKind::I32, false),
2242 ),
2243 (
2244 "named_struct",
2245 raw_named_struct(&[("field1", "fp64"), ("field2", "i32?")]),
2246 Type::named_struct(
2247 IndexMap::from_iter([
2248 ("field1".to_string(), Type::new(TypeKind::Fp64, false)),
2249 ("field2".to_string(), Type::new(TypeKind::I32, true)),
2250 ]),
2251 false,
2252 ),
2253 ),
2254 ];
2255
2256 for (label, raw, expected) in cases {
2257 let mut ctx = TypeContext::default();
2258 let parsed = Parse::parse(raw, &mut ctx)?;
2259 assert_eq!(parsed, expected, "unexpected type for {label}");
2260 }
2261
2262 Ok(())
2263 }
2264
2265 #[test]
2266 fn test_custom_type_parsing() -> Result<(), ExtensionTypeError> {
2267 let cases = vec![
2268 (
2269 "alias",
2270 simple_extensions::SimpleExtensionsTypesItem {
2271 deprecated: None,
2272 name: "Alias".to_string(),
2273 description: Some("Alias type".to_string()),
2274 metadata: Default::default(),
2275 parameters: None,
2276 structure: Some(RawType::String("BINARY".to_string())),
2277 variadic: None,
2278 },
2279 "Alias",
2280 Some("Alias type"),
2281 Some(Type::new(TypeKind::Binary, false)),
2282 ),
2283 (
2284 "named_struct",
2285 simple_extensions::SimpleExtensionsTypesItem {
2286 deprecated: None,
2287 name: "Point".to_string(),
2288 description: Some("A 2D point".to_string()),
2289 metadata: Default::default(),
2290 parameters: None,
2291 structure: Some(raw_named_struct(&[("x", "fp64"), ("y", "fp64?")])),
2292 variadic: None,
2293 },
2294 "Point",
2295 Some("A 2D point"),
2296 Some(Type::named_struct(
2297 IndexMap::from_iter([
2298 ("x".to_string(), Type::new(TypeKind::Fp64, false)),
2299 ("y".to_string(), Type::new(TypeKind::Fp64, true)),
2300 ]),
2301 false,
2302 )),
2303 ),
2304 (
2305 "no_structure",
2306 simple_extensions::SimpleExtensionsTypesItem {
2307 deprecated: None,
2308 name: "Opaque".to_string(),
2309 description: None,
2310 metadata: Default::default(),
2311 parameters: None,
2312 structure: None,
2313 variadic: Some(true),
2314 },
2315 "Opaque",
2316 None,
2317 None,
2318 ),
2319 ];
2320
2321 for (label, item, expected_name, expected_description, expected_structure) in cases {
2322 let mut ctx = TypeContext::default();
2323 let parsed = Parse::parse(item, &mut ctx)?;
2324 assert_eq!(parsed.name, expected_name);
2325 assert_eq!(
2326 parsed.description.as_deref(),
2327 expected_description,
2328 "description mismatch for {label}"
2329 );
2330 assert_eq!(
2331 parsed.structure, expected_structure,
2332 "structure mismatch for {label}"
2333 );
2334 }
2335
2336 Ok(())
2337 }
2338}