qail_core/ast/builders/
functions.rs1use super::literals::int;
4use crate::ast::{BinaryOp, Expr};
5
6pub fn func(name: &str, args: Vec<Expr>) -> FunctionBuilder {
8 FunctionBuilder {
9 name: name.to_string(),
10 args,
11 alias: None,
12 }
13}
14
15pub fn coalesce<E: Into<Expr>>(args: impl IntoIterator<Item = E>) -> FunctionBuilder {
17 func("COALESCE", args.into_iter().map(|e| e.into()).collect())
18}
19
20pub fn nullif(a: impl Into<Expr>, b: impl Into<Expr>) -> FunctionBuilder {
22 func("NULLIF", vec![a.into(), b.into()])
23}
24
25pub fn replace(
31 source: impl Into<Expr>,
32 from: impl Into<Expr>,
33 to: impl Into<Expr>,
34) -> FunctionBuilder {
35 func("REPLACE", vec![source.into(), from.into(), to.into()])
36}
37
38pub fn string_agg(column: impl Into<Expr>, delimiter: &str) -> FunctionBuilder {
44 func(
45 "STRING_AGG",
46 vec![column.into(), Expr::Literal(crate::ast::Value::String(delimiter.to_string()))],
47 )
48}
49
50#[derive(Debug, Clone)]
52pub struct FunctionBuilder {
53 pub(crate) name: String,
54 pub(crate) args: Vec<Expr>,
55 pub(crate) alias: Option<String>,
56}
57
58impl FunctionBuilder {
59 pub fn alias(mut self, name: &str) -> Expr {
61 self.alias = Some(name.to_string());
62 self.build()
63 }
64
65 pub fn build(self) -> Expr {
67 Expr::FunctionCall {
68 name: self.name,
69 args: self.args,
70 alias: self.alias,
71 }
72 }
73}
74
75impl From<FunctionBuilder> for Expr {
76 fn from(builder: FunctionBuilder) -> Self {
77 builder.build()
78 }
79}
80
81pub fn substring(source: impl Into<Expr>, from: i32) -> Expr {
88 Expr::SpecialFunction {
89 name: "SUBSTRING".to_string(),
90 args: vec![
91 (None, Box::new(source.into())),
92 (Some("FROM".to_string()), Box::new(int(from as i64))),
93 ],
94 alias: None,
95 }
96}
97
98pub fn substring_for(source: impl Into<Expr>, from: i32, length: i32) -> Expr {
100 Expr::SpecialFunction {
101 name: "SUBSTRING".to_string(),
102 args: vec![
103 (None, Box::new(source.into())),
104 (Some("FROM".to_string()), Box::new(int(from as i64))),
105 (Some("FOR".to_string()), Box::new(int(length as i64))),
106 ],
107 alias: None,
108 }
109}
110
111pub fn concat<E: Into<Expr>>(exprs: impl IntoIterator<Item = E>) -> ConcatBuilder {
117 let exprs: Vec<Expr> = exprs.into_iter().map(|e| e.into()).collect();
118 ConcatBuilder { exprs, alias: None }
119}
120
121#[derive(Debug, Clone)]
123pub struct ConcatBuilder {
124 pub(crate) exprs: Vec<Expr>,
125 pub(crate) alias: Option<String>,
126}
127
128impl ConcatBuilder {
129 pub fn alias(mut self, name: &str) -> Expr {
131 self.alias = Some(name.to_string());
132 self.build()
133 }
134
135 pub fn build(self) -> Expr {
137 use super::literals::text;
138
139 if self.exprs.is_empty() {
140 return text("");
141 }
142
143 let mut iter = self.exprs.into_iter();
144 let first = iter.next().unwrap();
145
146 let result = iter.fold(first, |acc, expr| Expr::Binary {
147 left: Box::new(acc),
148 op: BinaryOp::Concat,
149 right: Box::new(expr),
150 alias: None,
151 });
152
153 if let Some(alias) = self.alias {
155 match result {
156 Expr::Binary {
157 left, op, right, ..
158 } => Expr::Binary {
159 left,
160 op,
161 right,
162 alias: Some(alias),
163 },
164 other => other,
165 }
166 } else {
167 result
168 }
169 }
170}
171
172impl From<ConcatBuilder> for Expr {
173 fn from(builder: ConcatBuilder) -> Self {
174 builder.build()
175 }
176}