substrait_explain/lib.rs
1#![doc = include_str!("../API.md")]
2
3// Used as links in API.md
4#[cfg(doc)]
5pub use extensions::{AnyConvertible, Explainable, ExtensionRegistry};
6
7pub mod extensions;
8pub mod fixtures;
9pub mod grammar;
10pub mod parser;
11pub mod textify;
12
13#[cfg(feature = "cli")]
14pub mod cli;
15#[cfg(feature = "cli")]
16pub mod json;
17
18// Re-export commonly used types for easier access
19pub use parser::ParseError;
20use substrait::proto::Plan;
21use textify::foundation::ErrorQueue;
22pub use textify::foundation::{FormatError, OutputOptions, Visibility};
23use textify::plan::PlanWriter;
24
25/// Parse a Substrait plan from text format.
26///
27/// This is the main entry point for parsing well-formed plans.
28/// Returns a clear error if parsing fails.
29///
30/// The input should be in the Substrait text format, which consists of:
31/// - An optional extensions section starting with "=== Extensions"
32/// - A plan section starting with "=== Plan"
33/// - Indented relation definitions
34///
35/// # Example
36/// ```rust
37/// use substrait_explain::parse;
38///
39/// let plan_text = r#"
40/// === Plan
41/// Root[c, d]
42/// Project[$1, 42]
43/// Read[schema.table => a:i64, b:string?]
44/// "#;
45///
46/// // Parse the plan. Builds a complete Substrait plan.
47/// let plan = parse(plan_text).unwrap();
48/// ```
49///
50/// # Errors
51///
52/// Returns a `ParseError` if the input cannot be parsed as a valid Substrait plan.
53/// The error includes details about what went wrong and where in the input.
54///
55/// ```rust
56/// use substrait_explain::parse;
57///
58/// let invalid_plan = r#"
59/// === Plan
60/// InvalidRelation[invalid syntax]
61/// "#;
62///
63/// match parse(invalid_plan) {
64/// Ok(_) => println!("Valid plan"),
65/// Err(e) => println!("Parse error: {}", e),
66/// }
67/// ```
68pub fn parse(input: &str) -> Result<Plan, ParseError> {
69 parser::Parser::parse(input)
70}
71
72/// Parse a Substrait plan from text format with a custom extension registry.
73///
74/// Use this when the plan contains custom extensions registered via
75/// [`extensions::ExtensionRegistry`]. This is the parsing counterpart to
76/// [`format_with_registry`].
77pub fn parse_with_registry(
78 input: &str,
79 registry: &extensions::ExtensionRegistry,
80) -> Result<Plan, ParseError> {
81 parser::Parser::new()
82 .with_extension_registry(registry.clone())
83 .parse_plan(input)
84}
85
86/// Format a Substrait plan as human-readable text.
87///
88/// This is the main entry point for formatting plans. It uses default
89/// formatting options that produce concise, readable output.
90///
91/// Returns a tuple of `(formatted_text, errors)`. The text is always generated,
92/// even if there are formatting errors. Errors are collected and returned for
93/// inspection.
94///
95/// # Example
96/// ```rust
97/// use substrait_explain::{parse, format};
98/// use substrait::proto::Plan;
99///
100/// let plan: Plan = parse(r#"
101/// === Plan
102/// Root[result]
103/// Project[$0, $1]
104/// Read[data => a:i64, b:string]
105/// "#).unwrap();
106///
107/// let (text, errors) = format(&plan);
108/// println!("{}", text);
109///
110/// if !errors.is_empty() {
111/// println!("Formatting warnings: {:?}", errors);
112/// }
113/// ```
114///
115/// # Output Format
116///
117/// The output follows the Substrait text format specification, with relations
118/// displayed in a hierarchical structure using indentation.
119pub fn format(plan: &Plan) -> (String, Vec<FormatError>) {
120 let options = OutputOptions::default();
121 format_with_options(plan, &options)
122}
123
124/// Format a Substrait plan with custom options.
125///
126/// This function allows you to customize the formatting behavior, such as
127/// showing more or less detail, changing indentation, or controlling
128/// type visibility.
129///
130/// # Example
131/// ```rust
132/// use substrait_explain::{parse, format_with_options, OutputOptions, Visibility};
133///
134/// let plan = parse(r#"
135/// === Plan
136/// Root[result]
137/// Project[$0, 42]
138/// Read[data => a:i64]
139/// "#).unwrap();
140///
141/// // Use verbose formatting
142/// let verbose_options = OutputOptions::verbose();
143/// let (text, _errors) = format_with_options(&plan, &verbose_options);
144/// println!("Verbose output:\n{}", text);
145///
146/// // Custom options
147/// let custom_options = OutputOptions {
148/// literal_types: Visibility::Always,
149/// indent: " ".to_string(),
150/// ..OutputOptions::default()
151/// };
152/// let (text, _errors) = format_with_options(&plan, &custom_options);
153/// println!("Custom output:\n{}", text);
154/// ```
155///
156/// # Options
157///
158/// See [`OutputOptions`] for all available configuration options.
159pub fn format_with_options(plan: &Plan, options: &OutputOptions) -> (String, Vec<FormatError>) {
160 let default_registry = extensions::ExtensionRegistry::default();
161 format_with_registry(plan, options, &default_registry)
162}
163
164/// Format a Substrait plan with custom options and an extension registry.
165///
166/// This function allows you to provide a custom extension registry for handling
167/// ExtensionLeaf, ExtensionSingle, and ExtensionMulti relations.
168///
169/// # Example
170/// ```rust,ignore
171/// use substrait_explain::{parse, format_with_registry, OutputOptions, ExtensionRegistry};
172///
173/// let mut registry = ExtensionRegistry::new();
174/// // Register custom extensions...
175///
176/// let plan = parse("...").unwrap();
177/// let (text, errors) = format_with_registry(&plan, &OutputOptions::default(), ®istry);
178/// ```
179pub fn format_with_registry(
180 plan: &Plan,
181 options: &OutputOptions,
182 registry: &extensions::ExtensionRegistry,
183) -> (String, Vec<FormatError>) {
184 let (writer, error_queue) = PlanWriter::<ErrorQueue>::new(options, plan, registry);
185 let output = format!("{writer}");
186 let errors = error_queue.into_iter().collect();
187 (output, errors)
188}