1use std::fmt;
2use std::str::FromStr;
3
4use substrait::proto::{Expression, Type};
5use thiserror::Error;
6
7use super::{
8 ErrorKind, ExpressionParser, MessageParseError, ParsePair, Rule, RuleIter, ScopedParsePair,
9 unescape_string, unwrap_single_pair,
10};
11use crate::extensions::simple::{self, ExtensionKind};
12use crate::extensions::{
13 AddendumKind, ExtensionArgs, ExtensionColumn, ExtensionValue, InsertError, SimpleExtensions,
14 TupleValue,
15};
16use crate::parser::structural::IndentedLine;
17
18#[derive(Debug, Clone, Error)]
19pub enum ExtensionParseError {
20 #[error("Unexpected line, expected {0}")]
21 UnexpectedLine(ExpectedExtensionLine),
22 #[error("Error adding extension: {0}")]
23 ExtensionError(#[from] InsertError),
24 #[error("Error parsing message: {0}")]
25 Message(#[from] super::MessageParseError),
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum ExpectedExtensionLine {
34 Extensions,
37 ExtensionUrns,
40 ExtensionDeclarations(ExtensionKind),
43}
44
45impl fmt::Display for ExpectedExtensionLine {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 ExpectedExtensionLine::Extensions => write!(f, "Subsection Header, e.g. 'URNs:'"),
49 ExpectedExtensionLine::ExtensionUrns => write!(f, "Extension URNs"),
50 ExpectedExtensionLine::ExtensionDeclarations(kind) => {
51 write!(f, "Extension Declaration for {kind}")
52 }
53 }
54 }
55}
56
57#[derive(Debug)]
64pub struct ExtensionParser {
65 state: ExpectedExtensionLine,
66 extensions: SimpleExtensions,
67}
68
69impl Default for ExtensionParser {
70 fn default() -> Self {
71 Self {
72 state: ExpectedExtensionLine::Extensions,
73 extensions: SimpleExtensions::new(),
74 }
75 }
76}
77
78impl ExtensionParser {
79 pub fn parse_line(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
80 if line.1.is_empty() {
81 self.state = ExpectedExtensionLine::Extensions;
84 return Ok(());
85 }
86
87 match self.state {
88 ExpectedExtensionLine::Extensions => self.parse_subsection(line),
89 ExpectedExtensionLine::ExtensionUrns => self.parse_extension_urns(line),
90 ExpectedExtensionLine::ExtensionDeclarations(extension_kind) => {
91 self.parse_declarations(line, extension_kind)
92 }
93 }
94 }
95
96 fn parse_subsection(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
97 match line {
98 IndentedLine(0, simple::EXTENSION_URNS_HEADER) => {
99 self.state = ExpectedExtensionLine::ExtensionUrns;
100 Ok(())
101 }
102 IndentedLine(0, simple::EXTENSION_FUNCTIONS_HEADER) => {
103 self.state = ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::Function);
104 Ok(())
105 }
106 IndentedLine(0, simple::EXTENSION_TYPES_HEADER) => {
107 self.state = ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::Type);
108 Ok(())
109 }
110 IndentedLine(0, simple::EXTENSION_TYPE_VARIATIONS_HEADER) => {
111 self.state =
112 ExpectedExtensionLine::ExtensionDeclarations(ExtensionKind::TypeVariation);
113 Ok(())
114 }
115 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
116 }
117 }
118
119 fn parse_extension_urns(&mut self, line: IndentedLine) -> Result<(), ExtensionParseError> {
120 match line {
121 IndentedLine(0, _s) => self.parse_subsection(line), IndentedLine(1, s) => {
123 let urn =
124 URNExtensionDeclaration::from_str(s).map_err(ExtensionParseError::Message)?;
125 self.extensions.add_extension_urn(urn.urn, urn.anchor)?;
126 Ok(())
127 }
128 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
129 }
130 }
131
132 fn parse_declarations(
133 &mut self,
134 line: IndentedLine,
135 extension_kind: ExtensionKind,
136 ) -> Result<(), ExtensionParseError> {
137 match line {
138 IndentedLine(0, _s) => self.parse_subsection(line), IndentedLine(1, s) => {
140 let decl = SimpleExtensionDeclaration::parse_from_kind(s, extension_kind)?;
141 self.extensions.add_extension(
142 extension_kind,
143 decl.urn_anchor,
144 decl.anchor,
145 decl.name,
146 )?;
147 Ok(())
148 }
149 _ => Err(ExtensionParseError::UnexpectedLine(self.state)),
150 }
151 }
152
153 pub fn extensions(&self) -> &SimpleExtensions {
154 &self.extensions
155 }
156
157 #[cfg(test)]
158 pub(crate) fn state(&self) -> ExpectedExtensionLine {
159 self.state
160 }
161}
162
163#[derive(Debug, Clone, PartialEq)]
164pub struct URNExtensionDeclaration {
165 pub anchor: u32,
166 pub urn: String,
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub struct SimpleExtensionDeclaration {
171 pub anchor: u32,
172 pub urn_anchor: u32,
173 pub name: String,
174}
175
176impl ParsePair for URNExtensionDeclaration {
177 fn rule() -> Rule {
178 Rule::extension_urn_declaration
179 }
180
181 fn message() -> &'static str {
182 "URNExtensionDeclaration"
183 }
184
185 fn parse_pair(pair: pest::iterators::Pair<Rule>) -> Self {
186 assert_eq!(pair.as_rule(), Self::rule());
187
188 let mut iter = RuleIter::from(pair.into_inner());
189 let anchor_pair = iter.pop(Rule::urn_anchor);
190 let anchor = unwrap_single_pair(anchor_pair)
191 .as_str()
192 .parse::<u32>()
193 .unwrap();
194 let urn = iter.pop(Rule::urn).as_str().to_string();
195 iter.done();
196
197 URNExtensionDeclaration { anchor, urn }
198 }
199}
200
201impl FromStr for URNExtensionDeclaration {
202 type Err = super::MessageParseError;
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 Self::parse_str(s)
206 }
207}
208
209impl SimpleExtensionDeclaration {
210 fn parse_from_kind(s: &str, kind: ExtensionKind) -> Result<Self, MessageParseError> {
211 let mut pairs = <ExpressionParser as pest::Parser<Rule>>::parse(Rule::simple_extension, s)
212 .map_err(|e| {
213 MessageParseError::new("SimpleExtensionDeclaration", ErrorKind::Syntax, Box::new(e))
214 })?;
215 assert_eq!(pairs.as_str(), s);
216 let pair = pairs.next().unwrap();
217 let mut iter = RuleIter::from(pair.into_inner());
218
219 let anchor = unwrap_single_pair(iter.pop(Rule::anchor))
220 .as_str()
221 .parse::<u32>()
222 .unwrap();
223 let urn_anchor = unwrap_single_pair(iter.pop(Rule::urn_anchor))
224 .as_str()
225 .parse::<u32>()
226 .unwrap();
227 let name_pair = iter.pop(Rule::simple_extension_name);
228 let name_span = name_pair.as_span();
229 let name = name_pair.as_str();
230
231 if kind != ExtensionKind::Type && name.starts_with("u!") {
232 return Err(MessageParseError::invalid(
233 "simple_extension_name",
234 name_span,
235 format!("'u!' prefix is only valid for type declarations, not {kind}"),
236 ));
237 }
238 if matches!(kind, ExtensionKind::Type | ExtensionKind::TypeVariation) && name.contains(':')
239 {
240 return Err(MessageParseError::invalid(
241 "simple_extension_name",
242 name_span,
243 format!(
244 "type/type-variation names must not include a signature suffix, got '{name}'"
245 ),
246 ));
247 }
248 iter.done();
249
250 Ok(SimpleExtensionDeclaration {
251 anchor,
252 urn_anchor,
253 name: name.to_string(),
254 })
255 }
256}
257
258use crate::extensions::any::Any;
262use crate::parser::expressions::{FieldIndex, Name};
263use crate::textify::expressions::Reference;
264
265impl ScopedParsePair for ExtensionValue {
266 fn rule() -> Rule {
267 Rule::extension_argument
268 }
269
270 fn message() -> &'static str {
271 "ExtensionValue"
272 }
273
274 fn parse_pair(
275 extensions: &SimpleExtensions,
276 pair: pest::iterators::Pair<Rule>,
277 ) -> Result<Self, MessageParseError> {
278 assert_eq!(pair.as_rule(), Self::rule());
279
280 let inner = unwrap_single_pair(pair); Ok(match inner.as_rule() {
283 Rule::enum_value => {
284 let s = inner.as_str().trim_start_matches('&').to_string();
286 ExtensionValue::Enum(s)
287 }
288 Rule::reference => {
289 let field_index = FieldIndex::parse_pair(inner);
291 ExtensionValue::from(Reference(field_index.0))
292 }
293 Rule::untyped_literal => {
294 let value_pair = unwrap_single_pair(inner);
296 match value_pair.as_rule() {
297 Rule::string_literal => ExtensionValue::String(unescape_string(value_pair)),
298 Rule::integer => {
299 ExtensionValue::Integer(value_pair.as_str().parse::<i64>().unwrap())
300 }
301 Rule::float => {
302 ExtensionValue::Float(value_pair.as_str().parse::<f64>().unwrap())
303 }
304 Rule::boolean => ExtensionValue::Boolean(value_pair.as_str() == "true"),
305 _ => panic!(
306 "Unexpected extension scalar literal type: {:?}",
307 value_pair.as_rule()
308 ),
309 }
310 }
311 Rule::tuple => {
312 let tv = inner
313 .into_inner()
314 .map(|pair| ExtensionValue::parse_pair(extensions, pair))
315 .collect::<Result<TupleValue, MessageParseError>>()?;
316 ExtensionValue::Tuple(tv)
317 }
318 Rule::expression => {
319 let expr = Expression::parse_pair(extensions, inner)?;
320 ExtensionValue::from(expr)
321 }
322 _ => panic!("Unexpected extension argument type: {:?}", inner.as_rule()),
323 })
324 }
325}
326
327impl ScopedParsePair for ExtensionColumn {
328 fn rule() -> Rule {
329 Rule::extension_column
330 }
331
332 fn message() -> &'static str {
333 "ExtensionColumn"
334 }
335
336 fn parse_pair(
337 extensions: &SimpleExtensions,
338 pair: pest::iterators::Pair<Rule>,
339 ) -> Result<Self, MessageParseError> {
340 assert_eq!(pair.as_rule(), Self::rule());
341
342 let inner = unwrap_single_pair(pair); Ok(match inner.as_rule() {
345 Rule::named_column => {
346 let mut iter = inner.into_inner();
347 let name_pair = iter.next().unwrap(); let type_pair = iter.next().unwrap(); let name = Name::parse_pair(name_pair).0.to_string(); let ty = Type::parse_pair(extensions, type_pair)?;
352
353 ExtensionColumn::Named { name, r#type: ty }
354 }
355 Rule::reference => {
356 let field_index = FieldIndex::parse_pair(inner);
358 ExtensionColumn::Expr(Reference(field_index.0).into())
359 }
360 Rule::expression => {
361 let expr = Expression::parse_pair(extensions, inner)?;
362 ExtensionColumn::Expr(expr.into())
363 }
364 _ => panic!("Unexpected extension column type: {:?}", inner.as_rule()),
365 })
366 }
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub(crate) enum ExtensionRelationKind {
373 Leaf,
374 Single,
375 Multi,
376}
377
378impl FromStr for ExtensionRelationKind {
379 type Err = String;
380
381 fn from_str(s: &str) -> Result<Self, Self::Err> {
382 match s {
383 "ExtensionLeaf" => Ok(ExtensionRelationKind::Leaf),
384 "ExtensionSingle" => Ok(ExtensionRelationKind::Single),
385 "ExtensionMulti" => Ok(ExtensionRelationKind::Multi),
386 _ => Err(format!("Unknown extension relation type: {s}")),
387 }
388 }
389}
390
391impl ExtensionRelationKind {
392 pub(crate) fn validate_child_count(self, child_count: usize) -> Result<(), String> {
393 match self {
394 ExtensionRelationKind::Leaf => {
395 if child_count == 0 {
396 Ok(())
397 } else {
398 Err(format!(
399 "ExtensionLeaf should have no input children, got {child_count}"
400 ))
401 }
402 }
403 ExtensionRelationKind::Single => {
404 if child_count == 1 {
405 Ok(())
406 } else {
407 Err(format!(
408 "ExtensionSingle should have exactly 1 input child, got {child_count}"
409 ))
410 }
411 }
412 ExtensionRelationKind::Multi => Ok(()),
413 }
414 }
415
416 pub(crate) fn create_rel(
418 self,
419 detail: Option<Any>,
420 children: Vec<substrait::proto::Rel>,
421 ) -> substrait::proto::Rel {
422 use substrait::proto::rel::RelType;
423 use substrait::proto::{ExtensionLeafRel, ExtensionMultiRel, ExtensionSingleRel};
424
425 let rel_type = match self {
426 ExtensionRelationKind::Leaf => RelType::ExtensionLeaf(ExtensionLeafRel {
427 common: None,
428 detail: detail.map(Into::into),
429 }),
430 ExtensionRelationKind::Single => {
431 let input = children.into_iter().next();
432 RelType::ExtensionSingle(Box::new(ExtensionSingleRel {
433 common: None,
434 detail: detail.map(Into::into),
435 input: input.map(Box::new),
436 }))
437 }
438 ExtensionRelationKind::Multi => RelType::ExtensionMulti(ExtensionMultiRel {
439 common: None,
440 detail: detail.map(Into::into),
441 inputs: children,
442 }),
443 };
444
445 substrait::proto::Rel {
446 rel_type: Some(rel_type),
447 }
448 }
449}
450
451#[derive(Debug, Clone)]
454pub(crate) struct ExtensionInvocation {
455 pub(crate) relation_kind: ExtensionRelationKind,
456 pub(crate) name: String,
457 pub(crate) args: ExtensionArgs,
458}
459
460impl ScopedParsePair for ExtensionInvocation {
461 fn rule() -> Rule {
462 Rule::extension_relation
463 }
464
465 fn message() -> &'static str {
466 "ExtensionInvocation"
467 }
468
469 fn parse_pair(
470 extensions: &SimpleExtensions,
471 pair: pest::iterators::Pair<Rule>,
472 ) -> Result<Self, MessageParseError> {
473 assert_eq!(pair.as_rule(), Self::rule());
474
475 let mut iter = pair.into_inner();
476
477 let extension_name_pair = iter.next().unwrap(); let full_extension_name = extension_name_pair.as_str();
480
481 let (relation_type_str, custom_name) = if full_extension_name.contains(':') {
484 let parts: Vec<&str> = full_extension_name.splitn(2, ':').collect();
485 (parts[0], parts[1].to_string())
486 } else {
487 (full_extension_name, "UnknownExtension".to_string())
488 };
489
490 let relation_kind = ExtensionRelationKind::from_str(relation_type_str).unwrap();
491 let mut args = ExtensionArgs::default();
492
493 let ext_arguments = iter.next().unwrap();
495 match ext_arguments.as_rule() {
496 Rule::arguments => {
497 arguments_rule_parsing(extensions, ext_arguments, &mut args)?;
498 }
499 r => unreachable!("Unexpected rule in ExtensionArgs: {:?}", r),
500 }
501
502 let extension_columns = iter.next();
504 if let Some(value) = extension_columns {
505 match value.as_rule() {
506 Rule::extension_columns => {
507 for col_pair in value.into_inner() {
508 if col_pair.as_rule() == Rule::extension_column {
509 let column = ExtensionColumn::parse_pair(extensions, col_pair)?;
510 args.output_columns.push(column);
511 }
512 }
513 }
514 r => unreachable!("Unexpected rule in ExtensionArgs: {:?}", r),
515 }
516 }
517
518 Ok(ExtensionInvocation {
519 relation_kind,
520 name: custom_name,
521 args,
522 })
523 }
524}
525
526#[derive(Debug, Clone)]
528pub(crate) struct AddendumInvocation {
529 pub(crate) kind: AddendumKind,
530 pub(crate) name: String,
531 pub(crate) args: ExtensionArgs,
532}
533
534impl ScopedParsePair for AddendumInvocation {
535 fn rule() -> Rule {
536 Rule::addendum
537 }
538
539 fn message() -> &'static str {
540 "AddendumInvocation"
541 }
542
543 fn parse_pair(
544 extensions: &SimpleExtensions,
545 pair: pest::iterators::Pair<Rule>,
546 ) -> Result<Self, MessageParseError> {
547 assert_eq!(pair.as_rule(), Self::rule());
548
549 let mut iter = pair.into_inner();
550
551 let type_pair = iter.next().unwrap(); let kind = match type_pair.as_str() {
554 "Enh" => AddendumKind::Enhancement,
555 "Opt" => AddendumKind::Optimization,
556 "Ext" => AddendumKind::ExtensionTable,
557 other => unreachable!("Unexpected addendum_type: {other}"),
558 };
559
560 let name_pair = iter.next().unwrap();
562 let name = Name::parse_pair(name_pair).0.to_string();
563
564 let mut args = ExtensionArgs::default();
566
567 let arguments_pair = iter.next().unwrap();
568 match arguments_pair.as_rule() {
569 Rule::arguments => {
570 arguments_rule_parsing(extensions, arguments_pair, &mut args)?;
571 }
572 r => unreachable!("Unexpected rule in AddendumInvocation args: {r:?}"),
573 }
574
575 Ok(AddendumInvocation { kind, name, args })
576 }
577}
578
579fn arguments_rule_parsing(
580 extensions: &SimpleExtensions,
581 inner_pair: pest::iterators::Pair<'_, Rule>,
582 args: &mut ExtensionArgs,
583) -> Result<(), MessageParseError> {
584 for arg in inner_pair.into_inner() {
585 match arg.as_rule() {
586 Rule::extension_arguments => {
587 for arg_pair in arg.into_inner() {
588 assert_eq!(arg_pair.as_rule(), Rule::extension_argument);
589 args.push(ExtensionValue::parse_pair(extensions, arg_pair)?);
590 }
591 }
592 Rule::extension_named_arguments => {
593 for arg_pair in arg.into_inner() {
594 assert_eq!(arg_pair.as_rule(), Rule::extension_named_argument);
595 let mut arg_iter = arg_pair.into_inner();
596 let name_p = arg_iter.next().unwrap();
597 let value_p = arg_iter.next().unwrap();
598 let key = Name::parse_pair(name_p).0.to_string();
599 let val = ExtensionValue::parse_pair(extensions, value_p)?;
600 args.insert(key, val);
601 }
602 }
603 Rule::empty => {}
604 r => unreachable!("Unexpected rule in extension args: {r:?}"),
605 }
606 }
607 Ok(())
608}
609
610#[cfg(test)]
611mod tests {
612 use substrait::proto;
613 use substrait::proto::expression::RexType;
614 use substrait::proto::expression::literal::LiteralType;
615
616 use super::*;
617 use crate::extensions::{Expr, ExtensionValue};
618 use crate::fixtures::TestContext;
619 use crate::parser::Parser;
620 use crate::parser::common::test_support::ScopedParse;
621 use crate::{OutputOptions, format};
622
623 fn parse_extension_value(text: &str) -> ExtensionValue {
624 ExtensionValue::parse(&SimpleExtensions::default(), text).unwrap()
625 }
626
627 #[test]
628 fn test_parse_urn_extension_declaration() {
629 let line = "@1: /my/urn1";
630 let urn = URNExtensionDeclaration::parse_str(line).unwrap();
631 assert_eq!(urn.anchor, 1);
632 assert_eq!(urn.urn, "/my/urn1");
633 }
634
635 #[test]
636 fn test_parse_simple_extension_declaration() {
637 let line = "#5@2: my_function_name";
638 let decl =
639 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
640 assert_eq!(decl.anchor, 5);
641 assert_eq!(decl.urn_anchor, 2);
642 assert_eq!(decl.name, "my_function_name");
643
644 let line2 = "#10 @200: another_ext_123";
645 let decl =
646 SimpleExtensionDeclaration::parse_from_kind(line2, ExtensionKind::Function).unwrap();
647 assert_eq!(decl.anchor, 10);
648 assert_eq!(decl.urn_anchor, 200);
649 assert_eq!(decl.name, "another_ext_123");
650 }
651
652 #[test]
653 fn test_parse_urn_extension_declaration_str() {
654 let line = "@1: /my/urn1";
655 let urn = URNExtensionDeclaration::parse_str(line).unwrap();
656 assert_eq!(urn.anchor, 1);
657 assert_eq!(urn.urn, "/my/urn1");
658 }
659
660 #[test]
661 fn test_extensions_round_trip_plan() {
662 let input = r#"
663=== Extensions
664URNs:
665 @ 1: /urn/common
666 @ 2: /urn/specific_funcs
667Functions:
668 # 10 @ 1: func_a
669 # 11 @ 2: func_b_special
670Types:
671 # 20 @ 1: SomeType
672Type Variations:
673 # 30 @ 2: VarX
674"#
675 .trim_start();
676
677 let plan = Parser::parse(input).unwrap();
679
680 assert_eq!(plan.extension_urns.len(), 2);
682 assert_eq!(plan.extensions.len(), 4);
683
684 let (extensions, errors) =
686 SimpleExtensions::from_extensions(&plan.extension_urns, &plan.extensions);
687
688 assert!(errors.is_empty());
689 let output = extensions.to_string(" ");
691
692 assert_eq!(output, input);
694 }
695
696 #[test]
697 fn test_parse_simple_extension_declaration_compound_name() {
698 let line = "#1 @2: equal:any_any";
700 let decl =
701 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
702 assert_eq!(decl.anchor, 1);
703 assert_eq!(decl.urn_anchor, 2);
704 assert_eq!(decl.name, "equal:any_any");
705 }
706
707 #[test]
708 fn test_parse_simple_extension_declaration_compound_name_multi_segment() {
709 let line = "#3 @1: regexp_match_substring:str_str_i64";
710 let decl =
711 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
712 assert_eq!(decl.anchor, 3);
713 assert_eq!(decl.urn_anchor, 1);
714 assert_eq!(decl.name, "regexp_match_substring:str_str_i64");
715 }
716
717 #[test]
718 fn test_parse_simple_extension_declaration_u_prefix_function_with_u_signature() {
719 let line = "#5 @2: json_extract_path:u!json_str";
722 let decl =
723 SimpleExtensionDeclaration::parse_from_kind(line, ExtensionKind::Function).unwrap();
724 assert_eq!(decl.anchor, 5);
725 assert_eq!(decl.urn_anchor, 2);
726 assert_eq!(decl.name, "json_extract_path:u!json_str");
727 }
728
729 #[test]
730 fn test_u_prefix_type_declaration_accepted() {
731 let plan_text = "\
733=== Extensions
734URNs:
735 @ 1: https://example.com/types
736Types:
737 # 11 @ 1: u!point
738=== Plan
739Root[result]
740 Project[$0]
741 Read[data => p:point#11]";
742 let plan = Parser::parse(plan_text).unwrap();
743 let (text, errors) = format(&plan);
744 assert!(errors.is_empty(), "unexpected errors: {errors:?}");
745 assert!(
746 text.contains(" # 11 @ 1: point"),
747 "declaration line must use bare name"
748 );
749 assert!(
750 !text.contains("u!point"),
751 "u! prefix should be stripped in output"
752 );
753 }
754
755 #[test]
756 fn test_u_prefix_type_variation_declaration_rejected() {
757 let plan_text = "\
759=== Extensions
760URNs:
761 @ 1: https://example.com/types
762Type Variations:
763 # 30 @ 1: u!myvar
764=== Plan
765Root[result]
766 Read[data => x:i64]";
767 assert!(
768 Parser::parse(plan_text).is_err(),
769 "u! prefix on a type variation name should be rejected"
770 );
771 }
772
773 #[test]
774 fn test_u_prefix_function_declaration_rejected() {
775 let plan_text = "\
777=== Extensions
778URNs:
779 @ 1: https://example.com/funcs
780Functions:
781 # 21 @ 1: u!json_get
782=== Plan
783Root[result]
784 Read[data => x:i64]";
785 assert!(
786 Parser::parse(plan_text).is_err(),
787 "u! prefix on a function name should be rejected"
788 );
789 }
790
791 #[test]
792 fn test_signature_on_type_declaration_rejected() {
793 let plan_text = "\
795=== Extensions
796URNs:
797 @ 1: https://example.com/types
798Types:
799 # 10 @ 1: mytype:i64_i64
800=== Plan
801Root[result]
802 Read[data => x:i64]";
803 assert!(
804 Parser::parse(plan_text).is_err(),
805 "function signature suffix on a type declaration should be rejected"
806 );
807 }
808
809 #[test]
810 fn test_u_prefix_function_rejected_at_parser_level() {
811 let plan_text = "\
813=== Extensions
814URNs:
815 @ 1: https://example.com/funcs
816Functions:
817 # 21 @ 1: u!bad_func
818=== Plan
819Root[result]
820 Read[data => x:i64]";
821 let err = Parser::parse(plan_text).unwrap_err();
822 assert!(
823 matches!(
824 err,
825 crate::parser::ParseError::Extension(_, ExtensionParseError::Message(_))
826 ),
827 "expected parser-level MessageParseError, got: {err}"
828 );
829 }
830
831 #[test]
832 fn test_extensions_round_trip_plan_with_compound_names() {
833 let input = r#"=== Extensions
834URNs:
835 @ 1: extension:io.substrait:functions_string
836 @ 2: extension:io.substrait:functions_comparison
837Functions:
838 # 1 @ 2: equal:any_any
839 # 2 @ 1: regexp_match_substring:str_str
840 # 3 @ 1: regexp_match_substring:str_str_i64
841"#;
842 let plan = Parser::parse(input).unwrap();
843 let (extensions, errors) =
844 SimpleExtensions::from_extensions(&plan.extension_urns, &plan.extensions);
845 assert!(errors.is_empty());
846 assert_eq!(
848 extensions
849 .find_by_anchor(crate::extensions::simple::ExtensionKind::Function, 1)
850 .unwrap()
851 .1
852 .full(),
853 "equal:any_any"
854 );
855 assert_eq!(
856 extensions
857 .find_by_anchor(crate::extensions::simple::ExtensionKind::Function, 3)
858 .unwrap()
859 .1
860 .full(),
861 "regexp_match_substring:str_str_i64"
862 );
863 assert_eq!(extensions.to_string(" "), input);
865 }
866
867 #[test]
868 fn test_tuple_mixed_types_parses() {
869 let val = parse_extension_value("(&HASH, 8, 'hello')");
871 let ExtensionValue::Tuple(items) = val else {
872 panic!("expected Tuple, got {val:?}");
873 };
874 assert_eq!(items.len(), 3);
875 let items: Vec<&ExtensionValue> = items.iter().collect();
876 assert!(matches!(items[0], ExtensionValue::Enum(s) if s == "HASH"));
877 assert_eq!(i64::try_from(items[1]).unwrap(), 8);
878 assert_eq!(<&str>::try_from(items[2]).unwrap(), "hello");
879 }
880
881 #[test]
882 fn test_empty_tuple_parses() {
883 let val = parse_extension_value("()");
884 let ExtensionValue::Tuple(items) = val else {
885 panic!("expected Tuple, got {val:?}");
886 };
887 assert!(items.is_empty());
888 }
889
890 #[test]
891 fn test_nested_tuple_parses() {
892 let val = parse_extension_value("((&HASH, &RANGE), 8)");
893 let ExtensionValue::Tuple(outer) = val else {
894 panic!("expected Tuple, got {val:?}");
895 };
896 assert_eq!(outer.len(), 2);
897 let ExtensionValue::Tuple(inner) = outer.iter().next().unwrap() else {
898 panic!("expected inner Tuple");
899 };
900 assert_eq!(inner.len(), 2);
901 assert!(matches!(inner.iter().next().unwrap(), ExtensionValue::Enum(s) if s == "HASH"));
902 assert_eq!(i64::try_from(outer.iter().nth(1).unwrap()).unwrap(), 8);
903 }
904
905 #[test]
906 fn test_tuple_in_addendum_parses() {
907 let inv = AddendumInvocation::parse(
908 &SimpleExtensions::default(),
909 "+ Enh:Foo[(&HASH, &RANGE), count=8]",
910 )
911 .unwrap();
912 assert_eq!(inv.kind, AddendumKind::Enhancement);
913 assert_eq!(inv.name, "Foo");
914 assert_eq!(inv.args.positional.len(), 1);
915 let ExtensionValue::Tuple(items) = &inv.args.positional[0] else {
916 panic!("expected Tuple positional arg");
917 };
918 assert_eq!(items.len(), 2);
919 let items: Vec<&ExtensionValue> = items.iter().collect();
920 assert!(matches!(items[0], ExtensionValue::Enum(s) if s == "HASH"));
921 assert!(matches!(items[1], ExtensionValue::Enum(s) if s == "RANGE"));
922 assert_eq!(inv.args.named.len(), 1);
923 }
924
925 #[test]
926 fn extension_relation_kind_parses_text_prefixes() {
927 assert_eq!(
928 ExtensionRelationKind::from_str("ExtensionLeaf").unwrap(),
929 ExtensionRelationKind::Leaf
930 );
931 assert_eq!(
932 ExtensionRelationKind::from_str("ExtensionSingle").unwrap(),
933 ExtensionRelationKind::Single
934 );
935 assert_eq!(
936 ExtensionRelationKind::from_str("ExtensionMulti").unwrap(),
937 ExtensionRelationKind::Multi
938 );
939 }
940
941 #[test]
942 fn extension_multi_allows_any_child_count() {
943 assert!(ExtensionRelationKind::Multi.validate_child_count(0).is_ok());
944 assert!(ExtensionRelationKind::Multi.validate_child_count(1).is_ok());
945 assert!(ExtensionRelationKind::Multi.validate_child_count(3).is_ok());
946 }
947
948 #[test]
949 fn extension_single_rejects_wrong_child_counts() {
950 assert!(
951 ExtensionRelationKind::Single
952 .validate_child_count(0)
953 .is_err()
954 );
955 assert!(
956 ExtensionRelationKind::Single
957 .validate_child_count(2)
958 .is_err()
959 );
960 }
961
962 #[test]
963 fn test_tuple_textify_roundtrip() {
964 let ctx = TestContext::new();
965 for text in &[
966 "(&HASH, &RANGE)",
967 "(&HASH, 8, 'hello')",
968 "()",
969 "(&HASH,)",
970 "((&HASH, &RANGE), 8)",
971 ] {
972 let val = parse_extension_value(text);
973 let rendered = ctx.textify_no_errors(&val);
974 assert_eq!(&rendered, text, "roundtrip failed for {text}");
975 }
976 }
977
978 #[test]
979 fn test_literal_expression_value_textifies_to_canonical_literal() {
980 let expr = proto::Expression {
981 rex_type: Some(RexType::Literal(proto::expression::Literal {
982 literal_type: Some(LiteralType::I64(42)),
983 nullable: false,
984 type_variation_reference: 0,
985 })),
986 };
987 let value = ExtensionValue::from(expr.clone());
988 let ctx = TestContext::new();
989
990 let rendered = ctx.textify_no_errors(&value);
991 assert_eq!(rendered, "42");
992
993 let parsed = parse_extension_value(&rendered);
994 let parsed_expr = Expr::try_from(&parsed).unwrap();
995 assert_eq!(parsed_expr.as_proto(), &expr);
996 }
997
998 #[test]
999 fn test_extension_scalar_literals_stay_scalar_in_verbose_output() {
1000 let ctx = TestContext::new().with_options(OutputOptions::verbose());
1001
1002 let scalar = ExtensionValue::from(42_i64);
1003 assert_eq!(ctx.textify_no_errors(&scalar), "42");
1004
1005 let expression = ExtensionValue::from(Expr::from(42_i64));
1006 assert_eq!(ctx.textify_no_errors(&expression), "42:i64");
1007 }
1008
1009 #[test]
1010 fn test_typed_extension_literal_parses_as_expression() {
1011 let value = parse_extension_value("42:i16");
1012 assert!(i64::try_from(&value).is_err());
1013
1014 let expr = Expr::try_from(&value).unwrap();
1015 assert_eq!(ctx_text(&expr), "42:i16");
1016 }
1017
1018 fn ctx_text(value: &Expr) -> String {
1019 TestContext::new().textify_no_errors(value)
1020 }
1021}