1use std::fmt::{Result, Write};
2use crate::ast::{QailCmd, Column, Join, Cage, CageKind, Condition, Operator, Value, LogicalOp, SortOrder, Action};
3
4#[cfg(test)]
5mod tests;
6
7pub struct Formatter {
8 indent_level: usize,
9 buffer: String,
10}
11
12impl Formatter {
13 pub fn new() -> Self {
14 Self {
15 indent_level: 0,
16 buffer: String::new(),
17 }
18 }
19
20 pub fn format(mut self, cmd: &QailCmd) -> std::result::Result<String, std::fmt::Error> {
21 self.visit_cmd(cmd)?;
22 Ok(self.buffer)
23 }
24
25 fn indent(&mut self) -> Result {
26 for _ in 0..self.indent_level {
27 write!(self.buffer, " ")?;
28 }
29 Ok(())
30 }
31
32 fn visit_cmd(&mut self, cmd: &QailCmd) -> Result {
33 for cte in &cmd.ctes {
35 write!(self.buffer, "with {} = ", cte.name)?;
36 self.indent_level += 1;
37 writeln!(self.buffer)?;
38 self.indent()?;
39 self.visit_cmd(&cte.base_query)?;
40
41 if cte.recursive {
43 if let Some(ref recursive_query) = cte.recursive_query {
44 writeln!(self.buffer)?;
45 self.indent()?;
46 writeln!(self.buffer, "union all")?;
47 self.indent()?;
48 self.visit_cmd(recursive_query)?;
49 }
50 }
51
52 self.indent_level -= 1;
53 writeln!(self.buffer)?;
54 }
55
56 match cmd.action {
58 Action::Get => write!(self.buffer, "get {}", cmd.table)?,
59 Action::Set => write!(self.buffer, "set {}", cmd.table)?,
60 Action::Del => write!(self.buffer, "del {}", cmd.table)?,
61 Action::Add => write!(self.buffer, "add {}", cmd.table)?,
62 _ => write!(self.buffer, "{} {}", cmd.action, cmd.table)?, }
64 writeln!(self.buffer)?;
65
66 if !cmd.columns.is_empty() {
81 if !(cmd.columns.len() == 1 && matches!(cmd.columns[0], Column::Star)) {
86 self.indent()?;
87 writeln!(self.buffer, "fields")?;
88 self.indent_level += 1;
89 for (i, col) in cmd.columns.iter().enumerate() {
90 self.indent()?;
91 self.format_column(col)?;
92 if i < cmd.columns.len() - 1 {
93 writeln!(self.buffer, ",")?;
94 } else {
95 writeln!(self.buffer)?;
96 }
97 }
98 self.indent_level -= 1;
99 }
100 }
101
102 for join in &cmd.joins {
104 self.indent()?;
105 self.format_join(join)?;
106 writeln!(self.buffer)?;
107 }
108
109 let filters: Vec<&Cage> = cmd.cages.iter().filter(|c| matches!(c.kind, CageKind::Filter)).collect();
111 if !filters.is_empty() {
112 self.indent()?;
115 write!(self.buffer, "where ")?;
116 for (i, cage) in filters.iter().enumerate() {
117 if i > 0 {
118 write!(self.buffer, " and ")?; }
120 self.format_conditions(&cage.conditions, cage.logical_op)?;
121 }
122 writeln!(self.buffer)?;
123 }
124
125 let sorts: Vec<&Cage> = cmd.cages.iter().filter(|c| matches!(c.kind, CageKind::Sort(_))).collect();
127 if !sorts.is_empty() {
128 self.indent()?;
129 writeln!(self.buffer, "order by")?;
130 self.indent_level += 1;
131 for (i, cage) in sorts.iter().enumerate() {
132 if let CageKind::Sort(order) = cage.kind {
133 for (j, cond) in cage.conditions.iter().enumerate() {
134 self.indent()?;
135 write!(self.buffer, "{}", cond.column)?;
136 self.format_sort_order(order)?;
137 if i < sorts.len() - 1 || j < cage.conditions.len() - 1 {
138 writeln!(self.buffer, ",")?;
139 } else {
140 writeln!(self.buffer)?;
141 }
142 }
143 }
144 }
145 self.indent_level -= 1;
146 }
147
148 for cage in &cmd.cages {
150 match cage.kind {
151 CageKind::Limit(n) => {
152 self.indent()?;
153 writeln!(self.buffer, "limit {}", n)?;
154 },
155 CageKind::Offset(n) => {
156 self.indent()?;
157 writeln!(self.buffer, "offset {}", n)?;
158 },
159 _ => {}
160 }
161 }
162
163
164 Ok(())
166 }
167
168 fn format_column(&mut self, col: &Column) -> Result {
169 match col {
170 Column::Star => write!(self.buffer, "*")?,
171 Column::Named(name) => write!(self.buffer, "{}", name)?,
172 Column::Aliased { name, alias } => write!(self.buffer, "{} as {}", name, alias)?,
173 Column::Aggregate { col, func } => {
174 let func_name = match func {
175 crate::ast::AggregateFunc::Count => "count",
176 crate::ast::AggregateFunc::Sum => "sum",
177 crate::ast::AggregateFunc::Avg => "avg",
178 crate::ast::AggregateFunc::Min => "min",
179 crate::ast::AggregateFunc::Max => "max",
180 };
181 write!(self.buffer, "{}({})", func_name, col)?
182 },
183 Column::FunctionCall { name, args, alias } => {
184 write!(self.buffer, "{}({})", name, args.join(", "))?;
185 if let Some(a) = alias {
186 write!(self.buffer, " as {}", a)?;
187 }
188 }
189 _ => write!(self.buffer, "/* TODO: {:?} */", col)?,
191 }
192 Ok(())
193 }
194
195 fn format_join(&mut self, join: &Join) -> Result {
196 match join.kind {
197 crate::ast::JoinKind::Inner => write!(self.buffer, "join {}", join.table)?,
198 crate::ast::JoinKind::Left => write!(self.buffer, "left join {}", join.table)?,
199 crate::ast::JoinKind::Right => write!(self.buffer, "right join {}", join.table)?,
200 crate::ast::JoinKind::Full => write!(self.buffer, "full join {}", join.table)?,
201 crate::ast::JoinKind::Cross => write!(self.buffer, "cross join {}", join.table)?,
202 crate::ast::JoinKind::Lateral => write!(self.buffer, "lateral join {}", join.table)?,
203 }
204
205 if let Some(conditions) = &join.on {
206 if !conditions.is_empty() {
207 write!(self.buffer, "\n")?;
208 self.indent_level += 1;
209 self.indent()?;
210 write!(self.buffer, "on ")?;
211 self.format_conditions(conditions, LogicalOp::And)?;
212 self.indent_level -= 1;
213 }
214 }
215 Ok(())
216 }
217
218 fn format_conditions(&mut self, conditions: &[Condition], logical_op: LogicalOp) -> Result {
219 for (i, cond) in conditions.iter().enumerate() {
220 if i > 0 {
221 match logical_op {
222 LogicalOp::And => write!(self.buffer, " and ")?,
223 LogicalOp::Or => write!(self.buffer, " or ")?,
224 }
225 }
226
227 write!(self.buffer, "{}", cond.column)?;
228
229 match cond.op {
230 Operator::Eq => write!(self.buffer, " = ")?,
231 Operator::Ne => write!(self.buffer, " != ")?,
232 Operator::Gt => write!(self.buffer, " > ")?,
233 Operator::Gte => write!(self.buffer, " >= ")?,
234 Operator::Lt => write!(self.buffer, " < ")?,
235 Operator::Lte => write!(self.buffer, " <= ")?,
236 Operator::Fuzzy => write!(self.buffer, " ~ ")?, Operator::In => write!(self.buffer, " in ")?,
238 Operator::NotIn => write!(self.buffer, " not in ")?,
239 Operator::IsNull => write!(self.buffer, " is null")?,
240 Operator::IsNotNull => write!(self.buffer, " is not null")?,
241 Operator::Contains => write!(self.buffer, " @> ")?,
242 Operator::KeyExists => write!(self.buffer, " ? ")?,
243 _ => write!(self.buffer, " {:?} ", cond.op)?,
244 }
245
246 if !matches!(cond.op, Operator::IsNull | Operator::IsNotNull) {
248 self.format_value(&cond.value)?;
249 }
250 }
251 Ok(())
252 }
253
254 fn format_value(&mut self, val: &Value) -> Result {
255 match val {
256 Value::Null => write!(self.buffer, "null")?,
257 Value::Bool(b) => write!(self.buffer, "{}", b)?,
258 Value::Int(n) => write!(self.buffer, "{}", n)?,
259 Value::Float(n) => write!(self.buffer, "{}", n)?,
260 Value::Param(n) => write!(self.buffer, "${}", n)?,
261 Value::Function(f) => write!(self.buffer, "{}", f)?,
262 Value::Column(c) => write!(self.buffer, "{}", c)?,
263 Value::String(s) => write!(self.buffer, "'{}'", s)?, Value::Array(arr) => {
268 write!(self.buffer, "[")?;
269 for (i, v) in arr.iter().enumerate() {
270 if i > 0 { write!(self.buffer, ", ")?; }
271 self.format_value(v)?;
272 }
273 write!(self.buffer, "]")?;
274 }
275 _ => write!(self.buffer, "{:?}", val)?,
277 }
278 Ok(())
279 }
280
281 fn format_sort_order(&mut self, order: SortOrder) -> Result {
282 match order {
283 SortOrder::Asc => {},
284 SortOrder::Desc => write!(self.buffer, " desc")?,
285 SortOrder::AscNullsFirst => write!(self.buffer, " nulls first")?,
286 SortOrder::AscNullsLast => write!(self.buffer, " nulls last")?,
287 SortOrder::DescNullsFirst => write!(self.buffer, " desc nulls first")?,
288 SortOrder::DescNullsLast => write!(self.buffer, " desc nulls last")?,
289 }
290 Ok(())
291 }
292}