perspective_client/config/
aggregates.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::fmt::Display;
14use std::str::FromStr;
15
16use serde::{Deserialize, Serialize};
17use ts_rs::TS;
18
19use crate::proto;
20use crate::proto::view_config;
21
22#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
23#[serde()]
24pub enum SingleAggregate {
25    #[serde(rename = "sum")]
26    Sum,
27
28    #[serde(rename = "sum abs")]
29    SumAbs,
30
31    #[serde(rename = "sum not null")]
32    SumNotNull,
33
34    #[serde(rename = "abs sum")]
35    AbsSum,
36
37    #[serde(rename = "pct sum parent")]
38    PctSumParent,
39
40    #[serde(rename = "pct sum grand total")]
41    PctSumGrandTotal,
42
43    #[serde(rename = "any")]
44    Any,
45
46    #[serde(rename = "unique")]
47    Unique,
48
49    #[serde(rename = "dominant")]
50    Dominant,
51
52    #[serde(rename = "q1")]
53    Q1,
54
55    #[serde(rename = "q3")]
56    Q3,
57
58    #[serde(rename = "median")]
59    Median,
60
61    #[serde(rename = "first by index")]
62    FirstByIndex,
63
64    #[serde(rename = "first")]
65    First,
66
67    #[serde(rename = "last by index")]
68    LastByIndex,
69
70    #[serde(rename = "last minus first")]
71    LastMinusFirst,
72
73    #[serde(rename = "last")]
74    Last,
75
76    #[serde(rename = "count")]
77    Count,
78
79    #[serde(rename = "distinct count")]
80    DistinctCount,
81
82    #[serde(rename = "avg")]
83    Avg,
84
85    #[serde(rename = "mean")]
86    Mean,
87
88    #[serde(rename = "join")]
89    Join,
90
91    #[serde(rename = "high")]
92    High,
93
94    #[serde(rename = "low")]
95    Low,
96
97    #[serde(rename = "max")]
98    Max,
99
100    #[serde(rename = "min")]
101    Min,
102
103    #[serde(rename = "high minus low")]
104    HighMinusLow,
105
106    #[serde(rename = "stddev")]
107    StdDev,
108
109    #[serde(rename = "var")]
110    Var,
111}
112
113impl Display for SingleAggregate {
114    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
115        let term = match self {
116            Self::Sum => "sum",
117            Self::SumAbs => "sum abs",
118            Self::SumNotNull => "sum not null",
119            Self::AbsSum => "abs sum",
120            Self::PctSumParent => "pct sum parent",
121            Self::PctSumGrandTotal => "pct sum grand total",
122            Self::Any => "any",
123            Self::Unique => "unique",
124            Self::Dominant => "dominant",
125            Self::Median => "median",
126            Self::Q1 => "q1",
127            Self::Q3 => "q3",
128            Self::First => "first",
129            Self::FirstByIndex => "first by index",
130            Self::LastByIndex => "last by index",
131            Self::LastMinusFirst => "last minus first",
132            Self::Last => "last",
133            Self::Count => "count",
134            Self::DistinctCount => "distinct count",
135            Self::Avg => "avg",
136            Self::Mean => "mean",
137            Self::Join => "join",
138            Self::High => "high",
139            Self::Low => "low",
140            Self::HighMinusLow => "high minus low",
141            Self::StdDev => "stddev",
142            Self::Var => "var",
143            Self::Max => "max",
144            Self::Min => "min",
145        };
146
147        write!(fmt, "{}", term)
148    }
149}
150
151impl FromStr for SingleAggregate {
152    type Err = String;
153
154    fn from_str(value: &str) -> Result<Self, String> {
155        match value {
156            "sum" => Ok(Self::Sum),
157            "sum abs" => Ok(Self::SumAbs),
158            "sum not null" => Ok(Self::SumNotNull),
159            "abs sum" => Ok(Self::AbsSum),
160            "pct sum parent" => Ok(Self::PctSumParent),
161            "pct sum grand total" => Ok(Self::PctSumGrandTotal),
162            "any" => Ok(Self::Any),
163            "unique" => Ok(Self::Unique),
164            "dominant" => Ok(Self::Dominant),
165            "median" => Ok(Self::Median),
166            "q1" => Ok(Self::Q1),
167            "q3" => Ok(Self::Q3),
168            "first by index" => Ok(Self::FirstByIndex),
169            "first" => Ok(Self::First),
170            "last by index" => Ok(Self::LastByIndex),
171            "last minus first" => Ok(Self::LastMinusFirst),
172            "last" => Ok(Self::Last),
173            "count" => Ok(Self::Count),
174            "distinct count" => Ok(Self::DistinctCount),
175            "avg" => Ok(Self::Avg),
176            "mean" => Ok(Self::Mean),
177            "join" => Ok(Self::Join),
178            "high" => Ok(Self::High),
179            "low" => Ok(Self::Low),
180            "max" => Ok(Self::Max),
181            "min" => Ok(Self::Min),
182            "high minus low" => Ok(Self::HighMinusLow),
183            "stddev" => Ok(Self::StdDev),
184            "var" => Ok(Self::Var),
185            x => Err(format!("Unknown aggregate `{}`", x)),
186        }
187    }
188}
189
190#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, TS)]
191#[serde()]
192pub enum MultiAggregate {
193    #[serde(rename = "weighted mean")]
194    WeightedMean,
195
196    #[serde(rename = "max by")]
197    MaxBy,
198
199    #[serde(rename = "min by")]
200    MinBy,
201}
202
203impl Display for MultiAggregate {
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        match self {
206            MultiAggregate::WeightedMean => write!(f, "weighted mean"),
207            MultiAggregate::MaxBy => write!(f, "max by"),
208            MultiAggregate::MinBy => write!(f, "min by"),
209        }
210    }
211}
212
213#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
214#[serde(untagged)]
215pub enum Aggregate {
216    SingleAggregate(SingleAggregate),
217    MultiAggregate(MultiAggregate, String),
218}
219
220impl From<&'static str> for Aggregate {
221    fn from(value: &'static str) -> Self {
222        Self::from_str(value).expect("Unknown aggregate")
223    }
224}
225
226impl Display for Aggregate {
227    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
228        match self {
229            Self::SingleAggregate(x) => write!(fmt, "{}", x)?,
230            Self::MultiAggregate(MultiAggregate::WeightedMean, x) => {
231                write!(fmt, "weighted mean by {}", x)?
232            },
233            Self::MultiAggregate(MultiAggregate::MaxBy, x) => write!(fmt, "max by {}", x)?,
234            Self::MultiAggregate(MultiAggregate::MinBy, x) => write!(fmt, "min by {}", x)?,
235        };
236        Ok(())
237    }
238}
239
240impl FromStr for Aggregate {
241    type Err = String;
242
243    fn from_str(input: &str) -> Result<Self, String> {
244        Ok(
245            if let Some(stripped) = input.strip_prefix("weighted mean by ") {
246                Self::MultiAggregate(MultiAggregate::WeightedMean, stripped.to_owned())
247            } else if let Some(stripped) = input.strip_prefix("max by ") {
248                Self::MultiAggregate(MultiAggregate::MaxBy, stripped.to_owned())
249            } else if let Some(stripped) = input.strip_prefix("min by ") {
250                Self::MultiAggregate(MultiAggregate::MinBy, stripped.to_owned())
251            } else {
252                Self::SingleAggregate(SingleAggregate::from_str(input)?)
253            },
254        )
255    }
256}
257
258const STRING_AGGREGATES: &[SingleAggregate] = &[
259    SingleAggregate::Any,
260    SingleAggregate::Count,
261    SingleAggregate::DistinctCount,
262    SingleAggregate::Dominant,
263    SingleAggregate::First,
264    SingleAggregate::Join,
265    SingleAggregate::Last,
266    SingleAggregate::LastByIndex,
267    SingleAggregate::Median,
268    SingleAggregate::Q1,
269    SingleAggregate::Q3,
270    SingleAggregate::Unique,
271];
272
273const NUMBER_AGGREGATES: &[SingleAggregate] = &[
274    SingleAggregate::AbsSum,
275    SingleAggregate::Any,
276    SingleAggregate::Avg,
277    SingleAggregate::Count,
278    SingleAggregate::DistinctCount,
279    SingleAggregate::Dominant,
280    SingleAggregate::First,
281    SingleAggregate::High,
282    SingleAggregate::Low,
283    SingleAggregate::Max,
284    SingleAggregate::Min,
285    SingleAggregate::HighMinusLow,
286    SingleAggregate::LastByIndex,
287    SingleAggregate::LastMinusFirst,
288    SingleAggregate::Last,
289    SingleAggregate::Mean,
290    SingleAggregate::Median,
291    SingleAggregate::Q1,
292    SingleAggregate::Q3,
293    SingleAggregate::PctSumParent,
294    SingleAggregate::PctSumGrandTotal,
295    SingleAggregate::StdDev,
296    SingleAggregate::Sum,
297    SingleAggregate::SumAbs,
298    SingleAggregate::SumNotNull,
299    SingleAggregate::Unique,
300    SingleAggregate::Var,
301];
302
303const DATETIME_AGGREGATES: &[SingleAggregate] = &[
304    SingleAggregate::Any,
305    SingleAggregate::Avg,
306    SingleAggregate::Count,
307    SingleAggregate::DistinctCount,
308    SingleAggregate::Dominant,
309    SingleAggregate::First,
310    SingleAggregate::High,
311    SingleAggregate::Low,
312    SingleAggregate::Max,
313    SingleAggregate::Min,
314    SingleAggregate::LastByIndex,
315    SingleAggregate::Last,
316    SingleAggregate::Median,
317    SingleAggregate::Q1,
318    SingleAggregate::Q3,
319    SingleAggregate::Unique,
320];
321
322impl proto::ColumnType {
323    pub fn aggregates_iter(&self) -> Box<dyn Iterator<Item = Aggregate>> {
324        match self {
325            Self::Date | Self::Datetime => Box::new(
326                DATETIME_AGGREGATES
327                    .iter()
328                    .map(|x| Aggregate::SingleAggregate(*x)),
329            ),
330            Self::Boolean | Self::String => Box::new(
331                STRING_AGGREGATES
332                    .iter()
333                    .map(|x| Aggregate::SingleAggregate(*x)),
334            ),
335            Self::Integer | Self::Float => Box::new(
336                NUMBER_AGGREGATES
337                    .iter()
338                    .map(|x| Aggregate::SingleAggregate(*x)),
339            ),
340        }
341    }
342
343    pub const fn default_aggregate(&self) -> Aggregate {
344        match self {
345            Self::Boolean | Self::Date | Self::Datetime | Self::String => {
346                Aggregate::SingleAggregate(SingleAggregate::Count)
347            },
348            Self::Integer | Self::Float => Aggregate::SingleAggregate(SingleAggregate::Sum),
349        }
350    }
351}
352
353impl From<Aggregate> for view_config::AggList {
354    fn from(value: Aggregate) -> Self {
355        view_config::AggList {
356            aggregations: match value {
357                Aggregate::SingleAggregate(x) => vec![format!("{}", x)],
358                Aggregate::MultiAggregate(x, y) => vec![format!("{}", x), format!("{}", y)],
359            },
360        }
361    }
362}
363
364impl From<view_config::AggList> for Aggregate {
365    fn from(value: view_config::AggList) -> Self {
366        Aggregate::SingleAggregate(
367            SingleAggregate::from_str(value.aggregations.first().unwrap()).unwrap(),
368        )
369    }
370}