perspective_client/virtual_server/
features.rs1use 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#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, TS)]
29pub struct Features<'a> {
30 #[serde(default)]
32 #[ts(optional, as = "Option<_>")]
33 pub group_by: bool,
34
35 #[serde(default)]
37 #[ts(optional, as = "Option<_>")]
38 pub group_rollup_mode: Vec<GroupRollupMode>,
39
40 #[serde(default)]
42 #[ts(optional, as = "Option<_>")]
43 pub split_by: bool,
44
45 #[serde(default)]
49 #[ts(optional, as = "Option<_>")]
50 pub split_rollup_mode: Vec<SplitRollupMode>,
51
52 #[serde(default)]
54 #[ts(optional, as = "Option<_>")]
55 pub filter_ops: IndexMap<ColumnType, Vec<Cow<'a, str>>>,
56
57 #[serde(default)]
59 #[ts(optional, as = "Option<_>")]
60 pub aggregates: IndexMap<ColumnType, Vec<AggSpec<'a>>>,
61
62 #[serde(default)]
64 #[ts(optional, as = "Option<_>")]
65 pub sort: bool,
66
67 #[serde(default)]
69 #[ts(optional, as = "Option<_>")]
70 pub expressions: bool,
71
72 #[serde(default)]
74 #[ts(optional, as = "Option<_>")]
75 pub window_aggregates: IndexMap<ColumnType, Vec<WindowAggSpec<'a>>>,
76
77 #[serde(default)]
79 #[ts(optional, as = "Option<_>")]
80 pub on_update: bool,
81
82 #[serde(default)]
84 #[ts(optional, as = "Option<_>")]
85 pub unordered: bool,
86
87 #[serde(default)]
90 #[ts(optional, as = "Option<_>")]
91 pub view_derivations: bool,
92
93 #[serde(default)]
97 #[ts(optional, as = "Option<_>")]
98 pub expand_collapse: bool,
99
100 #[serde(default)]
102 #[ts(optional, as = "Option<_>")]
103 pub update: bool,
104}
105
106#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)]
108pub struct WindowAggSpec<'a> {
109 pub name: Cow<'a, str>,
110
111 #[serde(default)]
114 #[ts(optional, as = "Option<_>")]
115 pub frames: Vec<Cow<'a, str>>,
116
117 #[serde(default)]
119 #[ts(optional, as = "Option<_>")]
120 pub offset: bool,
121
122 #[serde(default)]
124 #[ts(optional, as = "Option<_>")]
125 pub alpha: bool,
126
127 #[serde(default)]
130 #[serde(skip_serializing_if = "Option::is_none")]
131 #[ts(optional)]
132 pub result_type: Option<ColumnType>,
133}
134
135impl<'a> From<&'a str> for WindowAggSpec<'a> {
136 fn from(name: &'a str) -> Self {
138 WindowAggSpec {
139 name: Cow::Borrowed(name),
140 frames: vec![],
141 offset: false,
142 alpha: false,
143 result_type: None,
144 }
145 }
146}
147
148#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, TS)]
153#[serde(untagged)]
154pub enum AggSpec<'a> {
155 Single(Cow<'a, str>),
157 Multiple(Cow<'a, str>, Vec<ColumnType>),
159}
160
161impl<'a> From<Features<'a>> for GetFeaturesResp {
162 fn from(value: Features<'a>) -> GetFeaturesResp {
163 GetFeaturesResp {
164 group_by: value.group_by,
165 group_rollup_mode: value
166 .group_rollup_mode
167 .iter()
168 .map(|x| crate::proto::GroupRollupMode::from(*x) as i32)
169 .collect(),
170 split_rollup_mode: value
171 .split_rollup_mode
172 .iter()
173 .map(|x| crate::proto::SplitRollupMode::from(*x) as i32)
174 .collect(),
175 split_by: value.split_by,
176 expressions: value.expressions,
177 on_update: value.on_update,
178 sort: value.sort,
179 unordered: value.unordered,
180 view_derivations: value.view_derivations,
181 expand_collapse: value.expand_collapse,
182 update: value.update,
183 window_aggregates: value
184 .window_aggregates
185 .iter()
186 .map(|(ty, aggs)| {
187 (
188 *ty as u32,
189 crate::proto::get_features_resp::WindowAggregateOptions {
190 options: aggs
191 .iter()
192 .map(|x| WindowAggregateArgs {
193 name: x.name.to_string(),
194 frames: x.frames.iter().map(|f| f.to_string()).collect(),
195 offset: x.offset,
196 alpha: x.alpha,
197 result_type: x.result_type.map(|t| t as i32),
198 })
199 .collect(),
200 },
201 )
202 })
203 .collect(),
204 aggregates: value
205 .aggregates
206 .iter()
207 .map(|(dtype, aggs)| {
208 (*dtype as u32, AggregateOptions {
209 aggregates: aggs
210 .iter()
211 .map(|agg| match agg {
212 AggSpec::Single(cow) => AggregateArgs {
213 name: cow.to_string(),
214 args: vec![],
215 },
216 AggSpec::Multiple(cow, column_types) => AggregateArgs {
217 name: cow.to_string(),
218 args: column_types.iter().map(|x| *x as i32).collect(),
219 },
220 })
221 .collect(),
222 })
223 })
224 .collect(),
225 filter_ops: value
226 .filter_ops
227 .iter()
228 .map(|(ty, options)| {
229 (*ty as u32, ColumnTypeOptions {
230 options: options.iter().map(|x| (*x).to_string()).collect(),
231 })
232 })
233 .collect(),
234 }
235 }
236}
237
238impl From<&GetFeaturesResp> for Features<'static> {
239 fn from(value: &GetFeaturesResp) -> Features<'static> {
240 Features {
241 group_by: value.group_by,
242 group_rollup_mode: value
243 .group_rollup_mode
244 .iter()
245 .filter_map(|x| crate::proto::GroupRollupMode::try_from(*x).ok())
246 .map(Into::into)
247 .collect(),
248 split_by: value.split_by,
249 split_rollup_mode: value
250 .split_rollup_mode
251 .iter()
252 .filter_map(|x| crate::proto::SplitRollupMode::try_from(*x).ok())
253 .map(Into::into)
254 .collect(),
255 filter_ops: by_column_type(&value.filter_ops, |x| {
256 x.options.iter().cloned().map(Cow::Owned).collect()
257 }),
258 aggregates: by_column_type(&value.aggregates, |x| {
259 x.aggregates
260 .iter()
261 .map(|agg| {
262 let name = Cow::Owned(agg.name.clone());
263 if agg.args.is_empty() {
264 AggSpec::Single(name)
265 } else {
266 AggSpec::Multiple(
267 name,
268 agg.args
269 .iter()
270 .filter_map(|t| ColumnType::try_from(*t).ok())
271 .collect(),
272 )
273 }
274 })
275 .collect()
276 }),
277 sort: value.sort,
278 expressions: value.expressions,
279 window_aggregates: by_column_type(&value.window_aggregates, |x| {
280 x.options
281 .iter()
282 .map(|w| WindowAggSpec {
283 name: Cow::Owned(w.name.clone()),
284 frames: w.frames.iter().cloned().map(Cow::Owned).collect(),
285 offset: w.offset,
286 alpha: w.alpha,
287 result_type: w.result_type.and_then(|t| ColumnType::try_from(t).ok()),
288 })
289 .collect()
290 }),
291 on_update: value.on_update,
292 unordered: value.unordered,
293 view_derivations: value.view_derivations,
294 expand_collapse: value.expand_collapse,
295 update: value.update,
296 }
297 }
298}
299
300fn by_column_type<'a, T: 'a, U>(
302 map: impl IntoIterator<Item = (&'a u32, &'a T)>,
303 f: impl Fn(&T) -> U,
304) -> IndexMap<ColumnType, U> {
305 let mut entries = map
306 .into_iter()
307 .filter_map(|(ty, x)| Some((ColumnType::try_from(*ty as i32).ok()?, f(x))))
308 .collect::<Vec<_>>();
309
310 entries.sort_by_key(|(ty, _)| *ty as i32);
311 entries.into_iter().collect()
312}