perspective_client/config/
expressions.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
13#![doc = include_str!("../../../docs/expressions.md")]
14#![cfg_attr(not(feature = "omit_metadata"), doc = include_str!("../../../docs/expression_gen.md"))]
15
16use std::borrow::Cow;
17use std::collections::HashMap;
18
19use serde::{Deserialize, Serialize};
20use ts_rs::TS;
21
22#[derive(Deserialize, Clone, PartialEq, Debug)]
23#[serde(untagged)]
24pub enum ExpressionsDeserde {
25    Array(Vec<String>),
26    Map(HashMap<String, String>),
27}
28
29#[derive(Deserialize, Serialize, Clone, PartialEq, Debug, Default, TS)]
30#[serde(from = "ExpressionsDeserde")]
31pub struct Expressions(pub HashMap<String, String>);
32
33impl std::ops::Deref for Expressions {
34    type Target = HashMap<String, String>;
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl std::ops::DerefMut for Expressions {
42    fn deref_mut(&mut self) -> &mut Self::Target {
43        &mut self.0
44    }
45}
46
47fn upgrade_legacy_format(expressions: &[String]) -> HashMap<String, String> {
48    tracing::debug!("Legacy `expressions` format: {:?}", expressions);
49    expressions
50        .iter()
51        .map(|s| {
52            if let Some((name, expression)) = s.split_once('\n') {
53                if !expression.is_empty() && name.starts_with("//") {
54                    (name.split_at(2).1.trim().to_owned(), expression.to_owned())
55                } else {
56                    (s.to_owned(), s.to_owned())
57                }
58            } else {
59                (s.to_owned(), s.to_owned())
60            }
61        })
62        .collect::<HashMap<_, _>>()
63}
64
65impl From<ExpressionsDeserde> for Expressions {
66    fn from(value: ExpressionsDeserde) -> Self {
67        match value {
68            ExpressionsDeserde::Array(arr) => Self(upgrade_legacy_format(&arr)),
69            ExpressionsDeserde::Map(map) => Self(map),
70        }
71    }
72}
73
74#[derive(Clone, Debug, PartialEq, TS)]
75pub struct Expression<'a> {
76    pub name: Cow<'a, str>,
77    pub expression: Cow<'a, str>,
78}
79
80impl<'a> Expression<'a> {
81    /// If name is None, the expression is used as the name.
82    pub fn new(name: Option<Cow<'a, str>>, expression: Cow<'a, str>) -> Self {
83        Self {
84            name: name.unwrap_or_else(|| expression.clone()),
85            expression,
86        }
87    }
88}
89
90impl<'a> FromIterator<Expression<'a>> for Expressions {
91    fn from_iter<T: IntoIterator<Item = Expression<'a>>>(iter: T) -> Self {
92        Self(
93            iter.into_iter()
94                .map(|x| (x.name.as_ref().to_owned(), x.expression.as_ref().to_owned()))
95                .collect(),
96        )
97    }
98}
99
100impl Expressions {
101    pub fn insert(&mut self, expr: &Expression) {
102        self.0.insert(
103            expr.name.as_ref().to_owned(),
104            expr.expression.as_ref().to_owned(),
105        );
106    }
107}
108
109#[doc(hidden)]
110#[derive(Serialize, Clone, Copy)]
111pub struct CompletionItemSuggestion {
112    pub label: &'static str,
113    pub insert_text: &'static str,
114    pub documentation: &'static str,
115}
116
117#[doc(hidden)]
118pub static COMPLETIONS: [CompletionItemSuggestion; 77] = [
119    CompletionItemSuggestion {
120        label: "var",
121        insert_text: "var ${1:x := 1}",
122        documentation: "Declare a new local variable",
123    },
124    CompletionItemSuggestion {
125        label: "abs",
126        insert_text: "abs(${1:x})",
127        documentation: "Absolute value of x",
128    },
129    CompletionItemSuggestion {
130        label: "avg",
131        insert_text: "avg(${1:x})",
132        documentation: "Average of all inputs",
133    },
134    CompletionItemSuggestion {
135        label: "bucket",
136        insert_text: "bucket(${1:x}, ${2:y})",
137        documentation: "Bucket x by y",
138    },
139    CompletionItemSuggestion {
140        label: "ceil",
141        insert_text: "ceil(${1:x})",
142        documentation: "Smallest integer >= x",
143    },
144    CompletionItemSuggestion {
145        label: "exp",
146        insert_text: "exp(${1:x})",
147        documentation: "Natural exponent of x (e ^ x)",
148    },
149    CompletionItemSuggestion {
150        label: "floor",
151        insert_text: "floor(${1:x})",
152        documentation: "Largest integer <= x",
153    },
154    CompletionItemSuggestion {
155        label: "frac",
156        insert_text: "frac(${1:x})",
157        documentation: "Fractional portion (after the decimal) of x",
158    },
159    CompletionItemSuggestion {
160        label: "iclamp",
161        insert_text: "iclamp(${1:x})",
162        documentation: "Inverse clamp x within a range",
163    },
164    CompletionItemSuggestion {
165        label: "inrange",
166        insert_text: "inrange(${1:x})",
167        documentation: "Returns whether x is within a range",
168    },
169    CompletionItemSuggestion {
170        label: "log",
171        insert_text: "log(${1:x})",
172        documentation: "Natural log of x",
173    },
174    CompletionItemSuggestion {
175        label: "log10",
176        insert_text: "log10(${1:x})",
177        documentation: "Base 10 log of x",
178    },
179    CompletionItemSuggestion {
180        label: "log1p",
181        insert_text: "log1p(${1:x})",
182        documentation: "Natural log of 1 + x where x is very small",
183    },
184    CompletionItemSuggestion {
185        label: "log2",
186        insert_text: "log2(${1:x})",
187        documentation: "Base 2 log of x",
188    },
189    CompletionItemSuggestion {
190        label: "logn",
191        insert_text: "logn(${1:x}, ${2:N})",
192        documentation: "Base N log of x where N >= 0",
193    },
194    CompletionItemSuggestion {
195        label: "max",
196        insert_text: "max(${1:x})",
197        documentation: "Maximum value of all inputs",
198    },
199    CompletionItemSuggestion {
200        label: "min",
201        insert_text: "min(${1:x})",
202        documentation: "Minimum value of all inputs",
203    },
204    CompletionItemSuggestion {
205        label: "mul",
206        insert_text: "mul(${1:x})",
207        documentation: "Product of all inputs",
208    },
209    CompletionItemSuggestion {
210        label: "percent_of",
211        insert_text: "percent_of(${1:x})",
212        documentation: "Percent y of x",
213    },
214    CompletionItemSuggestion {
215        label: "pow",
216        insert_text: "pow(${1:x}, ${2:y})",
217        documentation: "x to the power of y",
218    },
219    CompletionItemSuggestion {
220        label: "root",
221        insert_text: "root(${1:x}, ${2:N})",
222        documentation: "N-th root of x where N >= 0",
223    },
224    CompletionItemSuggestion {
225        label: "round",
226        insert_text: "round(${1:x})",
227        documentation: "Round x to the nearest integer",
228    },
229    CompletionItemSuggestion {
230        label: "sgn",
231        insert_text: "sgn(${1:x})",
232        documentation: "Sign of x: -1, 1, or 0",
233    },
234    CompletionItemSuggestion {
235        label: "sqrt",
236        insert_text: "sqrt(${1:x})",
237        documentation: "Square root of x",
238    },
239    CompletionItemSuggestion {
240        label: "sum",
241        insert_text: "sum(${1:x})",
242        documentation: "Sum of all inputs",
243    },
244    CompletionItemSuggestion {
245        label: "trunc",
246        insert_text: "trunc(${1:x})",
247        documentation: "Integer portion of x",
248    },
249    CompletionItemSuggestion {
250        label: "acos",
251        insert_text: "acos(${1:x})",
252        documentation: "Arc cosine of x in radians",
253    },
254    CompletionItemSuggestion {
255        label: "acosh",
256        insert_text: "acosh(${1:x})",
257        documentation: "Inverse hyperbolic cosine of x in radians",
258    },
259    CompletionItemSuggestion {
260        label: "asin",
261        insert_text: "asin(${1:x})",
262        documentation: "Arc sine of x in radians",
263    },
264    CompletionItemSuggestion {
265        label: "asinh",
266        insert_text: "asinh(${1:x})",
267        documentation: "Inverse hyperbolic sine of x in radians",
268    },
269    CompletionItemSuggestion {
270        label: "atan",
271        insert_text: "atan(${1:x})",
272        documentation: "Arc tangent of x in radians",
273    },
274    CompletionItemSuggestion {
275        label: "atanh",
276        insert_text: "atanh(${1:x})",
277        documentation: "Inverse hyperbolic tangent of x in radians",
278    },
279    CompletionItemSuggestion {
280        label: "cos",
281        insert_text: "cos(${1:x})",
282        documentation: "Cosine of x",
283    },
284    CompletionItemSuggestion {
285        label: "cosh",
286        insert_text: "cosh(${1:x})",
287        documentation: "Hyperbolic cosine of x",
288    },
289    CompletionItemSuggestion {
290        label: "cot",
291        insert_text: "cot(${1:x})",
292        documentation: "Cotangent of x",
293    },
294    CompletionItemSuggestion {
295        label: "sin",
296        insert_text: "sin(${1:x})",
297        documentation: "Sine of x",
298    },
299    CompletionItemSuggestion {
300        label: "sinc",
301        insert_text: "sinc(${1:x})",
302        documentation: "Sine cardinal of x",
303    },
304    CompletionItemSuggestion {
305        label: "sinh",
306        insert_text: "sinh(${1:x})",
307        documentation: "Hyperbolic sine of x",
308    },
309    CompletionItemSuggestion {
310        label: "tan",
311        insert_text: "tan(${1:x})",
312        documentation: "Tangent of x",
313    },
314    CompletionItemSuggestion {
315        label: "tanh",
316        insert_text: "tanh(${1:x})",
317        documentation: "Hyperbolic tangent of x",
318    },
319    CompletionItemSuggestion {
320        label: "deg2rad",
321        insert_text: "deg2rad(${1:x})",
322        documentation: "Convert x from degrees to radians",
323    },
324    CompletionItemSuggestion {
325        label: "deg2grad",
326        insert_text: "deg2grad(${1:x})",
327        documentation: "Convert x from degrees to gradians",
328    },
329    CompletionItemSuggestion {
330        label: "rad2deg",
331        insert_text: "rad2deg(${1:x})",
332        documentation: "Convert x from radians to degrees",
333    },
334    CompletionItemSuggestion {
335        label: "grad2deg",
336        insert_text: "grad2deg(${1:x})",
337        documentation: "Convert x from gradians to degrees",
338    },
339    CompletionItemSuggestion {
340        label: "concat",
341        insert_text: "concat(${1:x}, ${2:y})",
342        documentation: "Concatenate string columns and string literals, such \
343                        as:\nconcat(\"State\" ', ', \"City\")",
344    },
345    CompletionItemSuggestion {
346        label: "order",
347        insert_text: "order(${1:input column}, ${2:value}, ...)",
348        documentation: "Generates a sort order for a string column based on the input order of \
349                        the parameters, such as:\norder(\"State\", 'Texas', 'New York')",
350    },
351    CompletionItemSuggestion {
352        label: "upper",
353        insert_text: "upper(${1:x})",
354        documentation: "Uppercase of x",
355    },
356    CompletionItemSuggestion {
357        label: "lower",
358        insert_text: "lower(${1:x})",
359        documentation: "Lowercase of x",
360    },
361    CompletionItemSuggestion {
362        label: "hour_of_day",
363        insert_text: "hour_of_day(${1:x})",
364        documentation: "Return a datetime's hour of the day as a string",
365    },
366    CompletionItemSuggestion {
367        label: "month_of_year",
368        insert_text: "month_of_year(${1:x})",
369        documentation: "Return a datetime's month of the year as a string",
370    },
371    CompletionItemSuggestion {
372        label: "day_of_week",
373        insert_text: "day_of_week(${1:x})",
374        documentation: "Return a datetime's day of week as a string",
375    },
376    CompletionItemSuggestion {
377        label: "now",
378        insert_text: "now()",
379        documentation: "The current datetime in local time",
380    },
381    CompletionItemSuggestion {
382        label: "today",
383        insert_text: "today()",
384        documentation: "The current date in local time",
385    },
386    CompletionItemSuggestion {
387        label: "is_null",
388        insert_text: "is_null(${1:x})",
389        documentation: "Whether x is a null value",
390    },
391    CompletionItemSuggestion {
392        label: "is_not_null",
393        insert_text: "is_not_null(${1:x})",
394        documentation: "Whether x is not a null value",
395    },
396    CompletionItemSuggestion {
397        label: "not",
398        insert_text: "not(${1:x})",
399        documentation: "not x",
400    },
401    CompletionItemSuggestion {
402        label: "true",
403        insert_text: "true",
404        documentation: "Boolean value true",
405    },
406    CompletionItemSuggestion {
407        label: "false",
408        insert_text: "false",
409        documentation: "Boolean value false",
410    },
411    CompletionItemSuggestion {
412        label: "if",
413        insert_text: "if (${1:condition}) {} else if (${2:condition}) {} else {}",
414        documentation: "An if/else conditional, which evaluates a condition such as:\n if \
415                        (\"Sales\" > 100) { true } else { false }",
416    },
417    CompletionItemSuggestion {
418        label: "for",
419        insert_text: "for (${1:expression}) {}",
420        documentation: "A for loop, which repeatedly evaluates an incrementing expression such \
421                        as:\nvar x := 0; var y := 1; for (x < 10; x += 1) { y := x + y }",
422    },
423    CompletionItemSuggestion {
424        label: "string",
425        insert_text: "string(${1:x})",
426        documentation: "Converts the given argument to a string",
427    },
428    CompletionItemSuggestion {
429        label: "integer",
430        insert_text: "integer(${1:x})",
431        documentation: "Converts the given argument to a 32-bit integer. If the result \
432                        over/under-flows, null is returned",
433    },
434    CompletionItemSuggestion {
435        label: "float",
436        insert_text: "float(${1:x})",
437        documentation: "Converts the argument to a float",
438    },
439    CompletionItemSuggestion {
440        label: "date",
441        insert_text: "date(${1:year}, ${1:month}, ${1:day})",
442        documentation: "Given a year, month (1-12) and day, create a new date",
443    },
444    CompletionItemSuggestion {
445        label: "datetime",
446        insert_text: "datetime(${1:timestamp})",
447        documentation: "Given a POSIX timestamp of milliseconds since epoch, create a new datetime",
448    },
449    CompletionItemSuggestion {
450        label: "boolean",
451        insert_text: "boolean(${1:x})",
452        documentation: "Converts the given argument to a boolean",
453    },
454    CompletionItemSuggestion {
455        label: "random",
456        insert_text: "random()",
457        documentation: "Returns a random float between 0 and 1, inclusive.",
458    },
459    CompletionItemSuggestion {
460        label: "match",
461        insert_text: "match(${1:string}, ${2:pattern})",
462        documentation: "Returns True if any part of string matches pattern, and False otherwise.",
463    },
464    CompletionItemSuggestion {
465        label: "match_all",
466        insert_text: "match_all(${1:string}, ${2:pattern})",
467        documentation: "Returns True if the whole string matches pattern, and False otherwise.",
468    },
469    CompletionItemSuggestion {
470        label: "search",
471        insert_text: "search(${1:string}, ${2:pattern})",
472        documentation: "Returns the substring that matches the first capturing group in pattern, \
473                        or null if there are no capturing groups in the pattern or if there are \
474                        no matches.",
475    },
476    CompletionItemSuggestion {
477        label: "indexof",
478        insert_text: "indexof(${1:string}, ${2:pattern}, ${3:output_vector})",
479        documentation: "Writes into index 0 and 1 of output_vector the start and end indices of \
480                        the substring that matches the first capturing group in \
481                        pattern.\n\nReturns true if there is a match and output was written, or \
482                        false if there are no capturing groups in the pattern, if there are no \
483                        matches, or if the indices are invalid.",
484    },
485    CompletionItemSuggestion {
486        label: "substring",
487        insert_text: "substring(${1:string}, ${2:start_idx}, ${3:length})",
488        documentation: "Returns a substring of string from start_idx with the given length. If \
489                        length is not passed in, returns substring from start_idx to the end of \
490                        the string. Returns null if the string or any indices are invalid.",
491    },
492    CompletionItemSuggestion {
493        label: "replace",
494        insert_text: "replace(${1:string}, ${2:pattern}, ${3:replacer})",
495        documentation: "Replaces the first match of pattern in string with replacer, or return \
496                        the original string if no replaces were made.",
497    },
498    CompletionItemSuggestion {
499        label: "replace_all",
500        insert_text: "replace(${1:string}, ${2:pattern}, ${3:replacer})",
501        documentation: "Replaces all non-overlapping matches of pattern in string with replacer, \
502                        or return the original string if no replaces were made.",
503    },
504    CompletionItemSuggestion {
505        label: "index",
506        insert_text: "index()",
507        documentation: "Looks up the index value of the current row",
508    },
509    CompletionItemSuggestion {
510        label: "col",
511        insert_text: "col(${1:string})",
512        documentation: "Looks up a column value by name",
513    },
514    CompletionItemSuggestion {
515        label: "vlookup",
516        insert_text: "vlookup(${1:string}, ${2:uint64})",
517        documentation: "Looks up a value in another column by index",
518    },
519];