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
12// Re-export commonly used types for easier access
13pub use parser::ParseError;
14use substrait::proto::Plan;
15use textify::foundation::ErrorQueue;
16pub use textify::foundation::{FormatError, OutputOptions, Visibility};
17use textify::plan::PlanWriter;
18
19/// Parse a Substrait plan from text format.
20///
21/// This is the main entry point for parsing well-formed plans.
22/// Returns a clear error if parsing fails.
23///
24/// The input should be in the Substrait text format, which consists of:
25/// - An optional extensions section starting with "=== Extensions"
26/// - A plan section starting with "=== Plan"
27/// - Indented relation definitions
28///
29/// # Example
30/// ```rust
31/// use substrait_explain::parse;
32///
33/// let plan_text = r#"
34/// === Plan
35/// Root[c, d]
36/// Project[$1, 42]
37/// Read[schema.table => a:i64, b:string?]
38/// "#;
39///
40/// // Parse the plan. Builds a complete Substrait plan.
41/// let plan = parse(plan_text).unwrap();
42/// ```
43///
44/// # Errors
45///
46/// Returns a `ParseError` if the input cannot be parsed as a valid Substrait plan.
47/// The error includes details about what went wrong and where in the input.
48///
49/// ```rust
50/// use substrait_explain::parse;
51///
52/// let invalid_plan = r#"
53/// === Plan
54/// InvalidRelation[invalid syntax]
55/// "#;
56///
57/// match parse(invalid_plan) {
58/// Ok(_) => println!("Valid plan"),
59/// Err(e) => println!("Parse error: {}", e),
60/// }
61/// ```
62pub fn parse(input: &str) -> Result<Plan, ParseError> {
63 parser::structural::Parser::parse(input)
64}
65
66/// Format a Substrait plan as human-readable text.
67///
68/// This is the main entry point for formatting plans. It uses default
69/// formatting options that produce concise, readable output.
70///
71/// Returns a tuple of `(formatted_text, errors)`. The text is always generated,
72/// even if there are formatting errors. Errors are collected and returned for
73/// inspection.
74///
75/// # Example
76/// ```rust
77/// use substrait_explain::{parse, format};
78/// use substrait::proto::Plan;
79///
80/// let plan: Plan = parse(r#"
81/// === Plan
82/// Root[result]
83/// Project[$0, $1]
84/// Read[data => a:i64, b:string]
85/// "#).unwrap();
86///
87/// let (text, errors) = format(&plan);
88/// println!("{}", text);
89///
90/// if !errors.is_empty() {
91/// println!("Formatting warnings: {:?}", errors);
92/// }
93/// ```
94///
95/// # Output Format
96///
97/// The output follows the Substrait text format specification, with relations
98/// displayed in a hierarchical structure using indentation.
99pub fn format(plan: &Plan) -> (String, Vec<FormatError>) {
100 let options = OutputOptions::default();
101 format_with_options(plan, &options)
102}
103
104/// Format a Substrait plan with custom options.
105///
106/// This function allows you to customize the formatting behavior, such as
107/// showing more or less detail, changing indentation, or controlling
108/// type visibility.
109///
110/// # Example
111/// ```rust
112/// use substrait_explain::{parse, format_with_options, OutputOptions, Visibility};
113///
114/// let plan = parse(r#"
115/// === Plan
116/// Root[result]
117/// Project[$0, 42]
118/// Read[data => a:i64]
119/// "#).unwrap();
120///
121/// // Use verbose formatting
122/// let verbose_options = OutputOptions::verbose();
123/// let (text, _errors) = format_with_options(&plan, &verbose_options);
124/// println!("Verbose output:\n{}", text);
125///
126/// // Custom options
127/// let custom_options = OutputOptions {
128/// literal_types: Visibility::Always,
129/// indent: " ".to_string(),
130/// ..OutputOptions::default()
131/// };
132/// let (text, _errors) = format_with_options(&plan, &custom_options);
133/// println!("Custom output:\n{}", text);
134/// ```
135///
136/// # Options
137///
138/// See [`OutputOptions`] for all available configuration options.
139pub fn format_with_options(plan: &Plan, options: &OutputOptions) -> (String, Vec<FormatError>) {
140 let (writer, error_queue) = PlanWriter::<ErrorQueue>::new(options, plan);
141 let output = format!("{writer}");
142 let errors = error_queue.into_iter().collect();
143 (output, errors)
144}