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![
47 column.into(),
48 Expr::Literal(crate::ast::Value::String(delimiter.to_string())),
49 ],
50 )
51}
52
53#[derive(Debug, Clone)]
55pub struct FunctionBuilder {
56 pub(crate) name: String,
57 pub(crate) args: Vec<Expr>,
58 pub(crate) alias: Option<String>,
59}
60
61impl FunctionBuilder {
62 pub fn alias(mut self, name: &str) -> Expr {
64 self.alias = Some(name.to_string());
65 self.build()
66 }
67
68 pub fn build(self) -> Expr {
70 Expr::FunctionCall {
71 name: self.name,
72 args: self.args,
73 alias: self.alias,
74 }
75 }
76}
77
78impl From<FunctionBuilder> for Expr {
79 fn from(builder: FunctionBuilder) -> Self {
80 builder.build()
81 }
82}
83
84pub 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 {
120 let exprs: Vec<Expr> = exprs.into_iter().map(|e| e.into()).collect();
121 ConcatBuilder { exprs, alias: None }
122}
123
124#[derive(Debug, Clone)]
126pub struct ConcatBuilder {
127 pub(crate) exprs: Vec<Expr>,
128 pub(crate) alias: Option<String>,
129}
130
131impl ConcatBuilder {
132 pub fn alias(mut self, name: &str) -> Expr {
134 self.alias = Some(name.to_string());
135 self.build()
136 }
137
138 pub fn build(self) -> Expr {
140 use super::literals::text;
141
142 if self.exprs.is_empty() {
143 return text("");
144 }
145
146 let mut iter = self.exprs.into_iter();
147 let Some(first) = iter.next() else {
148 return text("");
149 };
150
151 let result = iter.fold(first, |acc, expr| Expr::Binary {
152 left: Box::new(acc),
153 op: BinaryOp::Concat,
154 right: Box::new(expr),
155 alias: None,
156 });
157
158 if let Some(alias) = self.alias {
160 match result {
161 Expr::Binary {
162 left, op, right, ..
163 } => Expr::Binary {
164 left,
165 op,
166 right,
167 alias: Some(alias),
168 },
169 other => other,
170 }
171 } else {
172 result
173 }
174 }
175}
176
177impl From<ConcatBuilder> for Expr {
178 fn from(builder: ConcatBuilder) -> Self {
179 builder.build()
180 }
181}