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