use std::fmt::{self, Write};
use rudb_plan::{Expr, ExprRef, Plan};
use crate::schema::Schema;
#[must_use]
pub fn written(plan: &Plan, expr: ExprRef, schema: &Schema) -> String {
let mut out = String::new();
let _ = form(plan, &mut out, expr, schema);
out
}
fn form<W: Write>(plan: &Plan, out: &mut W, expr: ExprRef, schema: &Schema) -> fmt::Result {
match *plan.expr(expr) {
Expr::Column(binding) => match schema.position_of(binding) {
Some(position) => out.write_str(&schema.fields()[position].name),
None => write!(out, "#{}.{}", binding.table, binding.column),
},
Expr::Constant(value) => write!(out, "{}", plan.value(value)),
Expr::Cast { input, try_cast } => {
out.write_str(if try_cast { "TRY_CAST(" } else { "CAST(" })?;
form(plan, out, input, schema)?;
write!(out, " AS {})", plan.expr_type(expr))
}
Expr::Compare { op, left, right } => {
out.write_char('(')?;
form(plan, out, left, schema)?;
write!(out, " {} ", op.symbol())?;
form(plan, out, right, schema)?;
out.write_char(')')
}
Expr::Conjunction { op, children } => {
out.write_char('(')?;
for (position, &child) in plan.expr_list(children).iter().enumerate() {
if position > 0 {
write!(out, " {} ", op.keyword())?;
}
form(plan, out, child, schema)?;
}
out.write_char(')')
}
Expr::Function { name, args } | Expr::Aggregate { name, args, .. } => {
call(plan, out, plan.string(name), args, schema)
}
Expr::Case { arms, otherwise } => {
out.write_str("CASE")?;
for arm in plan.arm_list(arms) {
out.write_str(" WHEN ")?;
form(plan, out, arm.when, schema)?;
out.write_str(" THEN ")?;
form(plan, out, arm.then, schema)?;
}
if let Some(otherwise) = otherwise {
out.write_str(" ELSE ")?;
form(plan, out, otherwise, schema)?;
}
out.write_str(" END")
}
}
}
fn call<W: Write>(
plan: &Plan,
out: &mut W,
name: &str,
args: rudb_plan::Slice,
schema: &Schema,
) -> fmt::Result {
let operator = !name.starts_with(|first: char| first.is_alphabetic() || first == '_');
let args = plan.expr_list(args);
match (operator, args) {
(true, [left, right]) => {
out.write_char('(')?;
form(plan, out, *left, schema)?;
write!(out, " {name} ")?;
form(plan, out, *right, schema)?;
out.write_char(')')
}
_ => {
write!(out, "{name}(")?;
for (position, &arg) in args.iter().enumerate() {
if position > 0 {
out.write_str(", ")?;
}
form(plan, out, arg, schema)?;
}
out.write_char(')')
}
}
}