Skip to main content

perspective_client/virtual_server/
features.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::borrow::Cow;
14
15use indexmap::IndexMap;
16use serde::{Deserialize, Serialize};
17use ts_rs::TS;
18
19use crate::config::{GroupRollupMode, SplitRollupMode};
20use crate::proto::get_features_resp::{AggregateArgs, AggregateOptions, ColumnTypeOptions};
21use crate::proto::{ColumnType, GetFeaturesResp, WindowAggregateArgs};
22
23/// Describes the capabilities supported by a virtual server handler.
24///
25/// This struct is returned by
26/// [`VirtualServerHandler::get_features`](super::VirtualServerHandler::get_features)
27/// to inform clients about which operations are available.
28#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, TS)]
29pub struct Features<'a> {
30    /// Whether group-by aggregation is supported.
31    #[serde(default)]
32    #[ts(optional, as = "Option<_>")]
33    pub group_by: bool,
34
35    /// Which `group_by_rollup_mode` options are supported
36    #[serde(default)]
37    #[ts(optional, as = "Option<_>")]
38    pub group_rollup_mode: Vec<GroupRollupMode>,
39
40    /// Whether split-by (pivot) operations are supported.
41    #[serde(default)]
42    #[ts(optional, as = "Option<_>")]
43    pub split_by: bool,
44
45    /// Which `split_rollup_mode` options are supported. Empty (the default)
46    /// means `["flat"]` - a server must opt in to `"rollup"` explicitly, as
47    /// it requires emitting subtotal and grand-total column groups.
48    #[serde(default)]
49    #[ts(optional, as = "Option<_>")]
50    pub split_rollup_mode: Vec<SplitRollupMode>,
51
52    /// Available filter operators per column type.
53    #[serde(default)]
54    #[ts(optional, as = "Option<_>")]
55    pub filter_ops: IndexMap<ColumnType, Vec<Cow<'a, str>>>,
56
57    /// Available aggregate functions per column type.
58    #[serde(default)]
59    #[ts(optional, as = "Option<_>")]
60    pub aggregates: IndexMap<ColumnType, Vec<AggSpec<'a>>>,
61
62    /// Whether sorting is supported.
63    #[serde(default)]
64    #[ts(optional, as = "Option<_>")]
65    pub sort: bool,
66
67    /// Whether computed expressions are supported.
68    #[serde(default)]
69    #[ts(optional, as = "Option<_>")]
70    pub expressions: bool,
71
72    /// Available window aggregates per column type.
73    #[serde(default)]
74    #[ts(optional, as = "Option<_>")]
75    pub window_aggregates: IndexMap<ColumnType, Vec<WindowAggSpec<'a>>>,
76
77    /// Whether update callbacks are supported.
78    #[serde(default)]
79    #[ts(optional, as = "Option<_>")]
80    pub on_update: bool,
81
82    /// The data store has no reliable natural row order
83    #[serde(default)]
84    #[ts(optional, as = "Option<_>")]
85    pub unordered: bool,
86
87    /// Whether a `Table` can be derived from a `View`, which requires
88    /// [`VirtualServerHandler::view_make_table`](super::VirtualServerHandler::view_make_table).
89    #[serde(default)]
90    #[ts(optional, as = "Option<_>")]
91    pub view_derivations: bool,
92}
93
94/// Specification for a window aggregate.
95#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)]
96pub struct WindowAggSpec<'a> {
97    pub name: Cow<'a, str>,
98
99    /// Frame kinds this aggregate accepts (`rows`, `range`, `cumulative`).
100    /// Empty means it takes no frame at all.
101    #[serde(default)]
102    #[ts(optional, as = "Option<_>")]
103    pub frames: Vec<Cow<'a, str>>,
104
105    /// Takes a row offset, as `lag` / `lead` / `diff` do.
106    #[serde(default)]
107    #[ts(optional, as = "Option<_>")]
108    pub offset: bool,
109
110    /// Takes a smoothing factor, as `ema` does.
111    #[serde(default)]
112    #[ts(optional, as = "Option<_>")]
113    pub alpha: bool,
114
115    /// The output column type. `None` means the source column's type, which
116    /// is what `min` / `max` / `lag` do.
117    #[serde(default)]
118    #[serde(skip_serializing_if = "Option::is_none")]
119    #[ts(optional)]
120    pub result_type: Option<ColumnType>,
121}
122
123impl<'a> From<&'a str> for WindowAggSpec<'a> {
124    /// A bare name, taking no frame and no arguments.
125    fn from(name: &'a str) -> Self {
126        WindowAggSpec {
127            name: Cow::Borrowed(name),
128            frames: vec![],
129            offset: false,
130            alpha: false,
131            result_type: None,
132        }
133    }
134}
135
136/// Specification for an aggregate function.
137///
138/// Aggregates can either take no additional arguments ([`AggSpec::Single`])
139/// or require column type arguments ([`AggSpec::Multiple`]).
140#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)]
141#[serde(untagged)]
142pub enum AggSpec<'a> {
143    /// An aggregate function with no additional arguments.
144    Single(Cow<'a, str>),
145    /// An aggregate function that requires column type arguments.
146    Multiple(Cow<'a, str>, Vec<ColumnType>),
147}
148
149impl<'a> From<Features<'a>> for GetFeaturesResp {
150    fn from(value: Features<'a>) -> GetFeaturesResp {
151        GetFeaturesResp {
152            group_by: value.group_by,
153            group_rollup_mode: value
154                .group_rollup_mode
155                .iter()
156                .map(|x| crate::proto::GroupRollupMode::from(*x) as i32)
157                .collect(),
158            split_rollup_mode: value
159                .split_rollup_mode
160                .iter()
161                .map(|x| crate::proto::SplitRollupMode::from(*x) as i32)
162                .collect(),
163            split_by: value.split_by,
164            expressions: value.expressions,
165            on_update: value.on_update,
166            sort: value.sort,
167            unordered: value.unordered,
168            view_derivations: value.view_derivations,
169            window_aggregates: value
170                .window_aggregates
171                .iter()
172                .map(|(ty, aggs)| {
173                    (
174                        *ty as u32,
175                        crate::proto::get_features_resp::WindowAggregateOptions {
176                            options: aggs
177                                .iter()
178                                .map(|x| WindowAggregateArgs {
179                                    name: x.name.to_string(),
180                                    frames: x.frames.iter().map(|f| f.to_string()).collect(),
181                                    offset: x.offset,
182                                    alpha: x.alpha,
183                                    result_type: x.result_type.map(|t| t as i32),
184                                })
185                                .collect(),
186                        },
187                    )
188                })
189                .collect(),
190            aggregates: value
191                .aggregates
192                .iter()
193                .map(|(dtype, aggs)| {
194                    (*dtype as u32, AggregateOptions {
195                        aggregates: aggs
196                            .iter()
197                            .map(|agg| match agg {
198                                AggSpec::Single(cow) => AggregateArgs {
199                                    name: cow.to_string(),
200                                    args: vec![],
201                                },
202                                AggSpec::Multiple(cow, column_types) => AggregateArgs {
203                                    name: cow.to_string(),
204                                    args: column_types.iter().map(|x| *x as i32).collect(),
205                                },
206                            })
207                            .collect(),
208                    })
209                })
210                .collect(),
211            filter_ops: value
212                .filter_ops
213                .iter()
214                .map(|(ty, options)| {
215                    (*ty as u32, ColumnTypeOptions {
216                        options: options.iter().map(|x| (*x).to_string()).collect(),
217                    })
218                })
219                .collect(),
220        }
221    }
222}