Skip to main content

vantage_table/column/
collection.rs

1use std::sync::Arc;
2
3use indexmap::IndexMap;
4
5use crate::{column::flags::ColumnFlag, traits::column_like::ColumnLike};
6
7/// Extension trait for filtering collections of columns
8///
9/// This trait provides convenient methods for filtering columns by flags,
10/// allowing for fluent API usage.
11///
12/// # Examples
13///
14/// ```rust,ignore
15/// use vantage_table::{ColumnCollectionExt, ColumnFlag};
16///
17/// // Get only visible columns (exclude hidden)
18/// let visible = table.columns().exclude(ColumnFlag::Hidden);
19///
20/// // Get only mandatory columns
21/// let mandatory = table.columns().only(ColumnFlag::Mandatory);
22///
23/// // Chain filters
24/// let visible_mandatory = table.columns()
25///     .exclude(ColumnFlag::Hidden)
26///     .only(ColumnFlag::Mandatory);
27/// ```
28pub trait ColumnCollectionExt {
29    /// Filter columns to only include those with the specified flag
30    ///
31    /// # Arguments
32    ///
33    /// * `flag` - The column flag to filter by
34    ///
35    /// # Returns
36    ///
37    /// A new Arc containing only columns that have the specified flag
38    fn only(&self, flag: ColumnFlag) -> Arc<IndexMap<String, Arc<dyn ColumnLike>>>;
39
40    /// Filter columns to exclude those with the specified flag
41    ///
42    /// # Arguments
43    ///
44    /// * `flag` - The column flag to filter out
45    ///
46    /// # Returns
47    ///
48    /// A new Arc containing only columns that do not have the specified flag
49    fn exclude(&self, flag: ColumnFlag) -> Arc<IndexMap<String, Arc<dyn ColumnLike>>>;
50}
51
52impl ColumnCollectionExt for Arc<IndexMap<String, Arc<dyn ColumnLike>>> {
53    fn only(&self, flag: ColumnFlag) -> Arc<IndexMap<String, Arc<dyn ColumnLike>>> {
54        let filtered: IndexMap<String, Arc<dyn ColumnLike>> = self
55            .iter()
56            .filter(|(_, col)| col.flags().contains(&flag))
57            .map(|(k, v)| (k.clone(), v.clone()))
58            .collect();
59        Arc::new(filtered)
60    }
61
62    fn exclude(&self, flag: ColumnFlag) -> Arc<IndexMap<String, Arc<dyn ColumnLike>>> {
63        let filtered: IndexMap<String, Arc<dyn ColumnLike>> = self
64            .iter()
65            .filter(|(_, col)| !col.flags().contains(&flag))
66            .map(|(k, v)| (k.clone(), v.clone()))
67            .collect();
68        Arc::new(filtered)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::column::core::Column;
75
76    use super::*;
77
78    #[test]
79    fn test_only_filter() {
80        let mut columns: IndexMap<String, Arc<dyn ColumnLike>> = IndexMap::new();
81        columns.insert(
82            "id".to_string(),
83            Arc::new(Column::new("id").with_flags(&[ColumnFlag::Mandatory])),
84        );
85        columns.insert(
86            "name".to_string(),
87            Arc::new(Column::new("name").with_flags(&[ColumnFlag::Mandatory])),
88        );
89        columns.insert(
90            "description".to_string(),
91            Arc::new(Column::new("description")),
92        );
93
94        let arc_columns = Arc::new(columns);
95        let mandatory = arc_columns.only(ColumnFlag::Mandatory);
96
97        assert_eq!(mandatory.len(), 2);
98        assert!(mandatory.contains_key("id"));
99        assert!(mandatory.contains_key("name"));
100        assert!(!mandatory.contains_key("description"));
101    }
102
103    #[test]
104    fn test_exclude_filter() {
105        let mut columns: IndexMap<String, Arc<dyn ColumnLike>> = IndexMap::new();
106        columns.insert(
107            "id".to_string(),
108            Arc::new(Column::new("id").with_flags(&[ColumnFlag::Hidden])),
109        );
110        columns.insert("name".to_string(), Arc::new(Column::new("name")));
111        columns.insert(
112            "password".to_string(),
113            Arc::new(Column::new("password").with_flags(&[ColumnFlag::Hidden])),
114        );
115
116        let arc_columns = Arc::new(columns);
117        let visible = arc_columns.exclude(ColumnFlag::Hidden);
118
119        assert_eq!(visible.len(), 1);
120        assert!(visible.contains_key("name"));
121        assert!(!visible.contains_key("id"));
122        assert!(!visible.contains_key("password"));
123    }
124
125    #[test]
126    fn test_chaining_filters() {
127        let mut columns: IndexMap<String, Arc<dyn ColumnLike>> = IndexMap::new();
128        columns.insert(
129            "id".to_string(),
130            Arc::new(Column::new("id").with_flags(&[ColumnFlag::Mandatory])),
131        );
132        columns.insert(
133            "name".to_string(),
134            Arc::new(Column::new("name").with_flags(&[ColumnFlag::Mandatory, ColumnFlag::Hidden])),
135        );
136        columns.insert("email".to_string(), Arc::new(Column::new("email")));
137
138        let arc_columns = Arc::new(columns);
139        let visible_mandatory = arc_columns
140            .exclude(ColumnFlag::Hidden)
141            .only(ColumnFlag::Mandatory);
142
143        assert_eq!(visible_mandatory.len(), 1);
144        assert!(visible_mandatory.contains_key("id"));
145        assert!(!visible_mandatory.contains_key("name")); // hidden
146        assert!(!visible_mandatory.contains_key("email")); // not mandatory
147    }
148}