1#[derive(Debug, Clone, PartialEq, Eq)]
2pub enum DimType {
3 String,
4 Int,
5 Float,
6 DateTime,
7 Bool,
8}
9
10#[derive(Debug, Clone)]
11pub struct Dimension {
12 pub graphql_name: String,
13 pub column: String,
14 pub dim_type: DimType,
15}
16
17#[derive(Debug, Clone)]
18pub enum DimensionNode {
19 Leaf(Dimension),
20 Group {
21 graphql_name: String,
22 children: Vec<DimensionNode>,
23 },
24}
25
26#[derive(Debug, Clone)]
30pub struct SelectorDef {
31 pub graphql_name: String,
32 pub column: String,
33 pub dim_type: DimType,
34}
35
36pub fn selector(graphql_name: &str, column: &str, dim_type: DimType) -> SelectorDef {
37 SelectorDef {
38 graphql_name: graphql_name.to_string(),
39 column: column.to_string(),
40 dim_type,
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct CubeDefinition {
46 pub name: String,
47 pub schema: String,
48 pub table_pattern: String,
53 pub chain_column: Option<String>,
57 pub dimensions: Vec<DimensionNode>,
58 pub metrics: Vec<String>,
59 pub selectors: Vec<SelectorDef>,
60 pub default_filters: Vec<(String, String)>,
61 pub default_limit: u32,
62 pub max_limit: u32,
63 pub use_final: bool,
65}
66
67impl CubeDefinition {
68 pub fn table_for_chain(&self, chain: &str) -> String {
69 self.table_pattern.replace("{chain}", chain)
70 }
71
72 pub fn qualified_table(&self, chain: &str) -> String {
73 format!("{}.{}", self.schema, self.table_for_chain(chain))
74 }
75
76 pub fn flat_dimensions(&self) -> Vec<(String, Dimension)> {
77 let mut out = Vec::new();
78 for node in &self.dimensions {
79 collect_leaves(node, "", &mut out);
80 }
81 out
82 }
83}
84
85fn collect_leaves(node: &DimensionNode, prefix: &str, out: &mut Vec<(String, Dimension)>) {
86 match node {
87 DimensionNode::Leaf(dim) => {
88 let path = if prefix.is_empty() {
89 dim.graphql_name.clone()
90 } else {
91 format!("{}_{}", prefix, dim.graphql_name)
92 };
93 out.push((path, dim.clone()));
94 }
95 DimensionNode::Group { graphql_name, children } => {
96 let new_prefix = if prefix.is_empty() {
97 graphql_name.clone()
98 } else {
99 format!("{prefix}_{graphql_name}")
100 };
101 for child in children {
102 collect_leaves(child, &new_prefix, out);
103 }
104 }
105 }
106}
107
108pub struct CubeBuilder {
113 def: CubeDefinition,
114}
115
116impl CubeBuilder {
117 pub fn new(name: &str) -> Self {
118 Self {
119 def: CubeDefinition {
120 name: name.to_string(),
121 schema: String::new(),
122 table_pattern: String::new(),
123 chain_column: None,
124 dimensions: Vec::new(),
125 metrics: Vec::new(),
126 selectors: Vec::new(),
127 default_filters: Vec::new(),
128 default_limit: 25,
129 max_limit: 10000,
130 use_final: false,
131 },
132 }
133 }
134
135 pub fn schema(mut self, schema: &str) -> Self {
136 self.def.schema = schema.to_string();
137 self
138 }
139
140 pub fn table(mut self, pattern: &str) -> Self {
141 self.def.table_pattern = pattern.to_string();
142 self
143 }
144
145 pub fn chain_column(mut self, column: &str) -> Self {
146 self.def.chain_column = Some(column.to_string());
147 self
148 }
149
150 pub fn dimension(mut self, node: DimensionNode) -> Self {
151 self.def.dimensions.push(node);
152 self
153 }
154
155 pub fn metric(mut self, name: &str) -> Self {
156 self.def.metrics.push(name.to_string());
157 self
158 }
159
160 pub fn metrics(mut self, names: &[&str]) -> Self {
161 self.def.metrics.extend(names.iter().map(|s| s.to_string()));
162 self
163 }
164
165 pub fn selector(mut self, sel: SelectorDef) -> Self {
166 self.def.selectors.push(sel);
167 self
168 }
169
170 pub fn default_filter(mut self, column: &str, value: &str) -> Self {
171 self.def.default_filters.push((column.to_string(), value.to_string()));
172 self
173 }
174
175 pub fn default_limit(mut self, limit: u32) -> Self {
176 self.def.default_limit = limit;
177 self
178 }
179
180 pub fn max_limit(mut self, limit: u32) -> Self {
181 self.def.max_limit = limit;
182 self
183 }
184
185 pub fn use_final(mut self, val: bool) -> Self {
186 self.def.use_final = val;
187 self
188 }
189
190 pub fn build(self) -> CubeDefinition {
191 self.def
192 }
193}
194
195pub fn dim(graphql_name: &str, column: &str, dim_type: DimType) -> DimensionNode {
200 DimensionNode::Leaf(Dimension {
201 graphql_name: graphql_name.to_string(),
202 column: column.to_string(),
203 dim_type,
204 })
205}
206
207pub fn dim_group(graphql_name: &str, children: Vec<DimensionNode>) -> DimensionNode {
208 DimensionNode::Group {
209 graphql_name: graphql_name.to_string(),
210 children,
211 }
212}