dioxus_tabular/
columns.rs

1#[cfg(feature = "export")]
2use crate::Exporter;
3use crate::{Row, TableColumn, TableContext};
4use dioxus::prelude::*;
5
6/// Trait automatically implemented for tuples of [`TableColumn`]s.
7///
8/// This trait is implemented for tuples of 1 to 12 columns via macros.
9/// You don't need to implement this trait manually.
10///
11/// # Example
12///
13/// ```
14/// # use dioxus_tabular::*;
15/// # #[derive(Clone, PartialEq)]
16/// # struct Col1;
17/// # #[derive(Clone, PartialEq)]
18/// # struct Col2;
19/// # #[derive(Clone, PartialEq)]
20/// # struct User { id: u32 }
21/// # impl Row for User {
22/// #     fn key(&self) -> impl Into<String> { self.id.to_string() }
23/// # }
24/// # impl TableColumn<User> for Col1 {
25/// #     fn column_name(&self) -> String { "col1".into() }
26/// #     fn render_header(&self, _: ColumnContext, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
27/// #     fn render_cell(&self, _: ColumnContext, _: &User, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
28/// # }
29/// # impl TableColumn<User> for Col2 {
30/// #     fn column_name(&self) -> String { "col2".into() }
31/// #     fn render_header(&self, _: ColumnContext, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
32/// #     fn render_cell(&self, _: ColumnContext, _: &User, _: Vec<dioxus::prelude::Attribute>) -> dioxus::prelude::Element { todo!() }
33/// # }
34/// // Single column
35/// let cols1 = (Col1,);
36///
37/// // Multiple columns (tuple)
38/// let cols2 = (Col1, Col2);
39/// ```
40#[allow(clippy::type_complexity, reason = "to provide internal API")]
41pub trait Columns<R: Row>: Clone + PartialEq + 'static {
42    /// Returns the names of all columns.
43    fn column_names(&self) -> Vec<String>;
44    /// Returns header renderers for all columns.
45    fn headers(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, Vec<Attribute>) -> Element + '_>>;
46    /// Returns cell renderers for all columns.
47    fn columns(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, &R, Vec<Attribute>) -> Element + '_>>;
48    /// Returns true if the row passes all column filters.
49    fn filter(&self, row: &R) -> bool;
50    /// Returns comparators for all columns.
51    fn compare(&self) -> Vec<Box<dyn Fn(&R, &R) -> std::cmp::Ordering + '_>>;
52}
53
54/// A serializable header with export configuration.
55#[cfg(feature = "export")]
56pub struct SerializableHeader<'a> {
57    pub header_fn: Box<dyn Fn() -> String + 'a>,
58    pub include_in_export: bool,
59}
60
61/// A serializable cell with export configuration.
62#[cfg(feature = "export")]
63#[allow(clippy::type_complexity)]
64pub struct SerializableCell<'a, R, E: Exporter> {
65    pub cell_fn: Box<dyn Fn(usize, usize, &R, &mut E) -> Result<(), E::Error> + 'a>,
66    pub include_in_export: bool,
67}
68
69/// Trait for columns that support serialization (export feature).
70///
71/// Automatically implemented for tuples of [`SerializableColumn`](crate::SerializableColumn)s.
72#[cfg(feature = "export")]
73pub trait SerializableColumns<R: Row>: Columns<R> {
74    /// Returns header serializers for all columns including whether to include in export.
75    fn serialize_headers(&self) -> Vec<SerializableHeader<'_>>;
76    /// Returns cell serializers for all columns including whether to include in export.
77    fn serialize_cell<E: Exporter>(&self) -> Vec<SerializableCell<'_, R, E>>;
78}
79
80macro_rules! columns {
81    ($($number:tt => $column:ident),*) => {
82        impl<$($column: TableColumn<R>),*, R: Row> Columns<R> for ($($column),*,) {
83            fn column_names(&self) -> Vec<String> {
84                vec![$(self.$number.column_name()),*]
85            }
86            fn headers(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, Vec<Attribute>) -> Element + '_>> {
87                vec![$(Box::new(move |context, attributes| {
88                    self.$number.render_header(context.data.column_context($number), attributes)
89                })),*]
90            }
91            fn columns(&self) -> Vec<Box<dyn Fn(&TableContext<Self>, &R, Vec<Attribute>) -> Element + '_>> {
92                vec![$(Box::new(move |context, row, attributes| {
93                    self.$number.render_cell(context.data.column_context($number), row, attributes)
94                })),*]
95            }
96            fn filter(&self, row: &R) -> bool {
97                $(self.$number.filter(row) &&)* true
98            }
99            fn compare(&self) -> Vec<Box<dyn Fn(&R, &R) -> std::cmp::Ordering + '_>> {
100                vec![$(Box::new(move |a, b| self.$number.compare(a, b))),*]
101            }
102        }
103        #[cfg(feature = "export")]
104        serialize_columns!($($number => $column),*);
105    }
106}
107
108#[cfg(feature = "export")]
109macro_rules! serialize_columns {
110    ($($number:tt => $column:ident),*) => {
111        impl<$($column: crate::SerializableColumn<R>),*, R: Row> SerializableColumns<R> for ($($column),*,) {
112            fn serialize_headers(&self) -> Vec<SerializableHeader<'_>> {
113                vec![$(SerializableHeader {
114                    header_fn: Box::new(move || self.$number.header()),
115                    include_in_export: self.$number.include_in_export(),
116                }),*]
117            }
118            fn serialize_cell<Ex: Exporter>(&self) -> Vec<SerializableCell<'_, R, Ex>> {
119                vec![$(SerializableCell {
120                    cell_fn: Box::new(move |row_index, col_index, row, exporter| -> Result<(), Ex::Error> {
121                        exporter.serialize_cell(row_index, col_index, self.$number.serialize_cell(row))
122                    }),
123                    include_in_export: self.$number.include_in_export(),
124                }),*]
125            }
126        }
127    }
128}
129
130columns!(0 => A);
131columns!(0 => A, 1 => B);
132columns!(0 => A, 1 => B, 2 => C);
133columns!(0 => A, 1 => B, 2 => C, 3 => D);
134columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E);
135columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F);
136columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G);
137columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H);
138columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I);
139columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J);
140columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K);
141columns!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K, 11 => L);