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 pub description: String,
67}
68
69impl CubeDefinition {
70 pub fn table_for_chain(&self, chain: &str) -> String {
71 self.table_pattern.replace("{chain}", chain)
72 }
73
74 pub fn qualified_table(&self, chain: &str) -> String {
75 format!("{}.{}", self.schema, self.table_for_chain(chain))
76 }
77
78 pub fn flat_dimensions(&self) -> Vec<(String, Dimension)> {
79 let mut out = Vec::new();
80 for node in &self.dimensions {
81 collect_leaves(node, "", &mut out);
82 }
83 out
84 }
85}
86
87fn collect_leaves(node: &DimensionNode, prefix: &str, out: &mut Vec<(String, Dimension)>) {
88 match node {
89 DimensionNode::Leaf(dim) => {
90 let path = if prefix.is_empty() {
91 dim.graphql_name.clone()
92 } else {
93 format!("{}_{}", prefix, dim.graphql_name)
94 };
95 out.push((path, dim.clone()));
96 }
97 DimensionNode::Group { graphql_name, children } => {
98 let new_prefix = if prefix.is_empty() {
99 graphql_name.clone()
100 } else {
101 format!("{prefix}_{graphql_name}")
102 };
103 for child in children {
104 collect_leaves(child, &new_prefix, out);
105 }
106 }
107 }
108}
109
110pub struct CubeBuilder {
115 def: CubeDefinition,
116}
117
118impl CubeBuilder {
119 pub fn new(name: &str) -> Self {
120 Self {
121 def: CubeDefinition {
122 name: name.to_string(),
123 schema: String::new(),
124 table_pattern: String::new(),
125 chain_column: None,
126 dimensions: Vec::new(),
127 metrics: Vec::new(),
128 selectors: Vec::new(),
129 default_filters: Vec::new(),
130 default_limit: 25,
131 max_limit: 10000,
132 use_final: false,
133 description: String::new(),
134 },
135 }
136 }
137
138 pub fn schema(mut self, schema: &str) -> Self {
139 self.def.schema = schema.to_string();
140 self
141 }
142
143 pub fn table(mut self, pattern: &str) -> Self {
144 self.def.table_pattern = pattern.to_string();
145 self
146 }
147
148 pub fn chain_column(mut self, column: &str) -> Self {
149 self.def.chain_column = Some(column.to_string());
150 self
151 }
152
153 pub fn dimension(mut self, node: DimensionNode) -> Self {
154 self.def.dimensions.push(node);
155 self
156 }
157
158 pub fn metric(mut self, name: &str) -> Self {
159 self.def.metrics.push(name.to_string());
160 self
161 }
162
163 pub fn metrics(mut self, names: &[&str]) -> Self {
164 self.def.metrics.extend(names.iter().map(|s| s.to_string()));
165 self
166 }
167
168 pub fn selector(mut self, sel: SelectorDef) -> Self {
169 self.def.selectors.push(sel);
170 self
171 }
172
173 pub fn default_filter(mut self, column: &str, value: &str) -> Self {
174 self.def.default_filters.push((column.to_string(), value.to_string()));
175 self
176 }
177
178 pub fn default_limit(mut self, limit: u32) -> Self {
179 self.def.default_limit = limit;
180 self
181 }
182
183 pub fn max_limit(mut self, limit: u32) -> Self {
184 self.def.max_limit = limit;
185 self
186 }
187
188 pub fn use_final(mut self, val: bool) -> Self {
189 self.def.use_final = val;
190 self
191 }
192
193 pub fn description(mut self, desc: &str) -> Self {
194 self.def.description = desc.to_string();
195 self
196 }
197
198 pub fn build(self) -> CubeDefinition {
199 self.def
200 }
201}
202
203pub fn dim(graphql_name: &str, column: &str, dim_type: DimType) -> DimensionNode {
208 DimensionNode::Leaf(Dimension {
209 graphql_name: graphql_name.to_string(),
210 column: column.to_string(),
211 dim_type,
212 })
213}
214
215pub fn dim_group(graphql_name: &str, children: Vec<DimensionNode>) -> DimensionNode {
216 DimensionNode::Group {
217 graphql_name: graphql_name.to_string(),
218 children,
219 }
220}