1use std::fmt::{self};
2
3use expr::RexType;
4use substrait::proto::expression::field_reference::ReferenceType;
5use substrait::proto::expression::literal::LiteralType;
6use substrait::proto::expression::{
7 FieldReference, ReferenceSegment, ScalarFunction, reference_segment,
8};
9use substrait::proto::function_argument::ArgType;
10use substrait::proto::r#type::{self as ptype, Kind, Nullability};
11use substrait::proto::{
12 AggregateFunction, Expression, FunctionArgument, FunctionOption, expression as expr,
13};
14
15use super::{PlanError, Scope, Textify, Visibility};
16use crate::extensions::SimpleExtensions;
17use crate::extensions::simple::ExtensionKind;
18use crate::textify::types::{Name, NamedAnchor, OutputType, escaped};
19
20pub fn textify_binary<S: Scope, W: fmt::Write>(items: &[u8], ctx: &S, w: &mut W) -> fmt::Result {
33 if ctx.options().show_literal_binaries {
34 write!(w, "0x")?;
35 for &n in items {
36 write!(w, "{n:02x}")?;
37 }
38 } else {
39 write!(w, "{{binary}}")?;
40 }
41 Ok(())
42}
43
44pub fn textify_literal_from_string<S: Scope, W: fmt::Write>(
45 s: &str,
46 t: Kind,
47 ctx: &S,
48 w: &mut W,
49) -> fmt::Result {
50 write!(w, "\'{}\':{}", escaped(s), ctx.display(&t))
51}
52
53pub fn textify_enum<S: Scope, W: fmt::Write>(s: &str, _ctx: &S, w: &mut W) -> fmt::Result {
56 write!(w, "&{}", Name(s))
57}
58
59pub fn timestamp_to_string(t: i64) -> String {
60 let ts = chrono::DateTime::from_timestamp_nanos(t);
61 ts.to_rfc3339()
62}
63
64trait Kinded {
65 fn kind(&self, ctx: &SimpleExtensions) -> Option<Kind>;
66}
67
68impl Kinded for LiteralType {
69 fn kind(&self, _ctx: &SimpleExtensions) -> Option<Kind> {
70 match self {
71 LiteralType::Boolean(_) => Some(Kind::Bool(ptype::Boolean {
72 type_variation_reference: 0,
73 nullability: Nullability::Required.into(),
74 })),
75 LiteralType::I8(_) => Some(Kind::I8(ptype::I8 {
76 type_variation_reference: 0,
77 nullability: Nullability::Required.into(),
78 })),
79 LiteralType::I16(_) => Some(Kind::I16(ptype::I16 {
80 type_variation_reference: 0,
81 nullability: Nullability::Required.into(),
82 })),
83 LiteralType::I32(_) => Some(Kind::I32(ptype::I32 {
84 type_variation_reference: 0,
85 nullability: Nullability::Required.into(),
86 })),
87 LiteralType::I64(_) => Some(Kind::I64(ptype::I64 {
88 type_variation_reference: 0,
89 nullability: Nullability::Required.into(),
90 })),
91 LiteralType::Fp32(_) => Some(Kind::Fp32(ptype::Fp32 {
92 type_variation_reference: 0,
93 nullability: Nullability::Required.into(),
94 })),
95 LiteralType::Fp64(_) => Some(Kind::Fp64(ptype::Fp64 {
96 type_variation_reference: 0,
97 nullability: Nullability::Required.into(),
98 })),
99 LiteralType::String(_) => Some(Kind::String(ptype::String {
100 type_variation_reference: 0,
101 nullability: Nullability::Required.into(),
102 })),
103 LiteralType::Binary(_) => Some(Kind::Binary(ptype::Binary {
104 type_variation_reference: 0,
105 nullability: Nullability::Required.into(),
106 })),
107 LiteralType::Timestamp(_) => Some(Kind::Timestamp(ptype::Timestamp {
108 type_variation_reference: 0,
109 nullability: Nullability::Required.into(),
110 })),
111 LiteralType::Date(_) => Some(Kind::Date(ptype::Date {
112 type_variation_reference: 0,
113 nullability: Nullability::Required.into(),
114 })),
115 LiteralType::Time(_) => Some(Kind::Time(ptype::Time {
116 type_variation_reference: 0,
117 nullability: Nullability::Required.into(),
118 })),
119 LiteralType::IntervalYearToMonth(_) => Some(Kind::IntervalYear(ptype::IntervalYear {
120 type_variation_reference: 0,
121 nullability: Nullability::Required.into(),
122 })),
123 LiteralType::IntervalDayToSecond(i) => {
124 let precision = match i.precision_mode {
125 Some(expr::literal::interval_day_to_second::PrecisionMode::Microseconds(
126 _m,
127 )) => Some(6),
128 Some(expr::literal::interval_day_to_second::PrecisionMode::Precision(p)) => {
129 Some(p)
130 }
131 None => None,
133 };
134
135 Some(Kind::IntervalDay(ptype::IntervalDay {
136 type_variation_reference: 0,
137 nullability: Nullability::Required.into(),
138 precision,
139 }))
140 }
141 LiteralType::IntervalCompound(_) => todo!(),
142 LiteralType::FixedChar(_) => todo!(),
143 LiteralType::VarChar(_c) => todo!(),
144 LiteralType::FixedBinary(_b) => todo!(),
145 LiteralType::Decimal(_d) => todo!(),
146 LiteralType::PrecisionTime(_t) => todo!(),
147 LiteralType::PrecisionTimestamp(t) => {
148 Some(Kind::PrecisionTimestamp(ptype::PrecisionTimestamp {
149 type_variation_reference: 0,
150 nullability: Nullability::Required.into(),
151 precision: t.precision,
152 }))
153 }
154 LiteralType::PrecisionTimestampTz(_t) => todo!(),
155 LiteralType::Struct(_s) => todo!(),
156 LiteralType::Map(_m) => todo!(),
157 LiteralType::TimestampTz(_t) => todo!(),
158 LiteralType::Uuid(_u) => todo!(),
159 LiteralType::Null(_n) => todo!(),
160 LiteralType::List(_l) => todo!(),
161 LiteralType::EmptyList(_l) => todo!(),
162 LiteralType::EmptyMap(_m) => todo!(),
163 LiteralType::UserDefined(_u) => todo!(),
164 }
165 }
166}
167
168impl Textify for LiteralType {
169 fn name() -> &'static str {
170 "LiteralType"
171 }
172
173 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
174 match self {
175 LiteralType::Boolean(true) => write!(w, "true")?,
176 LiteralType::Boolean(false) => write!(w, "false")?,
177 LiteralType::I8(i) => write!(w, "{i}:i8")?,
178 LiteralType::I16(i) => write!(w, "{i}:i16")?,
179 LiteralType::I32(i) => write!(w, "{i}:i32")?,
180 LiteralType::I64(i) => {
181 match ctx.options().literal_types {
182 Visibility::Always => write!(w, "{i}:i64")?,
183 Visibility::Required => write!(w, "{i}")?, Visibility::Never => write!(w, "{i}")?,
185 }
186 }
187 LiteralType::Fp32(f) => write!(w, "{f}:fp32")?,
188 LiteralType::Fp64(f) => {
189 match ctx.options().literal_types {
190 Visibility::Always => write!(w, "{f}:fp64")?,
191 Visibility::Required => write!(w, "{f}")?, Visibility::Never => write!(w, "{f}")?,
193 }
194 }
195 LiteralType::String(s) => write!(w, "\"{}\"", s.escape_debug())?,
196 LiteralType::Binary(items) => textify_binary(items, ctx, w)?,
197 LiteralType::Timestamp(t) => {
198 let k = match self.kind(ctx.extensions()) {
199 Some(k) => k,
200 None => {
201 let err = PlanError::internal(
202 "LiteralType",
203 Some("Timestamp"),
204 format!("No kind found for {self:?}"),
205 );
206 write!(w, "{}", ctx.failure(err))?;
207 return Ok(());
208 }
209 };
210 let s = timestamp_to_string(*t);
211 textify_literal_from_string(&s, k, ctx, w)?
212 }
213 LiteralType::Date(_) => {
214 return write!(
215 w,
216 "{}",
217 ctx.failure(PlanError::unimplemented(
218 "LiteralType",
219 Some("Date"),
220 "Date literal textification not implemented",
221 ))
222 );
223 }
224 LiteralType::Time(_) => {
225 return write!(
226 w,
227 "{}",
228 ctx.failure(PlanError::unimplemented(
229 "LiteralType",
230 Some("Time"),
231 "Time literal textification not implemented",
232 ))
233 );
234 }
235 LiteralType::IntervalYearToMonth(_i) => {
236 return write!(
237 w,
238 "{}",
239 ctx.failure(PlanError::unimplemented(
240 "LiteralType",
241 Some("IntervalYearToMonth"),
242 "IntervalYearToMonth literal textification not implemented",
243 ))
244 );
245 }
246 LiteralType::IntervalDayToSecond(_i) => {
247 return write!(
248 w,
249 "{}",
250 ctx.failure(PlanError::unimplemented(
251 "LiteralType",
252 Some("IntervalDayToSecond"),
253 "IntervalDayToSecond literal textification not implemented",
254 ))
255 );
256 }
257 LiteralType::IntervalCompound(_i) => {
258 return write!(
259 w,
260 "{}",
261 ctx.failure(PlanError::unimplemented(
262 "LiteralType",
263 Some("IntervalCompound"),
264 "IntervalCompound literal textification not implemented",
265 ))
266 );
267 }
268 LiteralType::FixedChar(_c) => {
269 return write!(
270 w,
271 "{}",
272 ctx.failure(PlanError::unimplemented(
273 "LiteralType",
274 Some("FixedChar"),
275 "FixedChar literal textification not implemented",
276 ))
277 );
278 }
279 LiteralType::VarChar(_c) => {
280 return write!(
281 w,
282 "{}",
283 ctx.failure(PlanError::unimplemented(
284 "LiteralType",
285 Some("VarChar"),
286 "VarChar literal textification not implemented",
287 ))
288 );
289 }
290 LiteralType::FixedBinary(_i) => {
291 return write!(
292 w,
293 "{}",
294 ctx.failure(PlanError::unimplemented(
295 "LiteralType",
296 Some("FixedBinary"),
297 "FixedBinary literal textification not implemented",
298 ))
299 );
300 }
301 LiteralType::Decimal(_d) => {
302 return write!(
303 w,
304 "{}",
305 ctx.failure(PlanError::unimplemented(
306 "LiteralType",
307 Some("Decimal"),
308 "Decimal literal textification not implemented",
309 ))
310 );
311 }
312 LiteralType::PrecisionTime(_p) => {
313 return write!(
314 w,
315 "{}",
316 ctx.failure(PlanError::unimplemented(
317 "LiteralType",
318 Some("PrecisionTime"),
319 "PrecisionTime literal textification not implemented",
320 ))
321 );
322 }
323 LiteralType::PrecisionTimestamp(_p) => {
324 return write!(
325 w,
326 "{}",
327 ctx.failure(PlanError::unimplemented(
328 "LiteralType",
329 Some("PrecisionTimestamp"),
330 "PrecisionTimestamp literal textification not implemented",
331 ))
332 );
333 }
334 LiteralType::PrecisionTimestampTz(_p) => {
335 return write!(
336 w,
337 "{}",
338 ctx.failure(PlanError::unimplemented(
339 "LiteralType",
340 Some("PrecisionTimestampTz"),
341 "PrecisionTimestampTz literal textification not implemented",
342 ))
343 );
344 }
345 LiteralType::Struct(_s) => {
346 return write!(
347 w,
348 "{}",
349 ctx.failure(PlanError::unimplemented(
350 "LiteralType",
351 Some("Struct"),
352 "Struct literal textification not implemented",
353 )),
354 );
355 }
356 LiteralType::Map(_m) => {
357 return write!(
358 w,
359 "{}",
360 ctx.failure(PlanError::unimplemented(
361 "LiteralType",
362 Some("Map"),
363 "Map literal textification not implemented",
364 )),
365 );
366 }
367 LiteralType::TimestampTz(_t) => {
368 return write!(
369 w,
370 "{}",
371 ctx.failure(PlanError::unimplemented(
372 "LiteralType",
373 Some("TimestampTz"),
374 "TimestampTz literal textification not implemented",
375 ))
376 );
377 }
378 LiteralType::Uuid(_u) => {
379 return write!(
380 w,
381 "{}",
382 ctx.failure(PlanError::unimplemented(
383 "LiteralType",
384 Some("Uuid"),
385 "Uuid literal textification not implemented",
386 ))
387 );
388 }
389 LiteralType::Null(_n) => {
390 return write!(
391 w,
392 "{}",
393 ctx.failure(PlanError::unimplemented(
394 "LiteralType",
395 Some("Null"),
396 "Null literal textification not implemented",
397 ))
398 );
399 }
400 LiteralType::List(_l) => {
401 return write!(
402 w,
403 "{}",
404 ctx.failure(PlanError::unimplemented(
405 "LiteralType",
406 Some("List"),
407 "List literal textification not implemented",
408 ))
409 );
410 }
411 LiteralType::EmptyList(_l) => {
412 return write!(
413 w,
414 "{}",
415 ctx.failure(PlanError::unimplemented(
416 "LiteralType",
417 Some("EmptyList"),
418 "EmptyList literal textification not implemented",
419 ))
420 );
421 }
422 LiteralType::EmptyMap(_l) => {
423 return write!(
424 w,
425 "{}",
426 ctx.failure(PlanError::unimplemented(
427 "LiteralType",
428 Some("EmptyMap"),
429 "EmptyMap literal textification not implemented",
430 ))
431 );
432 }
433 LiteralType::UserDefined(_u) => {
434 return write!(
435 w,
436 "{}",
437 ctx.failure(PlanError::unimplemented(
438 "LiteralType",
439 Some("UserDefined"),
440 "UserDefined literal textification not implemented",
441 ))
442 );
443 }
444 }
445 Ok(())
446 }
447}
448
449impl Textify for expr::Literal {
450 fn name() -> &'static str {
451 "Literal"
452 }
453
454 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
455 write!(w, "{}", ctx.expect(self.literal_type.as_ref()))
456 }
457}
458
459pub struct Reference(pub i32);
460
461impl fmt::Display for Reference {
462 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463 write!(f, "${}", self.0)
464 }
465}
466
467impl From<Reference> for Expression {
468 fn from(r: Reference) -> Self {
469 Expression {
472 rex_type: Some(RexType::Selection(Box::new(FieldReference {
473 reference_type: Some(ReferenceType::DirectReference(ReferenceSegment {
474 reference_type: Some(reference_segment::ReferenceType::StructField(Box::new(
475 reference_segment::StructField {
476 field: r.0,
477 child: None,
478 },
479 ))),
480 })),
481 root_type: None,
482 }))),
483 }
484 }
485}
486
487impl Textify for Reference {
488 fn name() -> &'static str {
489 "Reference"
490 }
491
492 fn textify<S: Scope, W: fmt::Write>(&self, _ctx: &S, w: &mut W) -> fmt::Result {
493 write!(w, "{self}")
494 }
495}
496
497impl Textify for FieldReference {
498 fn name() -> &'static str {
499 "FieldReference"
500 }
501
502 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
503 let ref_type = match &self.reference_type {
504 None => {
505 return write!(
506 w,
507 "{}",
508 ctx.failure(PlanError::invalid(
509 "FieldReference",
510 Some("reference_type"),
511 "Required field reference_type is missing",
512 ))
513 );
514 }
515 Some(ReferenceType::DirectReference(r)) => r,
516 _ => {
517 return write!(
518 w,
519 "{}",
520 ctx.failure(PlanError::unimplemented(
521 "FieldReference",
522 Some("FieldReference"),
523 "FieldReference textification implemented only for StructField",
524 ))
525 );
526 }
527 };
528
529 match &ref_type.reference_type {
530 Some(reference_segment::ReferenceType::StructField(s)) => {
531 write!(w, "{}", Reference(s.field))
532 }
533 None => write!(
534 w,
535 "{}",
536 ctx.failure(PlanError::invalid(
537 "ReferenceSegment",
538 Some("reference_type"),
539 "Required field reference_type is missing",
540 ))
541 ),
542 _ => write!(
543 w,
544 "{}",
545 ctx.failure(PlanError::unimplemented(
546 "ReferenceSegment",
547 Some("reference_type"),
548 "ReferenceSegment textification implemented only for StructField",
549 ))
550 ),
551 }
552 }
553}
554
555impl Textify for ScalarFunction {
556 fn name() -> &'static str {
557 "ScalarFunction"
558 }
559
560 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
561 let name_and_anchor =
562 NamedAnchor::lookup(ctx, ExtensionKind::Function, self.function_reference);
563 let name_and_anchor = ctx.display(&name_and_anchor);
564
565 let args = ctx.separated(&self.arguments, ", ");
566 let options = ctx.separated(&self.options, ", ");
567 let between = if self.arguments.is_empty() || self.options.is_empty() {
568 ""
569 } else {
570 ", "
571 };
572
573 let output = OutputType(self.output_type.as_ref());
574 let output_type = ctx.optional(&output, ctx.options().fn_types);
575
576 write!(
577 w,
578 "{name_and_anchor}({args}{between}{options}){output_type}"
579 )?;
580 Ok(())
581 }
582}
583
584impl Textify for FunctionOption {
585 fn name() -> &'static str {
586 "FunctionOption"
587 }
588
589 fn textify<S: Scope, W: fmt::Write>(&self, _ctx: &S, w: &mut W) -> fmt::Result {
590 write!(w, "{}⇒[", self.name)?;
591 let mut first = true;
592 for pref in self.preference.iter() {
593 if !first {
594 write!(w, ", ")?;
595 } else {
596 first = false;
597 }
598 write!(w, "{pref}")?;
599 }
600 write!(w, "]")?;
601 Ok(())
602 }
603}
604
605impl Textify for FunctionArgument {
606 fn name() -> &'static str {
607 "FunctionArgument"
608 }
609
610 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
611 write!(w, "{}", ctx.expect(self.arg_type.as_ref()))
612 }
613}
614
615impl Textify for ArgType {
616 fn name() -> &'static str {
617 "ArgType"
618 }
619
620 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
621 match self {
622 ArgType::Type(t) => t.textify(ctx, w),
623 ArgType::Value(v) => v.textify(ctx, w),
624 ArgType::Enum(e) => textify_enum(e, ctx, w),
625 }
626 }
627}
628
629impl Textify for RexType {
630 fn name() -> &'static str {
631 "RexType"
632 }
633
634 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
635 match self {
636 RexType::Literal(literal) => literal.textify(ctx, w),
637 RexType::Selection(f) => f.textify(ctx, w),
638 RexType::ScalarFunction(s) => s.textify(ctx, w),
639 RexType::WindowFunction(_w) => write!(
640 w,
641 "{}",
642 ctx.failure(PlanError::unimplemented(
643 "RexType",
644 Some("WindowFunction"),
645 "WindowFunction textification not implemented",
646 ))
647 ),
648 RexType::IfThen(_i) => write!(
649 w,
650 "{}",
651 ctx.failure(PlanError::unimplemented(
652 "RexType",
653 Some("IfThen"),
654 "IfThen textification not implemented",
655 ))
656 ),
657 RexType::SwitchExpression(_s) => write!(
658 w,
659 "{}",
660 ctx.failure(PlanError::unimplemented(
661 "RexType",
662 Some("SwitchExpression"),
663 "SwitchExpression textification not implemented",
664 ))
665 ),
666 RexType::SingularOrList(_s) => write!(
667 w,
668 "{}",
669 ctx.failure(PlanError::unimplemented(
670 "RexType",
671 Some("SingularOrList"),
672 "SingularOrList textification not implemented",
673 ))
674 ),
675 RexType::MultiOrList(_m) => write!(
676 w,
677 "{}",
678 ctx.failure(PlanError::unimplemented(
679 "RexType",
680 Some("MultiOrList"),
681 "MultiOrList textification not implemented",
682 ))
683 ),
684 RexType::Cast(_c) => write!(
685 w,
686 "{}",
687 ctx.failure(PlanError::unimplemented(
688 "RexType",
689 Some("Cast"),
690 "Cast textification not implemented",
691 ))
692 ),
693 RexType::Subquery(_s) => write!(
694 w,
695 "{}",
696 ctx.failure(PlanError::unimplemented(
697 "RexType",
698 Some("Subquery"),
699 "Subquery textification not implemented",
700 ))
701 ),
702 RexType::Nested(_n) => write!(
703 w,
704 "{}",
705 ctx.failure(PlanError::unimplemented(
706 "RexType",
707 Some("Nested"),
708 "Nested textification not implemented",
709 ))
710 ),
711 RexType::DynamicParameter(_d) => write!(
712 w,
713 "{}",
714 ctx.failure(PlanError::unimplemented(
715 "RexType",
716 Some("DynamicParameter"),
717 "DynamicParameter textification not implemented",
718 ))
719 ),
720 RexType::Enum(_) => write!(
721 w,
722 "{}",
723 ctx.failure(PlanError::unimplemented(
724 "RexType",
725 Some("Enum"),
726 "Enum textification not implemented",
727 ))
728 ),
729 }
730 }
731}
732
733impl Textify for Expression {
734 fn name() -> &'static str {
735 "Expression"
736 }
737
738 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
739 write!(w, "{}", ctx.expect(self.rex_type.as_ref()))
740 }
741}
742
743impl Textify for AggregateFunction {
744 fn name() -> &'static str {
745 "AggregateFunction"
746 }
747
748 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
749 let name_and_anchor = crate::textify::types::NamedAnchor::lookup(
751 ctx,
752 crate::extensions::simple::ExtensionKind::Function,
753 self.function_reference,
754 );
755 let name_and_anchor = ctx.display(&name_and_anchor);
756
757 let args = ctx.separated(&self.arguments, ", ");
758 let options = ctx.separated(&self.options, ", ");
759 let between = if self.arguments.is_empty() || self.options.is_empty() {
760 ""
761 } else {
762 ", "
763 };
764
765 let output = crate::textify::types::OutputType(self.output_type.as_ref());
766 let output_type = ctx.optional(&output, ctx.options().fn_types);
767
768 write!(
769 w,
770 "{name_and_anchor}({args}{between}{options}){output_type}"
771 )
772 }
773}
774
775#[cfg(test)]
776mod tests {
777 use super::*;
778 use crate::extensions::simple::{ExtensionKind, MissingReference};
779 use crate::fixtures::TestContext;
780 use crate::textify::foundation::FormatError;
781
782 #[test]
783 fn test_literal_textify() {
784 let ctx = TestContext::new();
785
786 let literal = LiteralType::Boolean(true);
787 assert_eq!(ctx.textify_no_errors(&literal), "true");
788 }
789
790 #[test]
791 fn test_expression_textify() {
792 let ctx = TestContext::new();
793
794 let expr_empty = Expression { rex_type: None }; let (s, errs) = ctx.textify(&expr_empty);
797 assert!(!errs.is_empty());
798 assert_eq!(s, "!{RexType}");
799
800 let expr_lit = Expression {
802 rex_type: Some(RexType::Literal(expr::Literal {
803 nullable: false,
804 type_variation_reference: 0,
805 literal_type: Some(expr::literal::LiteralType::Boolean(true)),
806 })),
807 };
808 assert_eq!(ctx.textify_no_errors(&expr_lit), "true");
809 }
810
811 #[test]
812 fn test_rextype_textify() {
813 let ctx = TestContext::new();
814
815 let func = RexType::ScalarFunction(ScalarFunction {
816 function_reference: 1000, arguments: vec![],
818 options: vec![],
819 output_type: None,
820 #[allow(deprecated)]
821 args: vec![],
822 });
823 let (s, errq) = ctx.textify(&func);
828 let errs: Vec<_> = errq.0;
829 match errs[0] {
830 FormatError::Lookup(MissingReference::MissingAnchor(k, a)) => {
831 assert_eq!(k, ExtensionKind::Function);
832 assert_eq!(a, 1000);
833 }
834 _ => panic!("Expected Lookup MissingAnchor: {}", errs[0]),
835 }
836 assert_eq!(s, "!{function}#1000()");
837
838 let ctx = ctx.with_uri(1, "first").with_function(1, 100, "first");
839 let func = RexType::ScalarFunction(ScalarFunction {
840 function_reference: 100,
841 arguments: vec![],
842 options: vec![],
843 output_type: None,
844 #[allow(deprecated)]
845 args: vec![],
846 });
847 let s = ctx.textify_no_errors(&func);
848 assert_eq!(s, "first()");
849
850 let options_show_anchor = Default::default();
852
853 let ctx = TestContext::new()
854 .with_options(options_show_anchor)
855 .with_uri(1, "somewhere_on_the_internet")
856 .with_uri(2, "somewhere_else")
857 .with_function(1, 231, "duplicated")
858 .with_function(2, 232, "duplicated");
859
860 let rex_dup = RexType::ScalarFunction(ScalarFunction {
861 function_reference: 231,
862 arguments: vec![FunctionArgument {
863 arg_type: Some(ArgType::Value(Expression {
864 rex_type: Some(RexType::Literal(expr::Literal {
865 nullable: false,
866 type_variation_reference: 0,
867 literal_type: Some(expr::literal::LiteralType::Boolean(true)),
868 })),
869 })),
870 }],
871 options: vec![],
872 output_type: None,
873 #[allow(deprecated)]
874 args: vec![],
875 });
876 let s = ctx.textify_no_errors(&rex_dup);
877 assert_eq!(s, "duplicated#231(true)");
878 }
879}