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