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