libxtabml/
types.rs

1use serde::{Deserialize, Serialize};
2
3/// Root element of an XtabML document
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct XtabML {
6    pub version: String,
7    pub date: Option<String>,
8    pub time: Option<String>,
9    pub origin: Option<String>,
10    pub user: Option<String>,
11
12    /// Languages used in the document
13    pub languages: Vec<Language>,
14
15    /// Control types defined in the document
16    pub control_types: Vec<ControlType>,
17
18    /// Statistic types defined in the document
19    pub statistic_types: Vec<StatisticType>,
20
21    /// Report-level controls
22    pub controls: Vec<Control>,
23
24    /// Tables in the document
25    pub tables: Vec<Table>,
26}
27
28/// Language specification for alternative texts
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct Language {
31    pub lang: String,
32    pub base: Option<String>,
33    pub description: String,
34}
35
36/// Control type definition
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ControlType {
39    pub name: String,
40    pub status: Option<String>,
41    pub text: String,
42}
43
44/// Statistic type definition
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct StatisticType {
47    pub name: String,
48    pub text: String,
49}
50
51/// Control element (metadata)
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Control {
54    pub r#type: String,
55    pub text: String,
56}
57
58/// A table in the XtabML document
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct Table {
61    pub name: Option<String>,
62    pub title: String,
63
64    /// Controls specific to this table (e.g., weight, base)
65    pub controls: Vec<Control>,
66
67    /// Row edge (axis="r")
68    pub row_edge: Option<Edge>,
69
70    /// Column edge (axis="c")
71    pub column_edge: Option<Edge>,
72
73    /// Statistics included in this table
74    pub statistics: Vec<Statistic>,
75
76    /// The data matrix
77    pub data: TableData,
78}
79
80/// Edge definition (row or column)
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct Edge {
83    pub axis: String, // "r" for row, "c" for column
84    pub groups: Vec<Group>,
85}
86
87/// A group within an edge
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Group {
90    pub elements: Vec<Element>,
91    pub summaries: Vec<Summary>,
92}
93
94/// An element (item) in a group
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct Element {
97    pub text: String,
98    pub index: Option<i32>,
99}
100
101/// A summary element
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct Summary {
104    pub text: String,
105}
106
107/// Statistic specification
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct Statistic {
110    pub r#type: String,
111}
112
113/// Table data matrix
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct TableData {
116    pub rows: Vec<DataRow>,
117}
118
119/// Represents multiple data series in a row
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct DataRowSeries {
122    pub statistic: Option<Statistic>,
123    pub cells: Vec<DataCell>,
124}
125
126/// A row in the data matrix
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct DataRow {
129    pub data_row_series: Vec<DataRowSeries>,
130}
131
132/// A cell in the data matrix
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct DataCell {
135    pub value: Option<String>,
136    pub is_missing: bool,
137}
138
139impl Default for DataCell {
140    fn default() -> Self {
141        Self {
142            value: None,
143            is_missing: false,
144        }
145    }
146}
147
148/// Convenience structure for accessing table data by statistic type
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct StatisticData {
151    pub statistic_type: String,
152    pub values: Vec<Vec<Option<String>>>,
153}
154
155impl Table {
156    /// Get all statistic types in this table
157    pub fn statistic_types(&self) -> Vec<&str> {
158        self.statistics.iter().map(|s| s.r#type.as_str()).collect()
159    }
160
161    /// Get the shape of the table (rows, columns)
162    pub fn shape(&self) -> (usize, usize) {
163        let rows = self.data.rows.len();
164        let cols = if rows > 0 {
165            self.data.rows[0].data_row_series[0].cells.len()
166        } else {
167            0
168        };
169        (rows, cols)
170    }
171
172    /// Get data for a specific statistic type
173    pub fn get_statistic_data(&self, statistic_index: usize) -> Option<Vec<Vec<Option<String>>>> {
174        if statistic_index >= self.statistics.len() {
175            return None;
176        }
177
178        let statistics_count = self.statistics.len();
179        let _values_per_cell = (self.data.rows.len() / statistics_count).max(1);
180
181        let result = Vec::new();
182
183        // Extract values for this statistic
184        // for (row_idx, row) in self.data.rows.iter().enumerate() {
185        //     if row_idx % statistics_count == statistic_index {
186        //         let cell_values: Vec<Option<String>> = row
187        //             .cells
188        //             .iter()
189        //             .map(|cell| {
190        //                 if cell.is_missing {
191        //                     None
192        //                 } else {
193        //                     cell.value.clone()
194        //                 }
195        //             })
196        //             .collect();
197        //         result.push(cell_values);
198        //     }
199        // }
200
201        Some(result)
202    }
203
204    /// Get row labels from the row edge
205    pub fn row_labels(&self) -> Vec<String> {
206        self.row_edge
207            .as_ref()
208            .and_then(|e| e.groups.first())
209            .map(|g| g.elements.iter().map(|e| e.text.clone()).collect())
210            .unwrap_or_default()
211    }
212
213    /// Get column labels from the column edge
214    pub fn column_labels(&self) -> Vec<String> {
215        self.column_edge
216            .as_ref()
217            .and_then(|e| e.groups.first())
218            .map(|g| g.elements.iter().map(|e| e.text.clone()).collect())
219            .unwrap_or_default()
220    }
221}