elasticube_core/cube/
dimension.rs

1//! Dimension types and operations
2
3use arrow::datatypes::DataType;
4use serde::{Deserialize, Serialize};
5
6/// Represents a dimension in the cube
7///
8/// A dimension is a categorical attribute used for slicing and dicing data
9/// (e.g., date, region, product category).
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct Dimension {
12    /// Name of the dimension
13    name: String,
14
15    /// Data type of the dimension
16    data_type: DataType,
17
18    /// Estimated cardinality (number of unique values)
19    /// None if unknown
20    cardinality: Option<usize>,
21
22    /// Whether this dimension can be null
23    nullable: bool,
24
25    /// User-provided description
26    description: Option<String>,
27}
28
29impl Dimension {
30    /// Create a new dimension
31    pub fn new(name: impl Into<String>, data_type: DataType) -> Self {
32        Self {
33            name: name.into(),
34            data_type,
35            cardinality: None,
36            nullable: true,
37            description: None,
38        }
39    }
40
41    /// Create a new dimension with full configuration
42    pub fn with_config(
43        name: impl Into<String>,
44        data_type: DataType,
45        nullable: bool,
46        cardinality: Option<usize>,
47        description: Option<String>,
48    ) -> Self {
49        Self {
50            name: name.into(),
51            data_type,
52            cardinality,
53            nullable,
54            description,
55        }
56    }
57
58    /// Get the dimension name
59    pub fn name(&self) -> &str {
60        &self.name
61    }
62
63    /// Get the data type
64    pub fn data_type(&self) -> &DataType {
65        &self.data_type
66    }
67
68    /// Get the cardinality
69    pub fn cardinality(&self) -> Option<usize> {
70        self.cardinality
71    }
72
73    /// Check if the dimension is nullable
74    pub fn is_nullable(&self) -> bool {
75        self.nullable
76    }
77
78    /// Get the description
79    pub fn description(&self) -> Option<&str> {
80        self.description.as_deref()
81    }
82
83    /// Set the cardinality
84    pub fn set_cardinality(&mut self, cardinality: usize) {
85        self.cardinality = Some(cardinality);
86    }
87
88    /// Set the description
89    pub fn set_description(&mut self, description: impl Into<String>) {
90        self.description = Some(description.into());
91    }
92
93    /// Builder-style: set cardinality
94    pub fn with_cardinality(mut self, cardinality: usize) -> Self {
95        self.cardinality = Some(cardinality);
96        self
97    }
98
99    /// Builder-style: set nullable
100    pub fn with_nullable(mut self, nullable: bool) -> Self {
101        self.nullable = nullable;
102        self
103    }
104
105    /// Builder-style: set description
106    pub fn with_description(mut self, description: impl Into<String>) -> Self {
107        self.description = Some(description.into());
108        self
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_dimension_creation() {
118        let dim = Dimension::new("region", DataType::Utf8);
119        assert_eq!(dim.name(), "region");
120        assert_eq!(dim.data_type(), &DataType::Utf8);
121        assert!(dim.is_nullable());
122        assert_eq!(dim.cardinality(), None);
123    }
124
125    #[test]
126    fn test_dimension_builder() {
127        let dim = Dimension::new("country", DataType::Utf8)
128            .with_cardinality(195)
129            .with_nullable(false)
130            .with_description("ISO country code");
131
132        assert_eq!(dim.name(), "country");
133        assert_eq!(dim.cardinality(), Some(195));
134        assert!(!dim.is_nullable());
135        assert_eq!(dim.description(), Some("ISO country code"));
136    }
137}