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, Ord, PartialEq, PartialOrd, Serialize, TS)]
191#[serde()]
192pub enum MultiAggregate {
193    #[serde(rename = "weighted mean")]
194    WeightedMean,
195}
196
197impl Display for MultiAggregate {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        match self {
200            MultiAggregate::WeightedMean => write!(f, "weighted mean"),
201        }
202    }
203}
204
205#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
206#[serde(untagged)]
207pub enum Aggregate {
208    SingleAggregate(SingleAggregate),
209    MultiAggregate(MultiAggregate, String),
210}
211
212impl From<&'static str> for Aggregate {
213    fn from(value: &'static str) -> Self {
214        Self::from_str(value).expect("Unknown aggregate")
215    }
216}
217
218impl Display for Aggregate {
219    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
220        match self {
221            Self::SingleAggregate(x) => write!(fmt, "{}", x)?,
222            Self::MultiAggregate(MultiAggregate::WeightedMean, x) => {
223                write!(fmt, "weighted mean by {}", x)?
224            },
225        };
226        Ok(())
227    }
228}
229
230impl FromStr for Aggregate {
231    type Err = String;
232
233    fn from_str(input: &str) -> Result<Self, String> {
234        Ok(
235            if let Some(stripped) = input.strip_prefix("weighted mean by ") {
236                Self::MultiAggregate(MultiAggregate::WeightedMean, stripped.to_owned())
237            } else {
238                Self::SingleAggregate(SingleAggregate::from_str(input)?)
239            },
240        )
241    }
242}
243
244const STRING_AGGREGATES: &[SingleAggregate] = &[
245    SingleAggregate::Any,
246    SingleAggregate::Count,
247    SingleAggregate::DistinctCount,
248    SingleAggregate::Dominant,
249    SingleAggregate::First,
250    SingleAggregate::Join,
251    SingleAggregate::Last,
252    SingleAggregate::LastByIndex,
253    SingleAggregate::Median,
254    SingleAggregate::Q1,
255    SingleAggregate::Q3,
256    SingleAggregate::Unique,
257];
258
259const NUMBER_AGGREGATES: &[SingleAggregate] = &[
260    SingleAggregate::AbsSum,
261    SingleAggregate::Any,
262    SingleAggregate::Avg,
263    SingleAggregate::Count,
264    SingleAggregate::DistinctCount,
265    SingleAggregate::Dominant,
266    SingleAggregate::First,
267    SingleAggregate::High,
268    SingleAggregate::Low,
269    SingleAggregate::Max,
270    SingleAggregate::Min,
271    SingleAggregate::HighMinusLow,
272    SingleAggregate::LastByIndex,
273    SingleAggregate::LastMinusFirst,
274    SingleAggregate::Last,
275    SingleAggregate::Mean,
276    SingleAggregate::Median,
277    SingleAggregate::Q1,
278    SingleAggregate::Q3,
279    SingleAggregate::PctSumParent,
280    SingleAggregate::PctSumGrandTotal,
281    SingleAggregate::StdDev,
282    SingleAggregate::Sum,
283    SingleAggregate::SumAbs,
284    SingleAggregate::SumNotNull,
285    SingleAggregate::Unique,
286    SingleAggregate::Var,
287];
288
289const DATETIME_AGGREGATES: &[SingleAggregate] = &[
290    SingleAggregate::Any,
291    SingleAggregate::Avg,
292    SingleAggregate::Count,
293    SingleAggregate::DistinctCount,
294    SingleAggregate::Dominant,
295    SingleAggregate::First,
296    SingleAggregate::High,
297    SingleAggregate::Low,
298    SingleAggregate::Max,
299    SingleAggregate::Min,
300    SingleAggregate::LastByIndex,
301    SingleAggregate::Last,
302    SingleAggregate::Median,
303    SingleAggregate::Q1,
304    SingleAggregate::Q3,
305    SingleAggregate::Unique,
306];
307
308impl proto::ColumnType {
309    pub fn aggregates_iter(&self) -> Box<dyn Iterator<Item = Aggregate>> {
310        match self {
311            Self::Date | Self::Datetime => Box::new(
312                DATETIME_AGGREGATES
313                    .iter()
314                    .map(|x| Aggregate::SingleAggregate(*x)),
315            ),
316            Self::Boolean | Self::String => Box::new(
317                STRING_AGGREGATES
318                    .iter()
319                    .map(|x| Aggregate::SingleAggregate(*x)),
320            ),
321            Self::Integer | Self::Float => Box::new(
322                NUMBER_AGGREGATES
323                    .iter()
324                    .map(|x| Aggregate::SingleAggregate(*x)),
325            ),
326        }
327    }
328
329    pub const fn default_aggregate(&self) -> Aggregate {
330        match self {
331            Self::Boolean | Self::Date | Self::Datetime | Self::String => {
332                Aggregate::SingleAggregate(SingleAggregate::Count)
333            },
334            Self::Integer | Self::Float => Aggregate::SingleAggregate(SingleAggregate::Sum),
335        }
336    }
337}
338
339impl From<Aggregate> for view_config::AggList {
340    fn from(value: Aggregate) -> Self {
341        view_config::AggList {
342            aggregations: match value {
343                Aggregate::SingleAggregate(x) => vec![format!("{}", x)],
344                Aggregate::MultiAggregate(x, y) => vec![format!("{}", x), format!("{}", y)],
345            },
346        }
347    }
348}
349
350impl From<view_config::AggList> for Aggregate {
351    fn from(value: view_config::AggList) -> Self {
352        Aggregate::SingleAggregate(
353            SingleAggregate::from_str(value.aggregations.first().unwrap()).unwrap(),
354        )
355    }
356}