vegafusion_core/spec/transform/
stack.rs1use crate::expression::column_usage::{ColumnUsage, DatasetsColumnUsage, VlSelectionFields};
2use crate::spec::transform::{TransformColumns, TransformSpecTrait};
3use crate::spec::values::{CompareSpec, Field};
4use crate::task_graph::graph::ScopedVariable;
5use crate::task_graph::scope::TaskScope;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9use vegafusion_common::escape::unescape_field;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct StackTransformSpec {
13 pub field: Field,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub groupby: Option<Vec<Field>>,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub sort: Option<CompareSpec>,
20
21 #[serde(rename = "as", skip_serializing_if = "Option::is_none")]
22 pub as_: Option<Vec<String>>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub offset: Option<StackOffsetSpec>,
26
27 #[serde(flatten)]
28 pub extra: HashMap<String, Value>,
29}
30
31impl StackTransformSpec {
32 pub fn as_(&self) -> Vec<String> {
33 self.as_
34 .clone()
35 .unwrap_or_else(|| vec!["y0".to_string(), "y1".to_string()])
36 }
37
38 pub fn offset(&self) -> StackOffsetSpec {
39 self.offset.clone().unwrap_or(StackOffsetSpec::Zero)
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "lowercase")]
45pub enum StackOffsetSpec {
46 Zero,
47 Center,
48 Normalize,
49}
50
51impl TransformSpecTrait for StackTransformSpec {
52 fn transform_columns(
53 &self,
54 datum_var: &Option<ScopedVariable>,
55 _usage_scope: &[u32],
56 _task_scope: &TaskScope,
57 _vl_selection_fields: &VlSelectionFields,
58 ) -> TransformColumns {
59 if let Some(datum_var) = datum_var {
60 let mut col_usage = ColumnUsage::from(unescape_field(&self.field.field()).as_str());
62
63 if let Some(groupby) = self.groupby.as_ref() {
65 let groupby: Vec<_> = groupby
66 .iter()
67 .map(|field| unescape_field(&field.field()))
68 .collect();
69 col_usage = col_usage.union(&ColumnUsage::from(groupby.as_slice()));
70 }
71
72 if let Some(compares) = self.sort.as_ref() {
74 let unescaped_sort_fields: Vec<_> = compares
75 .field
76 .to_vec()
77 .iter()
78 .map(|f| unescape_field(f))
79 .collect();
80 col_usage = col_usage.union(&ColumnUsage::from(unescaped_sort_fields.as_slice()));
81 }
82
83 let produced = ColumnUsage::from(self.as_().as_slice());
85
86 let usage = DatasetsColumnUsage::empty().with_column_usage(datum_var, col_usage);
87 TransformColumns::PassThrough { usage, produced }
88 } else {
89 TransformColumns::Unknown
90 }
91 }
92
93 fn local_datetime_columns_produced(
94 &self,
95 input_local_datetime_columns: &[String],
96 ) -> Vec<String> {
97 Vec::from(input_local_datetime_columns)
100 }
101}