1#![cfg_attr(not(feature = "omit_metadata"), doc = include_str!("../../../docs/expression_gen.md"))]
159
160use std::borrow::Cow;
161use std::collections::HashMap;
162use std::fmt;
163
164use serde::de::value::MapAccessDeserializer;
165use serde::de::{MapAccess, Visitor};
166use serde::{Deserialize, Deserializer, Serialize};
167use ts_rs::TS;
168
169#[derive(Serialize, Clone, PartialEq, Debug, Default, TS)]
170pub struct Expressions(pub HashMap<String, String>);
171
172impl<'de> Deserialize<'de> for Expressions {
173 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
174 deserializer.deserialize_any(ExpressionsVisitor)
175 }
176}
177
178struct ExpressionsVisitor;
179
180impl<'de> Visitor<'de> for ExpressionsVisitor {
181 type Value = Expressions;
182
183 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
184 formatter.write_str(
185 "an object mapping expression names to expressions (upgrade the legacy array format \
186 with `perspective-migrate`)",
187 )
188 }
189
190 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
191 HashMap::deserialize(MapAccessDeserializer::new(map)).map(Expressions)
192 }
193}
194
195impl std::ops::Deref for Expressions {
196 type Target = HashMap<String, String>;
197
198 fn deref(&self) -> &Self::Target {
199 &self.0
200 }
201}
202
203impl std::ops::DerefMut for Expressions {
204 fn deref_mut(&mut self) -> &mut Self::Target {
205 &mut self.0
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, TS)]
210pub struct Expression<'a> {
211 pub name: Cow<'a, str>,
212 pub expression: Cow<'a, str>,
213}
214
215impl<'a> Expression<'a> {
216 pub fn new(name: Option<Cow<'a, str>>, expression: Cow<'a, str>) -> Self {
218 Self {
219 name: name.unwrap_or_else(|| expression.clone()),
220 expression,
221 }
222 }
223}
224
225impl<'a> FromIterator<Expression<'a>> for Expressions {
226 fn from_iter<T: IntoIterator<Item = Expression<'a>>>(iter: T) -> Self {
227 Self(
228 iter.into_iter()
229 .map(|x| (x.name.as_ref().to_owned(), x.expression.as_ref().to_owned()))
230 .collect(),
231 )
232 }
233}
234
235impl Expressions {
236 pub fn insert(&mut self, expr: &Expression) {
237 self.0.insert(
238 expr.name.as_ref().to_owned(),
239 expr.expression.as_ref().to_owned(),
240 );
241 }
242}
243
244#[doc(hidden)]
245#[derive(Serialize, Clone, Copy, PartialEq)]
246pub struct CompletionItemSuggestion {
247 pub label: &'static str,
248 pub insert_text: &'static str,
249 pub documentation: &'static str,
250}
251
252#[doc(hidden)]
253pub static COMPLETIONS: [CompletionItemSuggestion; 79] = [
254 CompletionItemSuggestion {
255 label: "var",
256 insert_text: "var ${1:x := 1}",
257 documentation: "Declare a new local variable",
258 },
259 CompletionItemSuggestion {
260 label: "abs",
261 insert_text: "abs(${1:x})",
262 documentation: "Absolute value of x",
263 },
264 CompletionItemSuggestion {
265 label: "avg",
266 insert_text: "avg(${1:x})",
267 documentation: "Average of all inputs",
268 },
269 CompletionItemSuggestion {
270 label: "bucket",
271 insert_text: "bucket(${1:x}, ${2:y})",
272 documentation: "Bucket x by y",
273 },
274 CompletionItemSuggestion {
275 label: "ceil",
276 insert_text: "ceil(${1:x})",
277 documentation: "Smallest integer >= x",
278 },
279 CompletionItemSuggestion {
280 label: "exp",
281 insert_text: "exp(${1:x})",
282 documentation: "Natural exponent of x (e ^ x)",
283 },
284 CompletionItemSuggestion {
285 label: "floor",
286 insert_text: "floor(${1:x})",
287 documentation: "Largest integer <= x",
288 },
289 CompletionItemSuggestion {
290 label: "frac",
291 insert_text: "frac(${1:x})",
292 documentation: "Fractional portion (after the decimal) of x",
293 },
294 CompletionItemSuggestion {
295 label: "iclamp",
296 insert_text: "iclamp(${1:x})",
297 documentation: "Inverse clamp x within a range",
298 },
299 CompletionItemSuggestion {
300 label: "inrange",
301 insert_text: "inrange(${1:x})",
302 documentation: "Returns whether x is within a range",
303 },
304 CompletionItemSuggestion {
305 label: "log",
306 insert_text: "log(${1:x})",
307 documentation: "Natural log of x",
308 },
309 CompletionItemSuggestion {
310 label: "log10",
311 insert_text: "log10(${1:x})",
312 documentation: "Base 10 log of x",
313 },
314 CompletionItemSuggestion {
315 label: "log1p",
316 insert_text: "log1p(${1:x})",
317 documentation: "Natural log of 1 + x where x is very small",
318 },
319 CompletionItemSuggestion {
320 label: "log2",
321 insert_text: "log2(${1:x})",
322 documentation: "Base 2 log of x",
323 },
324 CompletionItemSuggestion {
325 label: "logn",
326 insert_text: "logn(${1:x}, ${2:N})",
327 documentation: "Base N log of x where N >= 0",
328 },
329 CompletionItemSuggestion {
330 label: "max",
331 insert_text: "max(${1:x})",
332 documentation: "Maximum value of all inputs",
333 },
334 CompletionItemSuggestion {
335 label: "min",
336 insert_text: "min(${1:x})",
337 documentation: "Minimum value of all inputs",
338 },
339 CompletionItemSuggestion {
340 label: "mul",
341 insert_text: "mul(${1:x})",
342 documentation: "Product of all inputs",
343 },
344 CompletionItemSuggestion {
345 label: "percent_of",
346 insert_text: "percent_of(${1:x})",
347 documentation: "Percent y of x",
348 },
349 CompletionItemSuggestion {
350 label: "pow",
351 insert_text: "pow(${1:x}, ${2:y})",
352 documentation: "x to the power of y",
353 },
354 CompletionItemSuggestion {
355 label: "root",
356 insert_text: "root(${1:x}, ${2:N})",
357 documentation: "N-th root of x where N >= 0",
358 },
359 CompletionItemSuggestion {
360 label: "round",
361 insert_text: "round(${1:x})",
362 documentation: "Round x to the nearest integer",
363 },
364 CompletionItemSuggestion {
365 label: "sgn",
366 insert_text: "sgn(${1:x})",
367 documentation: "Sign of x: -1, 1, or 0",
368 },
369 CompletionItemSuggestion {
370 label: "sqrt",
371 insert_text: "sqrt(${1:x})",
372 documentation: "Square root of x",
373 },
374 CompletionItemSuggestion {
375 label: "sum",
376 insert_text: "sum(${1:x})",
377 documentation: "Sum of all inputs",
378 },
379 CompletionItemSuggestion {
380 label: "trunc",
381 insert_text: "trunc(${1:x})",
382 documentation: "Integer portion of x",
383 },
384 CompletionItemSuggestion {
385 label: "acos",
386 insert_text: "acos(${1:x})",
387 documentation: "Arc cosine of x in radians",
388 },
389 CompletionItemSuggestion {
390 label: "acosh",
391 insert_text: "acosh(${1:x})",
392 documentation: "Inverse hyperbolic cosine of x in radians",
393 },
394 CompletionItemSuggestion {
395 label: "asin",
396 insert_text: "asin(${1:x})",
397 documentation: "Arc sine of x in radians",
398 },
399 CompletionItemSuggestion {
400 label: "asinh",
401 insert_text: "asinh(${1:x})",
402 documentation: "Inverse hyperbolic sine of x in radians",
403 },
404 CompletionItemSuggestion {
405 label: "atan",
406 insert_text: "atan(${1:x})",
407 documentation: "Arc tangent of x in radians",
408 },
409 CompletionItemSuggestion {
410 label: "atanh",
411 insert_text: "atanh(${1:x})",
412 documentation: "Inverse hyperbolic tangent of x in radians",
413 },
414 CompletionItemSuggestion {
415 label: "cos",
416 insert_text: "cos(${1:x})",
417 documentation: "Cosine of x",
418 },
419 CompletionItemSuggestion {
420 label: "cosh",
421 insert_text: "cosh(${1:x})",
422 documentation: "Hyperbolic cosine of x",
423 },
424 CompletionItemSuggestion {
425 label: "cot",
426 insert_text: "cot(${1:x})",
427 documentation: "Cotangent of x",
428 },
429 CompletionItemSuggestion {
430 label: "sin",
431 insert_text: "sin(${1:x})",
432 documentation: "Sine of x",
433 },
434 CompletionItemSuggestion {
435 label: "sinc",
436 insert_text: "sinc(${1:x})",
437 documentation: "Sine cardinal of x",
438 },
439 CompletionItemSuggestion {
440 label: "sinh",
441 insert_text: "sinh(${1:x})",
442 documentation: "Hyperbolic sine of x",
443 },
444 CompletionItemSuggestion {
445 label: "tan",
446 insert_text: "tan(${1:x})",
447 documentation: "Tangent of x",
448 },
449 CompletionItemSuggestion {
450 label: "tanh",
451 insert_text: "tanh(${1:x})",
452 documentation: "Hyperbolic tangent of x",
453 },
454 CompletionItemSuggestion {
455 label: "deg2rad",
456 insert_text: "deg2rad(${1:x})",
457 documentation: "Convert x from degrees to radians",
458 },
459 CompletionItemSuggestion {
460 label: "deg2grad",
461 insert_text: "deg2grad(${1:x})",
462 documentation: "Convert x from degrees to gradians",
463 },
464 CompletionItemSuggestion {
465 label: "rad2deg",
466 insert_text: "rad2deg(${1:x})",
467 documentation: "Convert x from radians to degrees",
468 },
469 CompletionItemSuggestion {
470 label: "grad2deg",
471 insert_text: "grad2deg(${1:x})",
472 documentation: "Convert x from gradians to degrees",
473 },
474 CompletionItemSuggestion {
475 label: "concat",
476 insert_text: "concat(${1:x}, ${2:y})",
477 documentation: "Concatenate string columns and string literals, such \
478 as:\nconcat(\"State\" ', ', \"City\")",
479 },
480 CompletionItemSuggestion {
481 label: "order",
482 insert_text: "order(${1:input column}, ${2:value}, ...)",
483 documentation: "Generates a sort order for a string column based on the input order of \
484 the parameters, such as:\norder(\"State\", 'Texas', 'New York')",
485 },
486 CompletionItemSuggestion {
487 label: "upper",
488 insert_text: "upper(${1:x})",
489 documentation: "Uppercase of x",
490 },
491 CompletionItemSuggestion {
492 label: "lower",
493 insert_text: "lower(${1:x})",
494 documentation: "Lowercase of x",
495 },
496 CompletionItemSuggestion {
497 label: "hour_of_day",
498 insert_text: "hour_of_day(${1:x})",
499 documentation: "Return a datetime's hour of the day as a string",
500 },
501 CompletionItemSuggestion {
502 label: "month_of_year",
503 insert_text: "month_of_year(${1:x})",
504 documentation: "Return a datetime's month of the year as a string",
505 },
506 CompletionItemSuggestion {
507 label: "day_of_week",
508 insert_text: "day_of_week(${1:x})",
509 documentation: "Return a datetime's day of week as a string",
510 },
511 CompletionItemSuggestion {
512 label: "now",
513 insert_text: "now()",
514 documentation: "The current datetime in local time",
515 },
516 CompletionItemSuggestion {
517 label: "today",
518 insert_text: "today()",
519 documentation: "The current date in local time",
520 },
521 CompletionItemSuggestion {
522 label: "is_null",
523 insert_text: "is_null(${1:x})",
524 documentation: "Whether x is a null value",
525 },
526 CompletionItemSuggestion {
527 label: "is_not_null",
528 insert_text: "is_not_null(${1:x})",
529 documentation: "Whether x is not a null value",
530 },
531 CompletionItemSuggestion {
532 label: "coalesce",
533 insert_text: "coalesce(${1:x}, ${2:y})",
534 documentation: "Returns the first non-null argument.",
535 },
536 CompletionItemSuggestion {
537 label: "contains",
538 insert_text: "contains(${1:x}, ${2:'substr'})",
539 documentation: "Whether the string column or value contains the literal substring.",
540 },
541 CompletionItemSuggestion {
542 label: "not",
543 insert_text: "not(${1:x})",
544 documentation: "not x",
545 },
546 CompletionItemSuggestion {
547 label: "true",
548 insert_text: "true",
549 documentation: "Boolean value true",
550 },
551 CompletionItemSuggestion {
552 label: "false",
553 insert_text: "false",
554 documentation: "Boolean value false",
555 },
556 CompletionItemSuggestion {
557 label: "if",
558 insert_text: "if (${1:condition}) {} else if (${2:condition}) {} else {}",
559 documentation: "An if/else conditional, which evaluates a condition such as:\n if \
560 (\"Sales\" > 100) { true } else { false }",
561 },
562 CompletionItemSuggestion {
563 label: "for",
564 insert_text: "for (${1:expression}) {}",
565 documentation: "A for loop, which repeatedly evaluates an incrementing expression such \
566 as:\nvar x := 0; var y := 1; for (x < 10; x += 1) { y := x + y }",
567 },
568 CompletionItemSuggestion {
569 label: "string",
570 insert_text: "string(${1:x})",
571 documentation: "Converts the given argument to a string",
572 },
573 CompletionItemSuggestion {
574 label: "integer",
575 insert_text: "integer(${1:x})",
576 documentation: "Converts the given argument to a 32-bit integer. If the result \
577 over/under-flows, null is returned",
578 },
579 CompletionItemSuggestion {
580 label: "float",
581 insert_text: "float(${1:x})",
582 documentation: "Converts the argument to a float",
583 },
584 CompletionItemSuggestion {
585 label: "date",
586 insert_text: "date(${1:year}, ${1:month}, ${1:day})",
587 documentation: "Given a year, month (1-12) and day, create a new date",
588 },
589 CompletionItemSuggestion {
590 label: "datetime",
591 insert_text: "datetime(${1:timestamp})",
592 documentation: "Given a POSIX timestamp of milliseconds since epoch, create a new datetime",
593 },
594 CompletionItemSuggestion {
595 label: "boolean",
596 insert_text: "boolean(${1:x})",
597 documentation: "Converts the given argument to a boolean",
598 },
599 CompletionItemSuggestion {
600 label: "random",
601 insert_text: "random()",
602 documentation: "Returns a random float between 0 and 1, inclusive.",
603 },
604 CompletionItemSuggestion {
605 label: "match",
606 insert_text: "match(${1:string}, ${2:pattern})",
607 documentation: "Returns True if any part of string matches pattern, and False otherwise.",
608 },
609 CompletionItemSuggestion {
610 label: "match_all",
611 insert_text: "match_all(${1:string}, ${2:pattern})",
612 documentation: "Returns True if the whole string matches pattern, and False otherwise.",
613 },
614 CompletionItemSuggestion {
615 label: "search",
616 insert_text: "search(${1:string}, ${2:pattern})",
617 documentation: "Returns the substring that matches the first capturing group in pattern, \
618 or null if there are no capturing groups in the pattern or if there are \
619 no matches.",
620 },
621 CompletionItemSuggestion {
622 label: "indexof",
623 insert_text: "indexof(${1:string}, ${2:pattern}, ${3:output_vector})",
624 documentation: "Writes into index 0 and 1 of output_vector the start and end indices of \
625 the substring that matches the first capturing group in \
626 pattern.\n\nReturns true if there is a match and output was written, or \
627 false if there are no capturing groups in the pattern, if there are no \
628 matches, or if the indices are invalid.",
629 },
630 CompletionItemSuggestion {
631 label: "substring",
632 insert_text: "substring(${1:string}, ${2:start_idx}, ${3:length})",
633 documentation: "Returns a substring of string from start_idx with the given length. If \
634 length is not passed in, returns substring from start_idx to the end of \
635 the string. Returns null if the string or any indices are invalid.",
636 },
637 CompletionItemSuggestion {
638 label: "replace",
639 insert_text: "replace(${1:string}, ${2:pattern}, ${3:replacer})",
640 documentation: "Replaces the first match of pattern in string with replacer, or return \
641 the original string if no replaces were made.",
642 },
643 CompletionItemSuggestion {
644 label: "replace_all",
645 insert_text: "replace_all(${1:string}, ${2:pattern}, ${3:replacer})",
646 documentation: "Replaces all non-overlapping matches of pattern in string with replacer, \
647 or return the original string if no replaces were made.",
648 },
649 CompletionItemSuggestion {
650 label: "index",
651 insert_text: "index()",
652 documentation: "Looks up the index value of the current row",
653 },
654 CompletionItemSuggestion {
655 label: "col",
656 insert_text: "col(${1:string})",
657 documentation: "Looks up a column value by name",
658 },
659 CompletionItemSuggestion {
660 label: "vlookup",
661 insert_text: "vlookup(${1:string}, ${2:uint64})",
662 documentation: "Looks up a value in another column by index",
663 },
664];
665
666#[test]
667fn test_completions_insert_text_matches_label() {
668 for comp in COMPLETIONS {
669 let label = comp.label;
670 let insert_text = comp.insert_text;
671 assert!(
672 insert_text.starts_with(label),
673 "insert_text for label {label} does not start with {label}:\n {insert_text}"
674 );
675 }
676}