Skip to main content

ravenrs_sheet/model/features/
pivot.rs

1use super::{CellRef, RangeRef};
2use serde::{Deserialize, Serialize};
3
4// ---------------------------------------------------------------- pivot tables
5
6/// How a PivotTable lays out its row fields.
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
8pub enum PivotLayout {
9    /// All row fields in one column, inner items indented ("Row Labels" header).
10    #[default]
11    Compact,
12    /// One column per row field; each item on its own row.
13    Outline,
14    /// One column per row field; inner items start on their parent's row, totals below.
15    Tabular,
16}
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
19pub enum PivotSort {
20    #[default]
21    Asc,
22    Desc,
23    /// Source order (first appearance).
24    None,
25}
26
27/// Grouping of a date field (values are date serials).
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
29pub enum PivotDateGroup {
30    #[default]
31    None,
32    Years,
33    Quarters,
34    Months,
35    Days,
36}
37
38/// A field placed in the Rows or Columns area.
39#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
40#[serde(default)]
41pub struct PivotField {
42    /// Source column header name.
43    pub source_col: String,
44    pub sort: PivotSort,
45    pub date_group: PivotDateGroup,
46    /// Item labels whose detail is hidden (collapsed).
47    pub collapsed_items: Vec<String>,
48}
49
50/// Summarize-values-by function.
51#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
52pub enum PivotFunc {
53    #[default]
54    Sum,
55    Count,
56    Average,
57    Max,
58    Min,
59    Product,
60    CountNumbers,
61    StdDev,
62    StdDevP,
63    Var,
64    VarP,
65}
66
67/// Show-values-as transform.
68#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
69pub enum PivotShowAs {
70    #[default]
71    Normal,
72    PercentOfGrandTotal,
73    PercentOfColumnTotal,
74    PercentOfRowTotal,
75    /// Running total in the first row field.
76    RunningTotal,
77    /// Rank largest to smallest among sibling row items.
78    Rank,
79}
80
81/// A field in the Values area.
82#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
83#[serde(default)]
84pub struct PivotValue {
85    pub source_col: String,
86    pub func: PivotFunc,
87    /// Caption, e.g. "Sum of Sales".
88    pub name: String,
89    pub show_as: PivotShowAs,
90    pub number_format: Option<String>,
91}
92
93/// A report filter (or, for a field also in Rows/Columns, its hidden-items filter).
94#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
95#[serde(default)]
96pub struct PivotFilter {
97    pub source_col: String,
98    /// Item labels shown; `None` = all.
99    pub selected: Option<Vec<String>>,
100}
101
102/// A PivotTable: its definition plus where its output was last written. The output itself is
103/// static cell values and styles on the sheet, rewritten on every refresh.
104#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
105#[serde(default)]
106pub struct PivotTable {
107    pub id: u32,
108    pub name: String,
109    /// Source: range formula text (`Sheet1!$A$1:$E$200`), a table name or a defined name.
110    pub source: String,
111    /// Top-left of the output body on the pivot's sheet.
112    pub anchor: CellRef,
113    pub rows: Vec<PivotField>,
114    pub columns: Vec<PivotField>,
115    pub values: Vec<PivotValue>,
116    pub filters: Vec<PivotFilter>,
117    pub layout: PivotLayout,
118    /// PivotTable Options › Data › Enable show details: Show Details lists a value cell's
119    /// source records only while this is on.
120    pub enable_show_details: bool,
121    /// Grand total for each row (the "Grand Total" column), like Excel's "On for Rows".
122    pub grand_totals_rows: bool,
123    /// Grand total for each column (the "Grand Total" row), like Excel's "On for Columns".
124    pub grand_totals_cols: bool,
125    pub subtotals: bool,
126    /// Subtotals at the top of each group (Compact/Outline); otherwise below it.
127    pub subtotals_top: bool,
128    pub style: String,
129    pub show_headers: bool,
130    /// Area written by the last refresh.
131    pub last_range: Option<RangeRef>,
132    /// Calculated fields, usable in the Values area by name.
133    pub calculated_fields: Vec<PivotCalcField>,
134    /// The Σ Values pseudo-field (it exists with two or more value fields) sits in Rows rather
135    /// than Columns (Excel's "values on rows").
136    pub values_on_rows: bool,
137    /// Position of Σ Values among the row fields (`None` = after the last). Only used with
138    /// `values_on_rows`: in Columns Σ Values always comes after the last column field.
139    pub values_position: Option<usize>,
140}
141
142/// A calculated field: a formula over other fields (`=Sales*0.1`, `='Unit Price'*Units`). As in
143/// Excel, it is evaluated on the sums of the fields it names for each report cell, not per record.
144#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
145#[serde(default)]
146pub struct PivotCalcField {
147    pub name: String,
148    /// Formula text, starting with `=`.
149    pub formula: String,
150}
151
152impl Default for PivotTable {
153    fn default() -> Self {
154        PivotTable {
155            id: 0,
156            name: "PivotTable1".into(),
157            source: String::new(),
158            anchor: CellRef::default(),
159            rows: vec![],
160            columns: vec![],
161            values: vec![],
162            filters: vec![],
163            layout: PivotLayout::Compact,
164            enable_show_details: true,
165            grand_totals_rows: true,
166            grand_totals_cols: true,
167            subtotals: true,
168            subtotals_top: true,
169            style: "PivotStyleLight16".into(),
170            show_headers: true,
171            last_range: None,
172            calculated_fields: vec![],
173            values_on_rows: false,
174            values_position: None,
175        }
176    }
177}