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/// Format a Substrait plan as human-readable text.
69///
70/// This is the main entry point for formatting plans. It uses default
71/// formatting options that produce concise, readable output.
72///
73/// Returns a tuple of `(formatted_text, errors)`. The text is always generated,
74/// even if there are formatting errors. Errors are collected and returned for
75/// inspection.
76///
77/// # Example
78/// ```rust
79/// use substrait_explain::{parse, format};
80/// use substrait::proto::Plan;
81///
82/// let plan: Plan = parse(r#"
83/// === Plan
84/// Root[result]
85/// Project[$0, $1]
86/// Read[data => a:i64, b:string]
87/// "#).unwrap();
88///
89/// let (text, errors) = format(&plan);
90/// println!("{}", text);
91///
92/// if !errors.is_empty() {
93/// println!("Formatting warnings: {:?}", errors);
94/// }
95/// ```
96///
97/// # Output Format
98///
99/// The output follows the Substrait text format specification, with relations
100/// displayed in a hierarchical structure using indentation.
101pub fn format(plan: &Plan) -> (String, Vec<FormatError>) {
102 let options = OutputOptions::default();
103 format_with_options(plan, &options)
104}
105
106/// Format a Substrait plan with custom options.
107///
108/// This function allows you to customize the formatting behavior, such as
109/// showing more or less detail, changing indentation, or controlling
110/// type visibility.
111///
112/// # Example
113/// ```rust
114/// use substrait_explain::{parse, format_with_options, OutputOptions, Visibility};
115///
116/// let plan = parse(r#"
117/// === Plan
118/// Root[result]
119/// Project[$0, 42]
120/// Read[data => a:i64]
121/// "#).unwrap();
122///
123/// // Use verbose formatting
124/// let verbose_options = OutputOptions::verbose();
125/// let (text, _errors) = format_with_options(&plan, &verbose_options);
126/// println!("Verbose output:\n{}", text);
127///
128/// // Custom options
129/// let custom_options = OutputOptions {
130/// literal_types: Visibility::Always,
131/// indent: " ".to_string(),
132/// ..OutputOptions::default()
133/// };
134/// let (text, _errors) = format_with_options(&plan, &custom_options);
135/// println!("Custom output:\n{}", text);
136/// ```
137///
138/// # Options
139///
140/// See [`OutputOptions`] for all available configuration options.
141pub fn format_with_options(plan: &Plan, options: &OutputOptions) -> (String, Vec<FormatError>) {
142 let default_registry = extensions::ExtensionRegistry::default();
143 format_with_registry(plan, options, &default_registry)
144}
145
146/// Format a Substrait plan with custom options and an extension registry.
147///
148/// This function allows you to provide a custom extension registry for handling
149/// ExtensionLeaf, ExtensionSingle, and ExtensionMulti relations.
150///
151/// # Example
152/// ```rust,ignore
153/// use substrait_explain::{parse, format_with_registry, OutputOptions, ExtensionRegistry};
154///
155/// let mut registry = ExtensionRegistry::new();
156/// // Register custom extensions...
157///
158/// let plan = parse("...").unwrap();
159/// let (text, errors) = format_with_registry(&plan, &OutputOptions::default(), ®istry);
160/// ```
161pub fn format_with_registry(
162 plan: &Plan,
163 options: &OutputOptions,
164 registry: &extensions::ExtensionRegistry,
165) -> (String, Vec<FormatError>) {
166 let (writer, error_queue) = PlanWriter::<ErrorQueue>::new(options, plan, registry);
167 let output = format!("{writer}");
168 let errors = error_queue.into_iter().collect();
169 (output, errors)
170}