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 pub description: Option<String>,
16}
17
18#[derive(Debug, Clone)]
19pub enum DimensionNode {
20 Leaf(Dimension),
21 Group {
22 graphql_name: String,
23 description: Option<String>,
24 children: Vec<DimensionNode>,
25 },
26}
27
28#[derive(Debug, Clone)]
32pub struct SelectorDef {
33 pub graphql_name: String,
34 pub column: String,
35 pub dim_type: DimType,
36}
37
38pub fn selector(graphql_name: &str, column: &str, dim_type: DimType) -> SelectorDef {
39 SelectorDef {
40 graphql_name: graphql_name.to_string(),
41 column: column.to_string(),
42 dim_type,
43 }
44}
45
46#[derive(Debug, Clone)]
47pub struct CubeDefinition {
48 pub name: String,
49 pub schema: String,
50 pub table_pattern: String,
55 pub chain_column: Option<String>,
59 pub dimensions: Vec<DimensionNode>,
60 pub metrics: Vec<String>,
61 pub selectors: Vec<SelectorDef>,
62 pub default_filters: Vec<(String, String)>,
63 pub default_limit: u32,
64 pub max_limit: u32,
65 pub use_final: bool,
67 pub description: String,
69}
70
71impl CubeDefinition {
72 pub fn table_for_chain(&self, chain: &str) -> String {
73 self.table_pattern.replace("{chain}", chain)
74 }
75
76 pub fn qualified_table(&self, chain: &str) -> String {
77 format!("{}.{}", self.schema, self.table_for_chain(chain))
78 }
79
80 pub fn flat_dimensions(&self) -> Vec<(String, Dimension)> {
81 let mut out = Vec::new();
82 for node in &self.dimensions {
83 collect_leaves(node, "", &mut out);
84 }
85 out
86 }
87}
88
89fn collect_leaves(node: &DimensionNode, prefix: &str, out: &mut Vec<(String, Dimension)>) {
90 match node {
91 DimensionNode::Leaf(dim) => {
92 let path = if prefix.is_empty() {
93 dim.graphql_name.clone()
94 } else {
95 format!("{}_{}", prefix, dim.graphql_name)
96 };
97 out.push((path, dim.clone()));
98 }
99 DimensionNode::Group { graphql_name, children, .. } => {
100 let new_prefix = if prefix.is_empty() {
101 graphql_name.clone()
102 } else {
103 format!("{prefix}_{graphql_name}")
104 };
105 for child in children {
106 collect_leaves(child, &new_prefix, out);
107 }
108 }
109 }
110}
111
112pub struct CubeBuilder {
117 def: CubeDefinition,
118}
119
120impl CubeBuilder {
121 pub fn new(name: &str) -> Self {
122 Self {
123 def: CubeDefinition {
124 name: name.to_string(),
125 schema: String::new(),
126 table_pattern: String::new(),
127 chain_column: None,
128 dimensions: Vec::new(),
129 metrics: Vec::new(),
130 selectors: Vec::new(),
131 default_filters: Vec::new(),
132 default_limit: 25,
133 max_limit: 10000,
134 use_final: false,
135 description: String::new(),
136 },
137 }
138 }
139
140 pub fn schema(mut self, schema: &str) -> Self {
141 self.def.schema = schema.to_string();
142 self
143 }
144
145 pub fn table(mut self, pattern: &str) -> Self {
146 self.def.table_pattern = pattern.to_string();
147 self
148 }
149
150 pub fn chain_column(mut self, column: &str) -> Self {
151 self.def.chain_column = Some(column.to_string());
152 self
153 }
154
155 pub fn dimension(mut self, node: DimensionNode) -> Self {
156 self.def.dimensions.push(node);
157 self
158 }
159
160 pub fn metric(mut self, name: &str) -> Self {
161 self.def.metrics.push(name.to_string());
162 self
163 }
164
165 pub fn metrics(mut self, names: &[&str]) -> Self {
166 self.def.metrics.extend(names.iter().map(|s| s.to_string()));
167 self
168 }
169
170 pub fn selector(mut self, sel: SelectorDef) -> Self {
171 self.def.selectors.push(sel);
172 self
173 }
174
175 pub fn default_filter(mut self, column: &str, value: &str) -> Self {
176 self.def.default_filters.push((column.to_string(), value.to_string()));
177 self
178 }
179
180 pub fn default_limit(mut self, limit: u32) -> Self {
181 self.def.default_limit = limit;
182 self
183 }
184
185 pub fn max_limit(mut self, limit: u32) -> Self {
186 self.def.max_limit = limit;
187 self
188 }
189
190 pub fn use_final(mut self, val: bool) -> Self {
191 self.def.use_final = val;
192 self
193 }
194
195 pub fn description(mut self, desc: &str) -> Self {
196 self.def.description = desc.to_string();
197 self
198 }
199
200 pub fn build(self) -> CubeDefinition {
201 self.def
202 }
203}
204
205pub fn dim(graphql_name: &str, column: &str, dim_type: DimType) -> DimensionNode {
210 DimensionNode::Leaf(Dimension {
211 graphql_name: graphql_name.to_string(),
212 column: column.to_string(),
213 dim_type,
214 description: None,
215 })
216}
217
218pub fn dim_desc(graphql_name: &str, column: &str, dim_type: DimType, desc: &str) -> DimensionNode {
219 DimensionNode::Leaf(Dimension {
220 graphql_name: graphql_name.to_string(),
221 column: column.to_string(),
222 dim_type,
223 description: Some(desc.to_string()),
224 })
225}
226
227pub fn dim_group(graphql_name: &str, children: Vec<DimensionNode>) -> DimensionNode {
228 DimensionNode::Group {
229 graphql_name: graphql_name.to_string(),
230 description: None,
231 children,
232 }
233}
234
235pub fn dim_group_desc(graphql_name: &str, desc: &str, children: Vec<DimensionNode>) -> DimensionNode {
236 DimensionNode::Group {
237 graphql_name: graphql_name.to_string(),
238 description: Some(desc.to_string()),
239 children,
240 }
241}