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)]
48pub struct JoinDef {
49 pub field_name: String,
51 pub target_cube: String,
53 pub conditions: Vec<(String, String)>,
55 pub description: Option<String>,
56}
57
58pub fn join_def(field_name: &str, target_cube: &str, conditions: &[(&str, &str)]) -> JoinDef {
59 JoinDef {
60 field_name: field_name.to_string(),
61 target_cube: target_cube.to_string(),
62 conditions: conditions.iter().map(|(l, r)| (l.to_string(), r.to_string())).collect(),
63 description: None,
64 }
65}
66
67pub fn join_def_desc(field_name: &str, target_cube: &str, conditions: &[(&str, &str)], desc: &str) -> JoinDef {
68 JoinDef {
69 field_name: field_name.to_string(),
70 target_cube: target_cube.to_string(),
71 conditions: conditions.iter().map(|(l, r)| (l.to_string(), r.to_string())).collect(),
72 description: Some(desc.to_string()),
73 }
74}
75
76#[derive(Debug, Clone)]
77pub struct CubeDefinition {
78 pub name: String,
79 pub schema: String,
80 pub table_pattern: String,
85 pub chain_column: Option<String>,
89 pub dimensions: Vec<DimensionNode>,
90 pub metrics: Vec<String>,
91 pub selectors: Vec<SelectorDef>,
92 pub default_filters: Vec<(String, String)>,
93 pub default_limit: u32,
94 pub max_limit: u32,
95 pub use_final: bool,
97 pub description: String,
99 pub joins: Vec<JoinDef>,
101}
102
103impl CubeDefinition {
104 pub fn table_for_chain(&self, chain: &str) -> String {
105 self.table_pattern.replace("{chain}", chain)
106 }
107
108 pub fn qualified_table(&self, chain: &str) -> String {
109 format!("{}.{}", self.schema, self.table_for_chain(chain))
110 }
111
112 pub fn flat_dimensions(&self) -> Vec<(String, Dimension)> {
113 let mut out = Vec::new();
114 for node in &self.dimensions {
115 collect_leaves(node, "", &mut out);
116 }
117 out
118 }
119}
120
121fn collect_leaves(node: &DimensionNode, prefix: &str, out: &mut Vec<(String, Dimension)>) {
122 match node {
123 DimensionNode::Leaf(dim) => {
124 let path = if prefix.is_empty() {
125 dim.graphql_name.clone()
126 } else {
127 format!("{}_{}", prefix, dim.graphql_name)
128 };
129 out.push((path, dim.clone()));
130 }
131 DimensionNode::Group { graphql_name, children, .. } => {
132 let new_prefix = if prefix.is_empty() {
133 graphql_name.clone()
134 } else {
135 format!("{prefix}_{graphql_name}")
136 };
137 for child in children {
138 collect_leaves(child, &new_prefix, out);
139 }
140 }
141 }
142}
143
144pub struct CubeBuilder {
149 def: CubeDefinition,
150}
151
152impl CubeBuilder {
153 pub fn new(name: &str) -> Self {
154 Self {
155 def: CubeDefinition {
156 name: name.to_string(),
157 schema: String::new(),
158 table_pattern: String::new(),
159 chain_column: None,
160 dimensions: Vec::new(),
161 metrics: Vec::new(),
162 selectors: Vec::new(),
163 default_filters: Vec::new(),
164 default_limit: 25,
165 max_limit: 10000,
166 use_final: false,
167 description: String::new(),
168 joins: Vec::new(),
169 },
170 }
171 }
172
173 pub fn schema(mut self, schema: &str) -> Self {
174 self.def.schema = schema.to_string();
175 self
176 }
177
178 pub fn table(mut self, pattern: &str) -> Self {
179 self.def.table_pattern = pattern.to_string();
180 self
181 }
182
183 pub fn chain_column(mut self, column: &str) -> Self {
184 self.def.chain_column = Some(column.to_string());
185 self
186 }
187
188 pub fn dimension(mut self, node: DimensionNode) -> Self {
189 self.def.dimensions.push(node);
190 self
191 }
192
193 pub fn metric(mut self, name: &str) -> Self {
194 self.def.metrics.push(name.to_string());
195 self
196 }
197
198 pub fn metrics(mut self, names: &[&str]) -> Self {
199 self.def.metrics.extend(names.iter().map(|s| s.to_string()));
200 self
201 }
202
203 pub fn selector(mut self, sel: SelectorDef) -> Self {
204 self.def.selectors.push(sel);
205 self
206 }
207
208 pub fn default_filter(mut self, column: &str, value: &str) -> Self {
209 self.def.default_filters.push((column.to_string(), value.to_string()));
210 self
211 }
212
213 pub fn default_limit(mut self, limit: u32) -> Self {
214 self.def.default_limit = limit;
215 self
216 }
217
218 pub fn max_limit(mut self, limit: u32) -> Self {
219 self.def.max_limit = limit;
220 self
221 }
222
223 pub fn use_final(mut self, val: bool) -> Self {
224 self.def.use_final = val;
225 self
226 }
227
228 pub fn description(mut self, desc: &str) -> Self {
229 self.def.description = desc.to_string();
230 self
231 }
232
233 pub fn join(mut self, j: JoinDef) -> Self {
234 self.def.joins.push(j);
235 self
236 }
237
238 pub fn joins(mut self, js: Vec<JoinDef>) -> Self {
239 self.def.joins.extend(js);
240 self
241 }
242
243 pub fn build(self) -> CubeDefinition {
244 self.def
245 }
246}
247
248pub fn dim(graphql_name: &str, column: &str, dim_type: DimType) -> DimensionNode {
253 DimensionNode::Leaf(Dimension {
254 graphql_name: graphql_name.to_string(),
255 column: column.to_string(),
256 dim_type,
257 description: None,
258 })
259}
260
261pub fn dim_desc(graphql_name: &str, column: &str, dim_type: DimType, desc: &str) -> DimensionNode {
262 DimensionNode::Leaf(Dimension {
263 graphql_name: graphql_name.to_string(),
264 column: column.to_string(),
265 dim_type,
266 description: Some(desc.to_string()),
267 })
268}
269
270pub fn dim_group(graphql_name: &str, children: Vec<DimensionNode>) -> DimensionNode {
271 DimensionNode::Group {
272 graphql_name: graphql_name.to_string(),
273 description: None,
274 children,
275 }
276}
277
278pub fn dim_group_desc(graphql_name: &str, desc: &str, children: Vec<DimensionNode>) -> DimensionNode {
279 DimensionNode::Group {
280 graphql_name: graphql_name.to_string(),
281 description: Some(desc.to_string()),
282 children,
283 }
284}