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