From 9f44246a2f6aeb45c6020072ee04e640d1650522 Mon Sep 17 00:00:00 2001 From: Alexey Orlenko Date: Sat, 11 Jan 2025 20:51:40 +0100 Subject: [PATCH] refactor formatting --- query-engine/core/src/compiler/expression.rs | 178 +------------ .../core/src/compiler/expression/format.rs | 244 ++++++++++++++++++ 2 files changed, 248 insertions(+), 174 deletions(-) create mode 100644 query-engine/core/src/compiler/expression/format.rs diff --git a/query-engine/core/src/compiler/expression.rs b/query-engine/core/src/compiler/expression.rs index 0cb342be988..4bca1d7b796 100644 --- a/query-engine/core/src/compiler/expression.rs +++ b/query-engine/core/src/compiler/expression.rs @@ -1,10 +1,8 @@ -use pretty::{ - termcolor::{Color, ColorSpec}, - DocAllocator, DocBuilder, -}; use query_structure::PrismaValue; use serde::Serialize; +mod format; + #[derive(Debug, Serialize)] pub struct Binding { pub name: String, @@ -102,7 +100,8 @@ pub enum PrettyPrintError { impl Expression { pub fn pretty_print(&self, color: bool, width: usize) -> Result { let arena = pretty::Arena::new(); - let doc = self.to_doc(&arena); + let builder = format::PrettyPrinter::new(&arena); + let doc = builder.expression(self); let mut buf = if color { pretty::termcolor::Buffer::ansi() @@ -113,175 +112,6 @@ impl Expression { doc.render_colored(width, &mut buf)?; Ok(String::from_utf8(buf.into_inner())?) } - - fn to_doc<'a, D>(&'a self, d: &'a D) -> DocBuilder<'a, D, ColorSpec> - where - D: DocAllocator<'a, ColorSpec>, - D::Doc: Clone, - { - let color_kw = || ColorSpec::new().set_fg(Some(Color::Blue)).clone(); - let color_fn = || ColorSpec::new().set_underline(true).clone(); - let color_var = || ColorSpec::new().set_bold(true).clone(); - let color_lit = || ColorSpec::new().set_italic(true).set_fg(Some(Color::Green)).clone(); - - let format_query = |tag: &'static str, db_query: &'a DbQuery| { - d.text(tag) - .annotate(color_kw()) - .append(d.softline()) - .append( - d.reflow(&db_query.query) - .align() - .enclose("«", "»") - .annotate(color_lit()), - ) - .append(d.line()) - .append(d.text("with params").annotate(color_kw())) - .append(d.space()) - .append( - d.intersperse( - db_query.params.iter().map(|param| match param { - PrismaValue::Placeholder { name, r#type } => d.text("var").annotate(color_kw()).append( - d.text(name) - .annotate(color_var()) - .append(d.space()) - .append(d.text("as").annotate(color_kw())) - .append(d.space()) - .append(match r#type { - query_structure::PlaceholderType::Array(inner) => format!("{inner:?}[]"), - _ => format!("{type:?}"), - }) - .parens(), - ), - _ => d - .text("const") - .annotate(color_kw()) - .append(d.text(format!("{param:?}")).annotate(color_lit()).parens()), - }), - d.text(",").append(d.softline()), - ) - .align() - .brackets(), - ) - .align() - }; - - let format_function = |name: &'static str, args: &'a [Expression]| { - d.text(name).annotate(color_fn()).append(d.space()).append( - d.intersperse(args.iter().map(|expr| expr.to_doc(d)), d.space()) - .parens(), - ) - }; - - let format_unary_function = |name: &'static str, arg: &'a Expression| { - d.text(name) - .annotate(color_fn()) - .append(d.space()) - .append(arg.to_doc(d).parens()) - }; - - match self { - Expression::Seq(vec) => d.intersperse(vec.iter().map(|expr| expr.to_doc(d)), d.text(";").append(d.line())), - - Expression::Get { name } => d - .text("get") - .annotate(color_kw()) - .append(d.space()) - .append(d.text(name).annotate(color_var())), - - Expression::Let { bindings, expr } => d - .text("let") - .annotate(color_kw()) - .append(d.softline()) - .append( - d.intersperse( - bindings.iter().map(|binding| { - d.text(&binding.name) - .annotate(color_var()) - .append(d.space()) - .append("=") - .append(d.softline()) - .append(binding.expr.to_doc(d)) - }), - d.text(";").append(d.line()), - ) - .align(), - ) - .append(d.line()) - .append(d.text("in").annotate(color_kw())) - .append(d.softline()) - .append(expr.to_doc(d).align()), - - Expression::GetFirstNonEmpty { names } => d - .text("getFirstNonEmpty") - .annotate(color_fn()) - .append(d.intersperse(names.iter().map(|name| d.text(name).annotate(color_var())), d.space())), - - Expression::Query(db_query) => format_query("query", db_query), - - Expression::Execute(db_query) => format_query("execute", db_query), - - Expression::Reverse(expression) => format_unary_function("reverse", expression), - - Expression::Sum(vec) => format_function("sum", vec), - - Expression::Concat(vec) => format_function("concat", vec), - - Expression::Unique(expression) => format_unary_function("unique", expression), - - Expression::Required(expression) => format_unary_function("required", expression), - - Expression::Join { parent, children } => d - .text("join") - .annotate(color_kw()) - .append(d.space()) - .append(parent.to_doc(d).parens()) - .append(d.line()) - .append(d.text("with").annotate(color_kw())) - .append(d.space()) - .append( - d.intersperse( - children.iter().map(|join| { - join.child - .to_doc(d) - .parens() - .append(d.space()) - .append(d.text("on").annotate(color_kw())) - .append(d.space()) - .append(d.intersperse( - join.on.iter().map(|(l, r)| { - d.text("left") - .annotate(color_kw()) - .append(".") - .append(d.text(l).annotate(color_var())) - .parens() - .append(d.space()) - .append("=") - .append(d.space()) - .append( - d.text("right") - .annotate(color_kw()) - .append(".") - .append(d.text(r).annotate(color_var())) - .parens(), - ) - }), - d.text(", "), - )) - }), - d.text(",").append(d.line()), - ) - .align(), - ), - - Expression::MapField { field, records } => d - .text("mapField") - .annotate(color_fn()) - .append(d.space()) - .append(d.text(field).double_quotes().annotate(color_lit())) - .append(d.space()) - .append(records.to_doc(d).parens()), - } - } } impl std::fmt::Display for Expression { diff --git a/query-engine/core/src/compiler/expression/format.rs b/query-engine/core/src/compiler/expression/format.rs new file mode 100644 index 00000000000..dd1df9643c8 --- /dev/null +++ b/query-engine/core/src/compiler/expression/format.rs @@ -0,0 +1,244 @@ +use pretty::{ + termcolor::{Color, ColorSpec}, + DocAllocator, DocBuilder, +}; +use query_structure::PrismaValue; + +use super::{Binding, DbQuery, Expression, JoinExpression}; + +fn color_kw() -> ColorSpec { + ColorSpec::new().set_fg(Some(Color::Blue)).clone() +} + +fn color_fn() -> ColorSpec { + ColorSpec::new().set_underline(true).clone() +} + +fn color_var() -> ColorSpec { + ColorSpec::new().set_bold(true).clone() +} + +fn color_lit() -> ColorSpec { + ColorSpec::new().set_italic(true).set_fg(Some(Color::Green)).clone() +} + +pub(super) struct PrettyPrinter<'a, D> { + allocator: &'a D, +} + +impl<'a, D> PrettyPrinter<'a, D> +where + D: DocAllocator<'a, ColorSpec>, + D::Doc: Clone, +{ + pub fn new(allocator: &'a D) -> Self { + Self { allocator } + } + + pub fn expression(&'a self, expression: &'a Expression) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + match expression { + Expression::Seq(vec) => self.seq(&vec), + Expression::Get { name } => self.get(&name), + Expression::Let { bindings, expr } => self.r#let(&bindings, &expr), + Expression::GetFirstNonEmpty { names } => self.get_first_non_empty(&names), + Expression::Query(db_query) => self.query("query", &db_query), + Expression::Execute(db_query) => self.query("execute", &db_query), + Expression::Reverse(expression) => self.unary_function("reverse", &expression), + Expression::Sum(vec) => self.function("sum", &vec), + Expression::Concat(vec) => self.function("concat", &vec), + Expression::Unique(expression) => self.unary_function("unique", expression), + Expression::Required(expression) => self.unary_function("required", expression), + Expression::Join { parent, children } => self.join(&parent, &children), + Expression::MapField { field, records } => self.map_field(&field, &records), + } + } + + fn keyword(&'a self, keyword: &'static str) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text(keyword).annotate(color_kw()) + } + + fn var_name(&'a self, name: &'a str) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text(name).annotate(color_var()) + } + + fn query(&'a self, tag: &'static str, db_query: &'a DbQuery) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.keyword(tag) + .append(self.softline()) + .append( + self.reflow(&db_query.query) + .align() + .enclose("«", "»") + .annotate(color_lit()), + ) + .append(self.line()) + .append(self.keyword("with params")) + .append(self.space()) + .append(self.list(&db_query.params)) + .align() + } + + fn list(&'a self, values: &'a [PrismaValue]) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.intersperse( + values.iter().map(|value| self.value(value)), + self.text(",").append(self.softline()), + ) + .align() + .brackets() + } + + fn value(&'a self, value: &'a PrismaValue) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + match value { + PrismaValue::Placeholder { name, r#type } => self.keyword("var").append( + self.var_name(name) + .append(self.space()) + .append(self.keyword("as")) + .append(self.space()) + .append(match r#type { + query_structure::PlaceholderType::Array(inner) => format!("{inner:?}[]"), + _ => format!("{type:?}"), + }) + .parens(), + ), + PrismaValue::List(values) => self.list(&values), + _ => self + .keyword("const") + .append(self.text(format!("{value:?}")).annotate(color_lit()).parens()), + } + } + + fn function( + &'a self, + name: &'static str, + args: &'a [Expression], + ) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text(name).annotate(color_fn()).append(self.space()).append( + self.intersperse(args.iter().map(|expr| self.expression(expr)), self.space()) + .parens(), + ) + } + + fn unary_function( + &'a self, + name: &'static str, + arg: &'a Expression, + ) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text(name) + .annotate(color_fn()) + .append(self.space()) + .append(self.expression(arg).parens()) + } + + fn seq(&'a self, vec: &'a [Expression]) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.intersperse( + vec.iter().map(|expr| self.expression(expr)), + self.text(";").append(self.line()), + ) + } + + fn get(&'a self, name: &'a str) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.keyword("get").append(self.space()).append(self.var_name(name)) + } + + fn r#let( + &'a self, + bindings: &'a [Binding], + expr: &'a Expression, + ) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.keyword("let") + .append(self.softline()) + .append( + self.intersperse( + bindings.iter().map(|binding| { + self.var_name(&binding.name) + .append(self.space()) + .append("=") + .append(self.softline()) + .append(self.expression(&binding.expr)) + }), + self.text(";").append(self.line()), + ) + .align(), + ) + .append(self.line()) + .append(self.keyword("in")) + .append(self.softline()) + .append(self.expression(expr).align()) + } + + fn get_first_non_empty(&'a self, names: &'a [String]) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text("getFirstNonEmpty") + .annotate(color_fn()) + .append(self.intersperse(names.iter().map(|name| self.var_name(name)), self.space())) + } + + fn join( + &'a self, + parent: &'a Expression, + children: &'a [JoinExpression], + ) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.keyword("join") + .append(self.space()) + .append(self.expression(parent).parens()) + .append(self.line()) + .append(self.keyword("with")) + .append(self.space()) + .append( + self.intersperse( + children.iter().map(|join| { + self.expression(&join.child) + .parens() + .append(self.space()) + .append(self.keyword("on")) + .append(self.space()) + .append(self.intersperse( + join.on.iter().map(|(l, r)| { + self.keyword("left") + .append(".") + .append(self.text(l)) + .parens() + .append(self.space()) + .append("=") + .append(self.space()) + .append(self.keyword("right").append(".").append(self.text(r)).parens()) + }), + self.text(", "), + )) + }), + self.text(",").append(self.line()), + ) + .align(), + ) + } + + fn map_field(&'a self, field: &'a str, records: &'a Expression) -> DocBuilder<'a, PrettyPrinter<'a, D>, ColorSpec> { + self.text("mapField") + .annotate(color_fn()) + .append(self.space()) + .append(self.text(field).double_quotes().annotate(color_lit())) + .append(self.space()) + .append(self.expression(records).parens()) + } +} + +impl<'a, D, A> DocAllocator<'a, A> for PrettyPrinter<'a, D> +where + D: DocAllocator<'a, A>, + A: 'a, +{ + type Doc = D::Doc; + + fn alloc(&'a self, doc: pretty::Doc<'a, Self::Doc, A>) -> Self::Doc { + self.allocator.alloc(doc) + } + + fn alloc_column_fn( + &'a self, + f: impl Fn(usize) -> Self::Doc + 'a, + ) -> >::ColumnFn { + self.allocator.alloc_column_fn(f) + } + + fn alloc_width_fn(&'a self, f: impl Fn(isize) -> Self::Doc + 'a) -> >::WidthFn { + self.allocator.alloc_width_fn(f) + } +}