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(
32 source: impl Into<Expr>,
33 from: impl Into<Expr>,
34 to: impl Into<Expr>,
35) -> FunctionBuilder {
36 func("REPLACE", vec![source.into(), from.into(), to.into()])
37}
38
39pub fn string_agg(column: impl Into<Expr>, delimiter: &str) -> FunctionBuilder {
46 func(
47 "STRING_AGG",
48 vec![column.into(), Expr::Literal(crate::ast::Value::String(delimiter.to_string()))],
49 )
50}
51
52#[derive(Debug, Clone)]
54pub struct FunctionBuilder {
55 pub(crate) name: String,
56 pub(crate) args: Vec<Expr>,
57 pub(crate) alias: Option<String>,
58}
59
60impl FunctionBuilder {
61 pub fn alias(mut self, name: &str) -> Expr {
63 self.alias = Some(name.to_string());
64 self.build()
65 }
66
67 pub fn build(self) -> Expr {
69 Expr::FunctionCall {
70 name: self.name,
71 args: self.args,
72 alias: self.alias,
73 }
74 }
75}
76
77impl From<FunctionBuilder> for Expr {
78 fn from(builder: FunctionBuilder) -> Self {
79 builder.build()
80 }
81}
82
83pub fn substring(source: impl Into<Expr>, from: i32) -> Expr {
91 Expr::SpecialFunction {
92 name: "SUBSTRING".to_string(),
93 args: vec![
94 (None, Box::new(source.into())),
95 (Some("FROM".to_string()), Box::new(int(from as i64))),
96 ],
97 alias: None,
98 }
99}
100
101pub fn substring_for(source: impl Into<Expr>, from: i32, length: i32) -> Expr {
103 Expr::SpecialFunction {
104 name: "SUBSTRING".to_string(),
105 args: vec![
106 (None, Box::new(source.into())),
107 (Some("FROM".to_string()), Box::new(int(from as i64))),
108 (Some("FOR".to_string()), Box::new(int(length as i64))),
109 ],
110 alias: None,
111 }
112}
113
114pub fn concat<E: Into<Expr>>(exprs: impl IntoIterator<Item = E>) -> ConcatBuilder {
121 let exprs: Vec<Expr> = exprs.into_iter().map(|e| e.into()).collect();
122 ConcatBuilder { exprs, alias: None }
123}
124
125#[derive(Debug, Clone)]
127pub struct ConcatBuilder {
128 pub(crate) exprs: Vec<Expr>,
129 pub(crate) alias: Option<String>,
130}
131
132impl ConcatBuilder {
133 pub fn alias(mut self, name: &str) -> Expr {
135 self.alias = Some(name.to_string());
136 self.build()
137 }
138
139 pub fn build(self) -> Expr {
141 use super::literals::text;
142
143 if self.exprs.is_empty() {
144 return text("");
145 }
146
147 let mut iter = self.exprs.into_iter();
148 let first = iter.next().unwrap();
149
150 let result = iter.fold(first, |acc, expr| Expr::Binary {
151 left: Box::new(acc),
152 op: BinaryOp::Concat,
153 right: Box::new(expr),
154 alias: None,
155 });
156
157 if let Some(alias) = self.alias {
159 match result {
160 Expr::Binary {
161 left, op, right, ..
162 } => Expr::Binary {
163 left,
164 op,
165 right,
166 alias: Some(alias),
167 },
168 other => other,
169 }
170 } else {
171 result
172 }
173 }
174}
175
176impl From<ConcatBuilder> for Expr {
177 fn from(builder: ConcatBuilder) -> Self {
178 builder.build()
179 }
180}