substrait_explain/textify/
extensions.rs1use std::fmt;
9
10use crate::FormatError;
11use crate::extensions::{Expr, ExtensionArgs, ExtensionColumn, ExtensionValue, TupleValue};
12use crate::textify::foundation::{Scope, Textify};
13use crate::textify::types::{Name, escaped};
14
15impl Textify for TupleValue {
16 fn name() -> &'static str {
17 "TupleValue"
18 }
19
20 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
21 write!(w, "(")?;
22 if self.len() == 1 {
23 self.iter().next().unwrap().textify(ctx, w)?;
24 write!(w, ",")?;
25 } else {
26 write!(w, "{}", ctx.separated(self, ", "))?;
27 }
28 write!(w, ")")
29 }
30}
31
32impl Textify for ExtensionValue {
33 fn name() -> &'static str {
34 "ExtensionValue"
35 }
36
37 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
38 match self {
39 ExtensionValue::String(s) => write!(w, "'{}'", escaped(s)),
40 ExtensionValue::Integer(i) => write!(w, "{i}"),
41 ExtensionValue::Float(f) => write!(w, "{f}"),
42 ExtensionValue::Boolean(b) => write!(w, "{b}"),
43 ExtensionValue::Null => write!(w, "null"),
44 ExtensionValue::Error(error) => {
45 write!(w, "{}", ctx.failure(FormatError::Extension(error.clone())))
46 }
47 ExtensionValue::Expr(expr) => expr.textify(ctx, w),
48 ExtensionValue::Enum(e) => write!(w, "&{e}"),
49 ExtensionValue::Tuple(tv) => tv.textify(ctx, w),
50 }
51 }
52}
53
54impl Textify for Expr {
55 fn name() -> &'static str {
56 "Expr"
57 }
58
59 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
60 write!(w, "{}", ctx.display(self.as_proto()))
61 }
62}
63
64impl Textify for ExtensionColumn {
65 fn name() -> &'static str {
66 "ExtensionColumn"
67 }
68
69 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
70 match self {
71 ExtensionColumn::Named { name, r#type: ty } => {
72 write!(w, "{}:{}", Name(name), ctx.display(ty))
73 }
74 ExtensionColumn::Expr(expr) => expr.textify(ctx, w),
75 }
76 }
77}
78
79impl Textify for ExtensionArgs {
80 fn name() -> &'static str {
81 "ExtensionArgs"
82 }
83
84 fn textify<S: Scope, W: fmt::Write>(&self, ctx: &S, w: &mut W) -> fmt::Result {
85 let mut has_args = false;
86
87 for (i, value) in self.positional.iter().enumerate() {
89 if i > 0 || has_args {
90 write!(w, ", ")?;
91 }
92 value.textify(ctx, w)?;
93 has_args = true;
94 }
95
96 for (name, value) in &self.named {
98 if has_args {
99 write!(w, ", ")?;
100 }
101 write!(w, "{name}=")?;
102 value.textify(ctx, w)?;
103 has_args = true;
104 }
105
106 if !has_args {
107 write!(w, "_")?;
108 }
109
110 if !self.output_columns.is_empty() {
112 write!(w, " => {}", ctx.separated(self.output_columns.iter(), ", "))?;
113 }
114
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::extensions::ExtensionError;
123 use crate::fixtures::TestContext;
124
125 #[test]
126 fn error_value_renders_failure_and_continues() {
127 let mut args = ExtensionArgs::default();
128 args.insert("bad", ExtensionError::Custom("malformed field".to_string()));
129 args.insert("good", "continues");
130
131 let (rendered, errors) = TestContext::new().textify(&args);
132
133 assert_eq!(rendered, "bad=!{extension}, good='continues'");
134 assert!(matches!(
135 errors.0.as_slice(),
136 [FormatError::Extension(ExtensionError::Custom(message))]
137 if message == "malformed field"
138 ));
139 }
140}