Skip to main content

sheetkit_xml/
sparkline.rs

1//! Sparkline XML schema structures.
2//!
3//! Sparklines are stored in worksheet extension lists (extLst) using the
4//! x14 namespace (http://schemas.microsoft.com/office/spreadsheetml/2009/9/main).
5
6use serde::{Deserialize, Serialize};
7
8/// A group of sparklines with shared settings.
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10#[serde(rename = "x14:sparklineGroup")]
11pub struct SparklineGroup {
12    /// Sparkline type: "line", "column", or "stacked" (win/loss).
13    #[serde(rename = "@type", skip_serializing_if = "Option::is_none")]
14    pub sparkline_type: Option<String>,
15
16    /// Whether to display markers on data points.
17    #[serde(rename = "@markers", skip_serializing_if = "Option::is_none")]
18    pub markers: Option<bool>,
19
20    /// Whether to highlight the high point.
21    #[serde(rename = "@high", skip_serializing_if = "Option::is_none")]
22    pub high: Option<bool>,
23
24    /// Whether to highlight the low point.
25    #[serde(rename = "@low", skip_serializing_if = "Option::is_none")]
26    pub low: Option<bool>,
27
28    /// Whether to highlight the first point.
29    #[serde(rename = "@first", skip_serializing_if = "Option::is_none")]
30    pub first: Option<bool>,
31
32    /// Whether to highlight the last point.
33    #[serde(rename = "@last", skip_serializing_if = "Option::is_none")]
34    pub last: Option<bool>,
35
36    /// Whether to highlight negative points.
37    #[serde(rename = "@negative", skip_serializing_if = "Option::is_none")]
38    pub negative: Option<bool>,
39
40    /// Whether to display an axis.
41    #[serde(rename = "@displayXAxis", skip_serializing_if = "Option::is_none")]
42    pub display_x_axis: Option<bool>,
43
44    /// Line weight in points.
45    #[serde(rename = "@lineWeight", skip_serializing_if = "Option::is_none")]
46    pub line_weight: Option<f64>,
47
48    /// Minimum axis type: "individual" or "group".
49    #[serde(rename = "@minAxisType", skip_serializing_if = "Option::is_none")]
50    pub min_axis_type: Option<String>,
51
52    /// Maximum axis type: "individual" or "group".
53    #[serde(rename = "@maxAxisType", skip_serializing_if = "Option::is_none")]
54    pub max_axis_type: Option<String>,
55
56    /// Individual sparklines in this group.
57    #[serde(rename = "x14:sparklines")]
58    pub sparklines: SparklineList,
59}
60
61/// Container for individual sparklines.
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63pub struct SparklineList {
64    #[serde(rename = "x14:sparkline", default)]
65    pub items: Vec<Sparkline>,
66}
67
68/// A single sparkline mapping data to a cell location.
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct Sparkline {
71    /// Data source range (e.g., "Sheet1!A1:A10").
72    #[serde(rename = "xm:f")]
73    pub formula: String,
74
75    /// Cell location where the sparkline is rendered (e.g., "B1").
76    #[serde(rename = "xm:sqref")]
77    pub sqref: String,
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_sparkline_group_default() {
86        let group = SparklineGroup {
87            sparkline_type: Some("line".to_string()),
88            markers: None,
89            high: None,
90            low: None,
91            first: None,
92            last: None,
93            negative: None,
94            display_x_axis: None,
95            line_weight: None,
96            min_axis_type: None,
97            max_axis_type: None,
98            sparklines: SparklineList {
99                items: vec![Sparkline {
100                    formula: "Sheet1!A1:A10".to_string(),
101                    sqref: "B1".to_string(),
102                }],
103            },
104        };
105        assert_eq!(group.sparklines.items.len(), 1);
106    }
107
108    #[test]
109    fn test_sparkline_with_options() {
110        let group = SparklineGroup {
111            sparkline_type: Some("column".to_string()),
112            markers: Some(true),
113            high: Some(true),
114            low: Some(true),
115            first: None,
116            last: None,
117            negative: Some(true),
118            display_x_axis: Some(true),
119            line_weight: Some(0.75),
120            min_axis_type: Some("group".to_string()),
121            max_axis_type: Some("group".to_string()),
122            sparklines: SparklineList { items: vec![] },
123        };
124        assert_eq!(group.sparkline_type, Some("column".to_string()));
125        assert_eq!(group.markers, Some(true));
126    }
127}