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}