Skip to main content

vantage_sql/primitives/
group_concat.rs

1use std::fmt::{Debug, Display};
2use vantage_expressions::{Expression, Expressive, ExpressiveEnum};
3
4/// Cross-database GROUP_CONCAT / STRING_AGG / array_agg aggregation.
5///
6/// Aggregates values from multiple rows into a single string.
7#[derive(Debug, Clone)]
8pub struct GroupConcat<T: Debug + Display + Clone> {
9    expr: Expression<T>,
10    separator: String,
11    distinct: bool,
12}
13
14impl<T: Debug + Display + Clone> GroupConcat<T> {
15    pub fn new(expr: impl Expressive<T>) -> Self {
16        Self {
17            expr: expr.expr(),
18            separator: ",".to_string(),
19            distinct: false,
20        }
21    }
22
23    pub fn separator(mut self, sep: impl Into<String>) -> Self {
24        self.separator = sep.into();
25        self
26    }
27
28    pub fn distinct(mut self) -> Self {
29        self.distinct = true;
30        self
31    }
32}
33
34// SQLite: GROUP_CONCAT(DISTINCT expr, separator)
35#[cfg(feature = "sqlite")]
36impl Expressive<crate::sqlite::types::AnySqliteType>
37    for GroupConcat<crate::sqlite::types::AnySqliteType>
38{
39    fn expr(&self) -> Expression<crate::sqlite::types::AnySqliteType> {
40        let distinct_kw = if self.distinct { "DISTINCT " } else { "" };
41        let template = format!("GROUP_CONCAT({}{{}}, '{}')", distinct_kw, self.separator);
42        Expression::new(&template, vec![ExpressiveEnum::Nested(self.expr.clone())])
43    }
44}
45
46// PostgreSQL: STRING_AGG(DISTINCT expr, separator)
47#[cfg(feature = "postgres")]
48impl Expressive<crate::postgres::types::AnyPostgresType>
49    for GroupConcat<crate::postgres::types::AnyPostgresType>
50{
51    fn expr(&self) -> Expression<crate::postgres::types::AnyPostgresType> {
52        let distinct_kw = if self.distinct { "DISTINCT " } else { "" };
53        let template = format!("STRING_AGG({}{{}}, '{}')", distinct_kw, self.separator);
54        Expression::new(&template, vec![ExpressiveEnum::Nested(self.expr.clone())])
55    }
56}
57
58// MySQL: GROUP_CONCAT(DISTINCT expr SEPARATOR separator)
59#[cfg(feature = "mysql")]
60impl Expressive<crate::mysql::types::AnyMysqlType>
61    for GroupConcat<crate::mysql::types::AnyMysqlType>
62{
63    fn expr(&self) -> Expression<crate::mysql::types::AnyMysqlType> {
64        let distinct_kw = if self.distinct { "DISTINCT " } else { "" };
65        let template = format!(
66            "GROUP_CONCAT({}{{}} SEPARATOR '{}')",
67            distinct_kw, self.separator
68        );
69        Expression::new(&template, vec![ExpressiveEnum::Nested(self.expr.clone())])
70    }
71}