will refer to "version 0xaf" and "version 0xb0" members later on.
A legacy member consists of `n-sources` data sources, each of which
-has Metadata and Data.
+has `Metadata` and `Data`.
`member-size` is the size of the legacy binary member, in bytes.
-The Data and Strings above are commented out because the Metadata has
-some oddities that mean that the Data sometimes seems to start at an
-unexpected place. The following section goes into detail.
+The `Data` and `Strings` above are commented out because the
+`Metadata` has some oddities that mean that the `Data` sometimes seems
+to start at an unexpected place. The following section goes into
+detail.
<!-- toc -->
0-bytes. The names that appear in the corpus are very generic: usually
`tableData` for pivot table data or `source0` for chart data.
-A given Metadata's `data-offset` is the offset, in bytes, from the
-beginning of the member to the start of the corresponding Data. This
-allows programs to skip to the beginning of the data for a particular
-source. In every case in the corpus, the Data follow the Metadata in
-the same order, but it is important to use `data-offset` instead of
-reading sequentially through the file because of the exception described
-below.
-
-One SPV file in the corpus has legacy binary members with version
-0xb0 but a 28-byte `source-name` field (and only a single source). In
-practice, this means that the 64-byte `source-name` used in version 0xb0
-has a lot of 0-bytes in the middle followed by the `variable-name` of
-the following Data. As long as a reader treats the first 0-byte in the
-`source-name` as terminating the string, it can properly interpret these
-members.
+A given `Metadata`'s `data-offset` is the offset, in bytes, from the
+beginning of the member to the start of the corresponding `Data`.
+This allows programs to skip to the beginning of the data for a
+particular source. In every case in the corpus, the `Data` follow the
+`Metadata` in the same order, but it is important to use `data-offset`
+instead of reading sequentially through the file because of the
+exception described below.
+
+One SPV file in the corpus has legacy binary members with version 0xb0
+but a 28-byte `source-name` field (and only a single source). In
+practice, this means that the 64-byte `source-name` used in version
+0xb0 has a lot of 0-bytes in the middle followed by the
+`variable-name` of the following `Data`. As long as a reader treats
+the first 0-byte in the `source-name` as terminating the string, it
+can properly interpret these members.
The meaning of `x` in version 0xb0 is unknown.
Variable => byte*288[variable-name] double*[n-values]
```
-Data follow the `Metadata` in the legacy binary format, with sources
+`Data` follow the `Metadata` in the legacy binary format, with sources
in the same order (but readers should use the `data-offset` in
-`Metadata` records, rather than reading sequentially). Each Variable
-begins with a `variable-name` that generally indicates its role in the
-pivot table, e.g. "cell", "cellFormat", "dimension0categories",
-"dimension0group0", followed by the numeric data, one double per
-datum. A double with the maximum negative double `-DBL_MAX`
-represents the system-missing value `SYSMIS`.
+`Metadata` records, rather than reading sequentially). Each
+`Variable` begins with a `variable-name` that generally indicates its
+role in the pivot table, e.g. `cell`, `cellFormat`,
+`dimension0categories`, `dimension0group0`, followed by the numeric
+data, one double per datum. A double with the maximum negative double
+`-DBL_MAX` represents the system-missing value `SYSMIS`.
## String Data
Each variable may include a mix of numeric and string data values.
If a legacy binary member contains any string data, `Strings` is present;
-otherwise, it ends just after the last Data element.
+otherwise, it ends just after the last `Data` element.
The string data overlays the numeric data. When a variable includes
-any string data, its Variable represents the string values with a
+any string data, its `Variable` represents the string values with a
`SYSMIS` or NaN placeholder. (Not all such values need be
placeholders.)
## The `facetLayout` Element
```
-facetLayout => tableLayout setCellProperties[scp1]*
- facetLevel+ setCellProperties[scp2]*
+facetLayout => tableLayout (setCellProperties | facetLevel)+
tableLayout
:verticalTitlesInCorner=bool
page::PageSetup,
pivot::{PivotTable, TableProperties, Value},
spv::{
- legacy::Visualization,
+ legacy_xml::Visualization,
light::{LightError, LightTable},
},
};
mod css;
pub mod html;
-mod legacy;
+mod legacy_xml;
mod light;
#[derive(Debug, Display, thiserror::Error)]
Ok(result) => result,
Err(error) => panic!("{error:?}"),
};
- dbg!(_visualization);
+ //dbg!(_visualization);
Ok(PivotTable::new([]).into_item())
}
}
+++ /dev/null
-// PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-// details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program. If not, see <http://www.gnu.org/licenses/>.
-
-use std::marker::PhantomData;
-
-use serde::Deserialize;
-
-use crate::output::pivot::Color;
-
-#[derive(Debug)]
-struct Ref<T> {
- references: String,
- _phantom: PhantomData<T>,
-}
-
-impl<'de, T> Deserialize<'de> for Ref<T> {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- Ok(Self {
- references: String::deserialize(deserializer)?,
- _phantom: PhantomData,
- })
- }
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct Visualization {
- /// In format `YYYY-MM-DD`.
- #[serde(rename = "@date")]
- date: String,
- // Locale used for output, e.g. `en-US`.
- #[serde(rename = "@lang")]
- lang: String,
- /// Localized title of the pivot table.
- #[serde(rename = "@name")]
- name: String,
- /// Base style for the pivot table.
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- extension: Option<VisualizationExtension>,
- user_source: UserSource,
- #[serde(rename = "$value")]
- variables: Vec<Variable>,
- categorical_domain: Option<CategoricalDomain>,
- graph: Graph,
- #[serde(default, rename = "labelFrame")]
- label_frames: Vec<LabelFrame>,
- container: Option<Container>,
- #[serde(rename = "style")]
- styles: Vec<Style>,
- layer_controller: Option<LayerController>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename = "extension", rename_all = "camelCase")]
-struct VisualizationExtension;
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Variable {
- SourceVariable(SourceVariable),
- DerivedVariable(DerivedVariable),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SourceVariable {
- #[serde(rename = "@id")]
- id: String,
-
- /// The name of a variable within the source, corresponding to the
- /// `variable-name` in the `tableData.bin` member.
- #[serde(rename = "@sourceName")]
- source_name: String,
-
- /// Variable label, if any.
- #[serde(rename = "@label")]
- label: Option<String>,
-
- /// A variable whose string values correspond one-to-one with the values of
- /// this variable and are suitable as value labels.
- #[serde(rename = "@labelVariable")]
- label_variable: Option<Ref<SourceVariable>>,
-
- #[serde(default, rename = "extension")]
- extensions: Vec<VariableExtension>,
- format: Option<Format>,
- string_format: Option<StringFormat>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct DerivedVariable {
- #[serde(rename = "@id")]
- id: String,
-
- /// An expression that defines the variable's value.
- #[serde(rename = "@value")]
- value: String,
- #[serde(default, rename = "extension")]
- extensions: Vec<VariableExtension>,
- format: Option<Format>,
- string_format: Option<StringFormat>,
- #[serde(default, rename = "valueMapEntry")]
- value_map: Vec<ValueMapEntry>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename = "extension", rename_all = "camelCase")]
-struct VariableExtension {
- #[serde(rename = "@from")]
- from: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct UserSource {
- #[serde(rename = "@id")]
- id: String,
-
- #[serde(rename = "@missing")]
- missing: Option<Missing>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct CategoricalDomain {
- variable_reference: VariableReference,
- simple_sort: SimpleSort,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct VariableReference {
- #[serde(rename = "@ref")]
- reference: Option<String>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SimpleSort {
- #[serde(rename = "@method")]
- category_order: CategoryOrder,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct CategoryOrder {}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Missing {
- Listwise,
- Pairwise,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct StringFormat {
- #[serde(default, rename = "relabel")]
- relabels: Vec<Relabel>,
- #[serde(default, rename = "affix")]
- affixes: Vec<Affix>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Format {
- #[serde(rename = "@baseFormat")]
- base_format: Option<BaseFormat>,
- #[serde(rename = "@errorCharacter")]
- error_character: Option<char>,
- #[serde(rename = "@separatorChars")]
- separator_chars: Option<String>,
- #[serde(rename = "@mdyOrder")]
- mdy_order: Option<MdyOrder>,
- #[serde(rename = "@showYear")]
- show_year: Option<bool>,
- #[serde(rename = "@showQuarter")]
- show_quarter: Option<bool>,
- #[serde(rename = "@quarterPrefix")]
- quarter_prefix: Option<String>,
- #[serde(rename = "@quarterSuffix")]
- quarter_suffix: Option<String>,
- #[serde(rename = "@yearAbbreviation")]
- year_abbreviation: Option<bool>,
- #[serde(rename = "@showMonth")]
- show_month: Option<bool>,
- #[serde(rename = "@monthFormat")]
- month_format: Option<MonthFormat>,
- #[serde(rename = "@dayPadding")]
- day_padding: Option<bool>,
- #[serde(rename = "@dayOfMonthPadding")]
- day_of_month_padding: Option<bool>,
- #[serde(rename = "@showWeek")]
- show_week: Option<bool>,
- #[serde(rename = "@weekPadding")]
- week_padding: Option<bool>,
- #[serde(rename = "@weekSuffix")]
- week_suffix: Option<String>,
- #[serde(rename = "@showDayOfWeek")]
- show_day_of_week: Option<bool>,
- #[serde(rename = "@dayOfWeekAbbreviation")]
- day_of_week_abbreviation: Option<bool>,
- #[serde(rename = "hourPadding")]
- hour_padding: Option<bool>,
- #[serde(rename = "minutePadding")]
- minute_padding: Option<bool>,
- #[serde(rename = "secondPadding")]
- second_padding: Option<bool>,
- #[serde(rename = "@showDay")]
- show_day: Option<bool>,
- #[serde(rename = "@showHour")]
- show_hour: Option<bool>,
- #[serde(rename = "@showMinute")]
- show_minute: Option<bool>,
- #[serde(rename = "@showSecond")]
- show_second: Option<bool>,
- #[serde(rename = "@showMillis")]
- show_millis: Option<bool>,
- #[serde(rename = "@dayType")]
- day_type: Option<DayType>,
- #[serde(rename = "@hourFormat")]
- hour_format: Option<HourFormat>,
- #[serde(rename = "@minimumIntegerDigits")]
- minimum_integer_digits: Option<usize>,
- #[serde(rename = "@maximumFractionDigits")]
- maximum_fraction_digits: Option<usize>,
- #[serde(rename = "@minimumFractionDigits")]
- minimum_fraction_digits: Option<usize>,
- #[serde(rename = "@useGrouping")]
- use_grouping: Option<bool>,
- #[serde(rename = "@scientific")]
- scientific: Option<Scientific>,
- #[serde(rename = "@small")]
- small: Option<f64>,
- #[serde(default, rename = "@prefix")]
- prefix: String,
- #[serde(default, rename = "@suffix")]
- suffix: String,
- #[serde(rename = "@tryStringsAsNumbers")]
- try_strings_as_numbers: Option<bool>,
- #[serde(rename = "@negativesOutside")]
- negatives_outside: Option<bool>,
- #[serde(default)]
- relabel: Vec<Relabel>,
- #[serde(default, rename = "affix")]
- affixes: Vec<Affix>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct NumberFormat {
- #[serde(rename = "@minimumIntegerDigits")]
- minimum_integer_digits: Option<usize>,
- #[serde(rename = "@maximumFractionDigits")]
- maximum_fraction_digits: Option<usize>,
- #[serde(rename = "@minimumFractionDigits")]
- minimum_fraction_digits: Option<usize>,
- #[serde(rename = "@useGrouping")]
- use_grouping: Option<bool>,
- #[serde(rename = "@scientific")]
- scientific: Option<Scientific>,
- #[serde(rename = "@small")]
- small: Option<f64>,
- #[serde(default, rename = "@prefix")]
- prefix: String,
- #[serde(default, rename = "@suffix")]
- suffix: String,
- #[serde(default, rename = "affix")]
- affixes: Vec<Affix>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct DateTimeFormat {
- #[serde(rename = "@baseFormat")]
- base_format: Option<BaseFormat>,
- #[serde(rename = "@separatorChars")]
- separator_chars: Option<String>,
- #[serde(rename = "@mdyOrder")]
- mdy_order: Option<MdyOrder>,
- #[serde(rename = "@showYear")]
- show_year: Option<bool>,
- #[serde(rename = "@showQuarter")]
- show_quarter: Option<bool>,
- #[serde(rename = "@quarterPrefix")]
- quarter_prefix: Option<String>,
- #[serde(rename = "@quarterSuffix")]
- quarter_suffix: Option<String>,
- #[serde(rename = "@yearAbbreviation")]
- year_abbreviation: Option<bool>,
- #[serde(rename = "@showMonth")]
- show_month: Option<bool>,
- #[serde(rename = "@monthFormat")]
- month_format: Option<MonthFormat>,
- #[serde(rename = "@dayPadding")]
- day_padding: Option<bool>,
- #[serde(rename = "@dayOfMonthPadding")]
- day_of_month_padding: Option<bool>,
- #[serde(rename = "@showWeek")]
- show_week: Option<bool>,
- #[serde(rename = "@weekPadding")]
- week_padding: Option<bool>,
- #[serde(rename = "@weekSuffix")]
- week_suffix: Option<String>,
- #[serde(rename = "@showDayOfWeek")]
- show_day_of_week: Option<bool>,
- #[serde(rename = "@dayOfWeekAbbreviation")]
- day_of_week_abbreviation: Option<bool>,
- #[serde(rename = "hourPadding")]
- hour_padding: Option<bool>,
- #[serde(rename = "minutePadding")]
- minute_padding: Option<bool>,
- #[serde(rename = "secondPadding")]
- second_padding: Option<bool>,
- #[serde(rename = "@showDay")]
- show_day: Option<bool>,
- #[serde(rename = "@showHour")]
- show_hour: Option<bool>,
- #[serde(rename = "@showMinute")]
- show_minute: Option<bool>,
- #[serde(rename = "@showSecond")]
- show_second: Option<bool>,
- #[serde(rename = "@showMillis")]
- show_millis: Option<bool>,
- #[serde(rename = "@dayType")]
- day_type: Option<DayType>,
- #[serde(rename = "@hourFormat")]
- hour_format: Option<HourFormat>,
- #[serde(default, rename = "affix")]
- affixes: Vec<Affix>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct ElapsedTimeFormat {
- #[serde(rename = "@baseFormat")]
- base_format: Option<BaseFormat>,
- #[serde(rename = "@dayPadding")]
- day_padding: Option<bool>,
- #[serde(rename = "hourPadding")]
- hour_padding: Option<bool>,
- #[serde(rename = "minutePadding")]
- minute_padding: Option<bool>,
- #[serde(rename = "secondPadding")]
- second_padding: Option<bool>,
- #[serde(rename = "@showDay")]
- show_day: Option<bool>,
- #[serde(rename = "@showHour")]
- show_hour: Option<bool>,
- #[serde(rename = "@showMinute")]
- show_minute: Option<bool>,
- #[serde(rename = "@showSecond")]
- show_second: Option<bool>,
- #[serde(rename = "@showMillis")]
- show_millis: Option<bool>,
- #[serde(rename = "@showYear")]
- show_year: Option<bool>,
- #[serde(default, rename = "affix")]
- affixes: Vec<Affix>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum BaseFormat {
- Date,
- Time,
- DateTime,
- ElapsedTime,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum MdyOrder {
- DayMonthYear,
- MonthDayYear,
- YearMonthDay,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum MonthFormat {
- Long,
- Short,
- Number,
- PaddedNumber,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum DayType {
- Month,
- Year,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum HourFormat {
- #[serde(rename = "AMPM")]
- AmPm,
- #[serde(rename = "AS_24")]
- As24,
- #[serde(rename = "AS_12")]
- As12,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Scientific {
- OnlyForSmall,
- WhenNeeded,
- True,
- False,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Affix {
- /// The footnote number as a natural number: 1 for the first footnote, 2 for
- /// the second, and so on.
- #[serde(rename = "@definesReference")]
- defines_reference: u64,
-
- /// Position for the footnote label.
- #[serde(rename = "@position")]
- position: Position,
-
- /// Whether the affix is a suffix (true) or a prefix (false).
- #[serde(rename = "@suffix")]
- suffix: bool,
-
- /// The text of the suffix or prefix. Typically a letter, e.g. `a` for
- /// footnote 1, `b` for footnote 2, ...
- #[serde(rename = "@value")]
- value: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Position {
- Subscript,
- Superscript,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Relabel {
- #[serde(rename = "@from")]
- from: f64,
- #[serde(rename = "@to")]
- to: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct ValueMapEntry {
- #[serde(rename = "@from")]
- from: String,
- #[serde(rename = "@to")]
- to: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Style {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- /// The text color or, in some cases, background color.
- #[serde(rename = "@color")]
- color: Option<Color>,
-
- /// Not used.
- #[serde(rename = "@color2")]
- color2: Option<Color>,
-
- /// Normally 0. The value -90 causes inner column or outer row labels to be
- /// rotated vertically.
- #[serde(rename = "@labelAngle")]
- label_angle: Option<f64>,
-
- #[serde(rename = "@border-bottom")]
- border_bottom: Option<Border>,
-
- #[serde(rename = "@border-top")]
- border_top: Option<Border>,
-
- #[serde(rename = "@border-left")]
- border_left: Option<Border>,
-
- #[serde(rename = "@border-right")]
- border_right: Option<Border>,
-
- #[serde(rename = "@border-bottom-color")]
- border_bottom_color: Option<Color>,
-
- #[serde(rename = "@border-top-color")]
- border_top_color: Option<Color>,
-
- #[serde(rename = "@border-left-color")]
- border_left_color: Option<Color>,
-
- #[serde(rename = "@border-right-color")]
- border_right_color: Option<Color>,
-
- #[serde(rename = "@font-family")]
- font_family: Option<String>,
-
- #[serde(rename = "@font-size")]
- font_size: Option<String>,
-
- #[serde(rename = "@font-weight")]
- font_weight: Option<FontWeight>,
-
- #[serde(rename = "@font-style")]
- font_style: Option<FontStyle>,
-
- #[serde(rename = "@font-underline")]
- font_underline: Option<FontUnderline>,
-
- #[serde(rename = "@margin-bottom")]
- margin_bottom: Option<String>,
-
- #[serde(rename = "@margin-top")]
- margin_top: Option<String>,
-
- #[serde(rename = "@margin-left")]
- margin_left: Option<String>,
-
- #[serde(rename = "@margin-right")]
- margin_right: Option<String>,
-
- #[serde(rename = "@textAlignment")]
- text_alignment: Option<TextAlignment>,
-
- #[serde(rename = "@labelLocationHorizontal")]
- label_location_horizontal: Option<LabelLocation>,
-
- #[serde(rename = "@labelLocationVertical")]
- label_location_vertical: Option<LabelLocation>,
-
- #[serde(rename = "@size")]
- size: Option<String>,
-
- #[serde(rename = "@width")]
- width: Option<String>,
-
- #[serde(rename = "@visible")]
- visible: Option<bool>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Border {
- Solid,
- Thick,
- Thin,
- Double,
- None,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum FontWeight {
- Regular,
- Bold,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum FontStyle {
- Regular,
- Italic,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum FontUnderline {
- None,
- Underline,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum TextAlignment {
- Left,
- Right,
- Center,
- Decimal,
- Mixed,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum LabelLocation {
- Positive,
- Negative,
- Center,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Graph {
- #[serde(rename = "@cellStyle")]
- cell_style: Ref<Style>,
-
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "location")]
- locations: Vec<Location>,
- coordinates: Coordinates,
- faceting: Faceting,
- facet_layout: FacetLayout,
- interval: Interval,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Coordinates;
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Location {
- /// The part of the table being located.
- #[serde(rename = "@part")]
- part: Part,
-
- /// How the location is determined.
- #[serde(rename = "@method")]
- method: Method,
-
- /// Minimum size.
- #[serde(rename = "@min")]
- min: Option<String>,
-
- /// Maximum size.
- #[serde(rename = "@max")]
- max: Option<String>,
-
- /// An element to attach to. Required when method is attach or same, not
- /// observed otherwise.
- #[serde(rename = "@target")]
- target: Option<String>,
-
- #[serde(rename = "@value")]
- value: Option<String>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Part {
- Height,
- Width,
- Top,
- Bottom,
- Left,
- Right,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Method {
- SizeToContent,
- Attach,
- Fixed,
- Same,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Faceting {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- #[serde(default)]
- layers1: Vec<Layer>,
- cross: Cross,
- #[serde(default)]
- layers2: Vec<Layer>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Cross {
- #[serde(rename = "$value")]
- children: Vec<CrossChild>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum CrossChild {
- /// No dimensions along this axis.
- Unity,
- /// Dimensions along this axis.
- Nest(Nest),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Nest {
- #[serde(rename = "variableReference")]
- variable_references: Vec<VariableReference>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Layer {
- #[serde(rename = "@variable")]
- variable: String,
-
- #[serde(rename = "@value")]
- value: String,
-
- #[serde(rename = "@visible")]
- visible: Option<bool>,
-
- #[serde(rename = "@titleVisible")]
- title_visible: Option<bool>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct FacetLayout {
- table_layout: TableLayout,
- #[serde(default)]
- #[serde(rename = "setCellProperties")]
- scp1: Vec<SetCellProperties>,
- #[serde(rename = "facetLevel")]
- facet_levels: Vec<FacetLevel>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct TableLayout {
- #[serde(rename = "@verticalTitlesInCorner")]
- vertical_titles_in_corner: bool,
-
- #[serde(rename = "@style")]
- style: Option<Ref<Style>>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SetCellProperties {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- #[serde(rename = "@applyToConverse")]
- apply_to_converse: Option<bool>,
-
- #[serde(rename = "$value")]
- sets: Vec<Set>,
-
- #[serde(rename = "union")]
- unions: Option<Union>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Union {
- #[serde(rename = "intersect")]
- intersects: Vec<Intersect>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Intersect {
- #[serde(rename = "$value")]
- child: Vec<IntersectChild>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum IntersectChild {
- Where(Where),
- IntersectWhere(IntersectWhere),
- Alternating,
- Empty,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Where {
- #[serde(rename = "@variable")]
- variable: String,
- #[serde(rename = "@include")]
- include: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct IntersectWhere {
- #[serde(rename = "@variable")]
- variable: String,
-
- #[serde(rename = "@variable2")]
- variable2: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Set {
- SetStyle(SetStyle),
- SetFrameStyle(SetFrameStyle),
- SetFormat(SetFormat),
- SetMetaData(SetMetaData),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SetStyle {
- #[serde(rename = "@target")]
- target: String,
-
- #[serde(rename = "@style")]
- style: Ref<Style>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SetMetaData {
- #[serde(rename = "@target")]
- target: Ref<Graph>,
-
- #[serde(rename = "@key")]
- key: String,
-
- #[serde(rename = "@value")]
- value: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SetFormat {
- #[serde(rename = "@target")]
- target: String,
-
- #[serde(rename = "@reset")]
- reset: Option<bool>,
-
- #[serde(rename = "$value")]
- child: SetFormatChild,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum SetFormatChild {
- Format(Format),
- NumberFormat(NumberFormat),
- StringFormat(Vec<StringFormat>),
- DateTimeFormat(DateTimeFormat),
- ElapsedTimeFormat(ElapsedTimeFormat),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct SetFrameStyle {
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "@target")]
- target: Ref<MajorTicks>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Interval {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- labeling: Labeling,
- footnotes: Option<Footnotes>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Labeling {
- #[serde(rename = "@style")]
- style: Option<Ref<Style>>,
-
- #[serde(rename = "@variable")]
- variable: String,
-
- #[serde(default)]
- children: Vec<LabelingChild>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum LabelingChild {
- Formatting(Formatting),
- Format(Format),
- Footnotes(Footnotes),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Formatting {
- #[serde(rename = "@variable")]
- variable: String,
-
- mappings: Vec<FormatMapping>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct FormatMapping {
- #[serde(rename = "@from")]
- from: i64,
-
- format: Option<Format>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Footnotes {
- #[serde(rename = "@superscript")]
- superscript: Option<bool>,
-
- #[serde(rename = "@variable")]
- variable: String,
-
- mappings: Vec<FootnoteMapping>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct FootnoteMapping {
- #[serde(rename = "@definesReference")]
- defines_reference: i64,
-
- #[serde(rename = "@from")]
- from: i64,
-
- #[serde(rename = "@to")]
- to: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct FacetLevel {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- #[serde(rename = "@level")]
- level: usize,
-
- #[serde(rename = "@gap")]
- gap: Option<String>,
- //axis: Axis,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Axis {
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- label: Option<Label>,
- major_ticks: MajorTicks,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct MajorTicks {
- #[serde(rename = "@id")]
- id: String,
-
- #[serde(rename = "@labelAngle")]
- label_angle: f64,
-
- #[serde(rename = "@length")]
- length: String,
-
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "@tickFrameStyle")]
- tick_frame_style: Ref<Style>,
-
- #[serde(rename = "@labelFrequency")]
- label_frequency: Option<i64>,
-
- #[serde(rename = "@stagger")]
- stagger: Option<bool>,
-
- gridline: Gridline,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Gridline {
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "@zOrder")]
- z_order: i64,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Label {
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "@textFrameStyle")]
- text_frame_style: Option<Ref<Style>>,
-
- #[serde(rename = "@purpose")]
- purpose: Option<Purpose>,
-
- #[serde(rename = "$value")]
- child: LabelChild,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Purpose {
- Title,
- SubTitle,
- SubSubTitle,
- Layer,
- Footnote,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum LabelChild {
- Text(Vec<Text>),
- DescriptionGroup(DescriptionGroup),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Text {
- #[serde(rename = "@usesReference")]
- uses_reference: Option<i64>,
-
- #[serde(rename = "@definesReference")]
- defines_reference: Option<i64>,
-
- #[serde(rename = "@position")]
- position: Option<Position>,
-
- #[serde(rename = "@style")]
- style: Option<Ref<Style>>,
-
- #[serde(default, rename = "$text")]
- text: String,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct DescriptionGroup {
- #[serde(rename = "@target")]
- target: Ref<Faceting>,
-
- #[serde(rename = "@separator")]
- separator: Option<String>,
-
- children: Vec<DescriptionGroupChild>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum DescriptionGroupChild {
- Description(Description),
- Text(Text),
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Description {
- #[serde(rename = "@name")]
- name: Name,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-enum Name {
- Variable,
- Value,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct LabelFrame {
- #[serde(rename = "@id")]
- id: Option<String>,
-
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(rename = "location")]
- locations: Vec<Location>,
-
- label: Option<Label>,
- paragraph: Option<Paragraph>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Paragraph;
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct Container {
- #[serde(rename = "@style")]
- style: Ref<Style>,
-
- #[serde(default, rename = "extension")]
- extensions: Option<ContainerExtension>,
- #[serde(default)]
- locations: Vec<Location>,
- #[serde(default)]
- label_frames: Vec<LabelFrame>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename = "extension", rename_all = "camelCase")]
-struct ContainerExtension {
- #[serde(rename = "@combinedFootnotes")]
- combined_footnotes: Option<bool>,
-}
-
-#[derive(Deserialize, Debug)]
-#[serde(rename_all = "camelCase")]
-struct LayerController {
- #[serde(rename = "@target")]
- target: Option<Ref<Label>>,
-}
--- /dev/null
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see <http://www.gnu.org/licenses/>.
+
+use std::marker::PhantomData;
+
+use serde::Deserialize;
+
+use crate::output::pivot::Color;
+
+#[derive(Debug)]
+struct Ref<T> {
+ references: String,
+ _phantom: PhantomData<T>,
+}
+
+impl<'de, T> Deserialize<'de> for Ref<T> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ Ok(Self {
+ references: String::deserialize(deserializer)?,
+ _phantom: PhantomData,
+ })
+ }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct Visualization {
+ /// In format `YYYY-MM-DD`.
+ #[serde(rename = "@date")]
+ date: String,
+ // Locale used for output, e.g. `en-US`.
+ #[serde(rename = "@lang")]
+ lang: String,
+ /// Localized title of the pivot table.
+ #[serde(rename = "@name")]
+ name: String,
+ /// Base style for the pivot table.
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "$value")]
+ children: Vec<VisChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum VisChild {
+ Extension(VisualizationExtension),
+ UserSource(UserSource),
+ SourceVariable(SourceVariable),
+ DerivedVariable(DerivedVariable),
+ CategoricalDomain(CategoricalDomain),
+ Graph(Graph),
+ LabelFrame(LabelFrame),
+ Container(Container),
+ Style(Style),
+ LayerController(LayerController),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename = "extension", rename_all = "camelCase")]
+struct VisualizationExtension;
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Variable {
+ SourceVariable(SourceVariable),
+ DerivedVariable(DerivedVariable),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SourceVariable {
+ #[serde(rename = "@id")]
+ id: String,
+
+ /// The name of a variable within the source, corresponding to the
+ /// `variable-name` in the `tableData.bin` member.
+ #[serde(rename = "@sourceName")]
+ source_name: String,
+
+ /// Variable label, if any.
+ #[serde(rename = "@label")]
+ label: Option<String>,
+
+ /// A variable whose string values correspond one-to-one with the values of
+ /// this variable and are suitable as value labels.
+ #[serde(rename = "@labelVariable")]
+ label_variable: Option<Ref<SourceVariable>>,
+
+ #[serde(default, rename = "extension")]
+ extensions: Vec<VariableExtension>,
+ format: Option<Format>,
+ string_format: Option<StringFormat>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DerivedVariable {
+ #[serde(rename = "@id")]
+ id: String,
+
+ /// An expression that defines the variable's value.
+ #[serde(rename = "@value")]
+ value: String,
+ #[serde(default, rename = "extension")]
+ extensions: Vec<VariableExtension>,
+ format: Option<Format>,
+ string_format: Option<StringFormat>,
+ #[serde(default, rename = "valueMapEntry")]
+ value_map: Vec<ValueMapEntry>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename = "extension", rename_all = "camelCase")]
+struct VariableExtension {
+ #[serde(rename = "@from")]
+ from: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct UserSource {
+ #[serde(rename = "@id")]
+ id: String,
+
+ #[serde(rename = "@missing")]
+ missing: Option<Missing>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CategoricalDomain {
+ variable_reference: VariableReference,
+ simple_sort: SimpleSort,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct VariableReference {
+ #[serde(rename = "@ref")]
+ reference: Option<String>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SimpleSort {
+ #[serde(rename = "@method")]
+ category_order: CategoryOrder,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CategoryOrder {}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Missing {
+ Listwise,
+ Pairwise,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct StringFormat {
+ #[serde(default, rename = "relabel")]
+ relabels: Vec<Relabel>,
+ #[serde(default, rename = "affix")]
+ affixes: Vec<Affix>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Format {
+ #[serde(rename = "@baseFormat")]
+ base_format: Option<BaseFormat>,
+ #[serde(rename = "@errorCharacter")]
+ error_character: Option<char>,
+ #[serde(rename = "@separatorChars")]
+ separator_chars: Option<String>,
+ #[serde(rename = "@mdyOrder")]
+ mdy_order: Option<MdyOrder>,
+ #[serde(rename = "@showYear")]
+ show_year: Option<bool>,
+ #[serde(rename = "@showQuarter")]
+ show_quarter: Option<bool>,
+ #[serde(rename = "@quarterPrefix")]
+ quarter_prefix: Option<String>,
+ #[serde(rename = "@quarterSuffix")]
+ quarter_suffix: Option<String>,
+ #[serde(rename = "@yearAbbreviation")]
+ year_abbreviation: Option<bool>,
+ #[serde(rename = "@showMonth")]
+ show_month: Option<bool>,
+ #[serde(rename = "@monthFormat")]
+ month_format: Option<MonthFormat>,
+ #[serde(rename = "@dayPadding")]
+ day_padding: Option<bool>,
+ #[serde(rename = "@dayOfMonthPadding")]
+ day_of_month_padding: Option<bool>,
+ #[serde(rename = "@showWeek")]
+ show_week: Option<bool>,
+ #[serde(rename = "@weekPadding")]
+ week_padding: Option<bool>,
+ #[serde(rename = "@weekSuffix")]
+ week_suffix: Option<String>,
+ #[serde(rename = "@showDayOfWeek")]
+ show_day_of_week: Option<bool>,
+ #[serde(rename = "@dayOfWeekAbbreviation")]
+ day_of_week_abbreviation: Option<bool>,
+ #[serde(rename = "hourPadding")]
+ hour_padding: Option<bool>,
+ #[serde(rename = "minutePadding")]
+ minute_padding: Option<bool>,
+ #[serde(rename = "secondPadding")]
+ second_padding: Option<bool>,
+ #[serde(rename = "@showDay")]
+ show_day: Option<bool>,
+ #[serde(rename = "@showHour")]
+ show_hour: Option<bool>,
+ #[serde(rename = "@showMinute")]
+ show_minute: Option<bool>,
+ #[serde(rename = "@showSecond")]
+ show_second: Option<bool>,
+ #[serde(rename = "@showMillis")]
+ show_millis: Option<bool>,
+ #[serde(rename = "@dayType")]
+ day_type: Option<DayType>,
+ #[serde(rename = "@hourFormat")]
+ hour_format: Option<HourFormat>,
+ #[serde(rename = "@minimumIntegerDigits")]
+ minimum_integer_digits: Option<usize>,
+ #[serde(rename = "@maximumFractionDigits")]
+ maximum_fraction_digits: Option<usize>,
+ #[serde(rename = "@minimumFractionDigits")]
+ minimum_fraction_digits: Option<usize>,
+ #[serde(rename = "@useGrouping")]
+ use_grouping: Option<bool>,
+ #[serde(rename = "@scientific")]
+ scientific: Option<Scientific>,
+ #[serde(rename = "@small")]
+ small: Option<f64>,
+ #[serde(default, rename = "@prefix")]
+ prefix: String,
+ #[serde(default, rename = "@suffix")]
+ suffix: String,
+ #[serde(rename = "@tryStringsAsNumbers")]
+ try_strings_as_numbers: Option<bool>,
+ #[serde(rename = "@negativesOutside")]
+ negatives_outside: Option<bool>,
+ #[serde(default)]
+ relabel: Vec<Relabel>,
+ #[serde(default, rename = "affix")]
+ affixes: Vec<Affix>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct NumberFormat {
+ #[serde(rename = "@minimumIntegerDigits")]
+ minimum_integer_digits: Option<usize>,
+ #[serde(rename = "@maximumFractionDigits")]
+ maximum_fraction_digits: Option<usize>,
+ #[serde(rename = "@minimumFractionDigits")]
+ minimum_fraction_digits: Option<usize>,
+ #[serde(rename = "@useGrouping")]
+ use_grouping: Option<bool>,
+ #[serde(rename = "@scientific")]
+ scientific: Option<Scientific>,
+ #[serde(rename = "@small")]
+ small: Option<f64>,
+ #[serde(default, rename = "@prefix")]
+ prefix: String,
+ #[serde(default, rename = "@suffix")]
+ suffix: String,
+ #[serde(default, rename = "affix")]
+ affixes: Vec<Affix>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DateTimeFormat {
+ #[serde(rename = "@baseFormat")]
+ base_format: Option<BaseFormat>,
+ #[serde(rename = "@separatorChars")]
+ separator_chars: Option<String>,
+ #[serde(rename = "@mdyOrder")]
+ mdy_order: Option<MdyOrder>,
+ #[serde(rename = "@showYear")]
+ show_year: Option<bool>,
+ #[serde(rename = "@showQuarter")]
+ show_quarter: Option<bool>,
+ #[serde(rename = "@quarterPrefix")]
+ quarter_prefix: Option<String>,
+ #[serde(rename = "@quarterSuffix")]
+ quarter_suffix: Option<String>,
+ #[serde(rename = "@yearAbbreviation")]
+ year_abbreviation: Option<bool>,
+ #[serde(rename = "@showMonth")]
+ show_month: Option<bool>,
+ #[serde(rename = "@monthFormat")]
+ month_format: Option<MonthFormat>,
+ #[serde(rename = "@dayPadding")]
+ day_padding: Option<bool>,
+ #[serde(rename = "@dayOfMonthPadding")]
+ day_of_month_padding: Option<bool>,
+ #[serde(rename = "@showWeek")]
+ show_week: Option<bool>,
+ #[serde(rename = "@weekPadding")]
+ week_padding: Option<bool>,
+ #[serde(rename = "@weekSuffix")]
+ week_suffix: Option<String>,
+ #[serde(rename = "@showDayOfWeek")]
+ show_day_of_week: Option<bool>,
+ #[serde(rename = "@dayOfWeekAbbreviation")]
+ day_of_week_abbreviation: Option<bool>,
+ #[serde(rename = "hourPadding")]
+ hour_padding: Option<bool>,
+ #[serde(rename = "minutePadding")]
+ minute_padding: Option<bool>,
+ #[serde(rename = "secondPadding")]
+ second_padding: Option<bool>,
+ #[serde(rename = "@showDay")]
+ show_day: Option<bool>,
+ #[serde(rename = "@showHour")]
+ show_hour: Option<bool>,
+ #[serde(rename = "@showMinute")]
+ show_minute: Option<bool>,
+ #[serde(rename = "@showSecond")]
+ show_second: Option<bool>,
+ #[serde(rename = "@showMillis")]
+ show_millis: Option<bool>,
+ #[serde(rename = "@dayType")]
+ day_type: Option<DayType>,
+ #[serde(rename = "@hourFormat")]
+ hour_format: Option<HourFormat>,
+ #[serde(default, rename = "affix")]
+ affixes: Vec<Affix>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct ElapsedTimeFormat {
+ #[serde(rename = "@baseFormat")]
+ base_format: Option<BaseFormat>,
+ #[serde(rename = "@dayPadding")]
+ day_padding: Option<bool>,
+ #[serde(rename = "hourPadding")]
+ hour_padding: Option<bool>,
+ #[serde(rename = "minutePadding")]
+ minute_padding: Option<bool>,
+ #[serde(rename = "secondPadding")]
+ second_padding: Option<bool>,
+ #[serde(rename = "@showDay")]
+ show_day: Option<bool>,
+ #[serde(rename = "@showHour")]
+ show_hour: Option<bool>,
+ #[serde(rename = "@showMinute")]
+ show_minute: Option<bool>,
+ #[serde(rename = "@showSecond")]
+ show_second: Option<bool>,
+ #[serde(rename = "@showMillis")]
+ show_millis: Option<bool>,
+ #[serde(rename = "@showYear")]
+ show_year: Option<bool>,
+ #[serde(default, rename = "affix")]
+ affixes: Vec<Affix>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum BaseFormat {
+ Date,
+ Time,
+ DateTime,
+ ElapsedTime,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum MdyOrder {
+ DayMonthYear,
+ MonthDayYear,
+ YearMonthDay,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum MonthFormat {
+ Long,
+ Short,
+ Number,
+ PaddedNumber,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum DayType {
+ Month,
+ Year,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum HourFormat {
+ #[serde(rename = "AMPM")]
+ AmPm,
+ #[serde(rename = "AS_24")]
+ As24,
+ #[serde(rename = "AS_12")]
+ As12,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Scientific {
+ OnlyForSmall,
+ WhenNeeded,
+ True,
+ False,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Affix {
+ /// The footnote number as a natural number: 1 for the first footnote, 2 for
+ /// the second, and so on.
+ #[serde(rename = "@definesReference")]
+ defines_reference: u64,
+
+ /// Position for the footnote label.
+ #[serde(rename = "@position")]
+ position: Position,
+
+ /// Whether the affix is a suffix (true) or a prefix (false).
+ #[serde(rename = "@suffix")]
+ suffix: bool,
+
+ /// The text of the suffix or prefix. Typically a letter, e.g. `a` for
+ /// footnote 1, `b` for footnote 2, ...
+ #[serde(rename = "@value")]
+ value: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Position {
+ Subscript,
+ Superscript,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Relabel {
+ #[serde(rename = "@from")]
+ from: f64,
+ #[serde(rename = "@to")]
+ to: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct ValueMapEntry {
+ #[serde(rename = "@from")]
+ from: String,
+ #[serde(rename = "@to")]
+ to: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Style {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ /// The text color or, in some cases, background color.
+ #[serde(rename = "@color")]
+ color: Option<Color>,
+
+ /// Not used.
+ #[serde(rename = "@color2")]
+ color2: Option<Color>,
+
+ /// Normally 0. The value -90 causes inner column or outer row labels to be
+ /// rotated vertically.
+ #[serde(rename = "@labelAngle")]
+ label_angle: Option<f64>,
+
+ #[serde(rename = "@border-bottom")]
+ border_bottom: Option<Border>,
+
+ #[serde(rename = "@border-top")]
+ border_top: Option<Border>,
+
+ #[serde(rename = "@border-left")]
+ border_left: Option<Border>,
+
+ #[serde(rename = "@border-right")]
+ border_right: Option<Border>,
+
+ #[serde(rename = "@border-bottom-color")]
+ border_bottom_color: Option<Color>,
+
+ #[serde(rename = "@border-top-color")]
+ border_top_color: Option<Color>,
+
+ #[serde(rename = "@border-left-color")]
+ border_left_color: Option<Color>,
+
+ #[serde(rename = "@border-right-color")]
+ border_right_color: Option<Color>,
+
+ #[serde(rename = "@font-family")]
+ font_family: Option<String>,
+
+ #[serde(rename = "@font-size")]
+ font_size: Option<String>,
+
+ #[serde(rename = "@font-weight")]
+ font_weight: Option<FontWeight>,
+
+ #[serde(rename = "@font-style")]
+ font_style: Option<FontStyle>,
+
+ #[serde(rename = "@font-underline")]
+ font_underline: Option<FontUnderline>,
+
+ #[serde(rename = "@margin-bottom")]
+ margin_bottom: Option<String>,
+
+ #[serde(rename = "@margin-top")]
+ margin_top: Option<String>,
+
+ #[serde(rename = "@margin-left")]
+ margin_left: Option<String>,
+
+ #[serde(rename = "@margin-right")]
+ margin_right: Option<String>,
+
+ #[serde(rename = "@textAlignment")]
+ text_alignment: Option<TextAlignment>,
+
+ #[serde(rename = "@labelLocationHorizontal")]
+ label_location_horizontal: Option<LabelLocation>,
+
+ #[serde(rename = "@labelLocationVertical")]
+ label_location_vertical: Option<LabelLocation>,
+
+ #[serde(rename = "@size")]
+ size: Option<String>,
+
+ #[serde(rename = "@width")]
+ width: Option<String>,
+
+ #[serde(rename = "@visible")]
+ visible: Option<bool>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Border {
+ Solid,
+ Thick,
+ Thin,
+ Double,
+ None,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FontWeight {
+ Regular,
+ Bold,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FontStyle {
+ Regular,
+ Italic,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FontUnderline {
+ None,
+ Underline,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum TextAlignment {
+ Left,
+ Right,
+ Center,
+ Decimal,
+ Mixed,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum LabelLocation {
+ Positive,
+ Negative,
+ Center,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Graph {
+ #[serde(rename = "@cellStyle")]
+ cell_style: Ref<Style>,
+
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "location")]
+ locations: Vec<Location>,
+ coordinates: Coordinates,
+ faceting: Faceting,
+ facet_layout: FacetLayout,
+ interval: Interval,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Coordinates;
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Location {
+ /// The part of the table being located.
+ #[serde(rename = "@part")]
+ part: Part,
+
+ /// How the location is determined.
+ #[serde(rename = "@method")]
+ method: Method,
+
+ /// Minimum size.
+ #[serde(rename = "@min")]
+ min: Option<String>,
+
+ /// Maximum size.
+ #[serde(rename = "@max")]
+ max: Option<String>,
+
+ /// An element to attach to. Required when method is attach or same, not
+ /// observed otherwise.
+ #[serde(rename = "@target")]
+ target: Option<String>,
+
+ #[serde(rename = "@value")]
+ value: Option<String>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Part {
+ Height,
+ Width,
+ Top,
+ Bottom,
+ Left,
+ Right,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Method {
+ SizeToContent,
+ Attach,
+ Fixed,
+ Same,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Faceting {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ #[serde(default)]
+ layers1: Vec<Layer>,
+ cross: Cross,
+ #[serde(default)]
+ layers2: Vec<Layer>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Cross {
+ #[serde(rename = "$value")]
+ children: Vec<CrossChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum CrossChild {
+ /// No dimensions along this axis.
+ Unity,
+ /// Dimensions along this axis.
+ Nest(Nest),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Nest {
+ #[serde(rename = "variableReference")]
+ variable_references: Vec<VariableReference>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Layer {
+ #[serde(rename = "@variable")]
+ variable: String,
+
+ #[serde(rename = "@value")]
+ value: String,
+
+ #[serde(rename = "@visible")]
+ visible: Option<bool>,
+
+ #[serde(rename = "@titleVisible")]
+ title_visible: Option<bool>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FacetLayout {
+ table_layout: TableLayout,
+ #[serde(rename = "$value")]
+ children: Vec<FacetLayoutChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FacetLayoutChild {
+ SetCellProperties(SetCellProperties),
+ FacetLevel(FacetLevel),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct TableLayout {
+ #[serde(rename = "@verticalTitlesInCorner")]
+ vertical_titles_in_corner: bool,
+
+ #[serde(rename = "@style")]
+ style: Option<Ref<Style>>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetCellProperties {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ #[serde(rename = "@applyToConverse")]
+ apply_to_converse: Option<bool>,
+
+ #[serde(rename = "$value")]
+ sets: Vec<Set>,
+
+ #[serde(rename = "union")]
+ unions: Option<Union>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Union {
+ #[serde(default, rename = "intersect")]
+ intersects: Vec<Intersect>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Intersect {
+ #[serde(default, rename = "$value")]
+ child: Vec<IntersectChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum IntersectChild {
+ Where(Where),
+ IntersectWhere(IntersectWhere),
+ Alternating,
+ #[serde(other)]
+ Empty,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Where {
+ #[serde(rename = "@variable")]
+ variable: String,
+ #[serde(rename = "@include")]
+ include: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct IntersectWhere {
+ #[serde(rename = "@variable")]
+ variable: String,
+
+ #[serde(rename = "@variable2")]
+ variable2: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Set {
+ SetStyle(SetStyle),
+ SetFrameStyle(SetFrameStyle),
+ SetFormat(SetFormat),
+ SetMetaData(SetMetaData),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetStyle {
+ #[serde(rename = "@target")]
+ target: String,
+
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetMetaData {
+ #[serde(rename = "@target")]
+ target: Ref<Graph>,
+
+ #[serde(rename = "@key")]
+ key: String,
+
+ #[serde(rename = "@value")]
+ value: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetFormat {
+ #[serde(rename = "@target")]
+ target: String,
+
+ #[serde(rename = "@reset")]
+ reset: Option<bool>,
+
+ #[serde(rename = "$value")]
+ child: Option<SetFormatChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum SetFormatChild {
+ Format(Format),
+ NumberFormat(NumberFormat),
+ StringFormat(Vec<StringFormat>),
+ DateTimeFormat(DateTimeFormat),
+ ElapsedTimeFormat(ElapsedTimeFormat),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetFrameStyle {
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "@target")]
+ target: Ref<MajorTicks>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Interval {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ labeling: Labeling,
+ footnotes: Option<Footnotes>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Labeling {
+ #[serde(rename = "@style")]
+ style: Option<Ref<Style>>,
+
+ #[serde(rename = "@variable")]
+ variable: String,
+
+ #[serde(default)]
+ children: Vec<LabelingChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum LabelingChild {
+ Formatting(Formatting),
+ Format(Format),
+ Footnotes(Footnotes),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Formatting {
+ #[serde(rename = "@variable")]
+ variable: String,
+
+ mappings: Vec<FormatMapping>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FormatMapping {
+ #[serde(rename = "@from")]
+ from: i64,
+
+ format: Option<Format>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Footnotes {
+ #[serde(rename = "@superscript")]
+ superscript: Option<bool>,
+
+ #[serde(rename = "@variable")]
+ variable: String,
+
+ #[serde(default, rename = "footnoteMapping")]
+ mappings: Vec<FootnoteMapping>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FootnoteMapping {
+ #[serde(rename = "@definesReference")]
+ defines_reference: i64,
+
+ #[serde(rename = "@from")]
+ from: i64,
+
+ #[serde(rename = "@to")]
+ to: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FacetLevel {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ #[serde(rename = "@level")]
+ level: usize,
+
+ #[serde(rename = "@gap")]
+ gap: Option<String>,
+ //axis: Axis,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Axis {
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ label: Option<Label>,
+ major_ticks: MajorTicks,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct MajorTicks {
+ #[serde(rename = "@id")]
+ id: String,
+
+ #[serde(rename = "@labelAngle")]
+ label_angle: f64,
+
+ #[serde(rename = "@length")]
+ length: String,
+
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "@tickFrameStyle")]
+ tick_frame_style: Ref<Style>,
+
+ #[serde(rename = "@labelFrequency")]
+ label_frequency: Option<i64>,
+
+ #[serde(rename = "@stagger")]
+ stagger: Option<bool>,
+
+ gridline: Gridline,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Gridline {
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "@zOrder")]
+ z_order: i64,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Label {
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "@textFrameStyle")]
+ text_frame_style: Option<Ref<Style>>,
+
+ #[serde(rename = "@purpose")]
+ purpose: Option<Purpose>,
+
+ #[serde(rename = "$value")]
+ child: LabelChild,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Purpose {
+ Title,
+ SubTitle,
+ SubSubTitle,
+ Layer,
+ Footnote,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum LabelChild {
+ Text(Vec<Text>),
+ DescriptionGroup(DescriptionGroup),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Text {
+ #[serde(rename = "@usesReference")]
+ uses_reference: Option<i64>,
+
+ #[serde(rename = "@definesReference")]
+ defines_reference: Option<i64>,
+
+ #[serde(rename = "@position")]
+ position: Option<Position>,
+
+ #[serde(rename = "@style")]
+ style: Option<Ref<Style>>,
+
+ #[serde(default, rename = "$text")]
+ text: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DescriptionGroup {
+ #[serde(rename = "@target")]
+ target: Ref<Faceting>,
+
+ #[serde(rename = "@separator")]
+ separator: Option<String>,
+
+ #[serde(rename = "$value")]
+ children: Vec<DescriptionGroupChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum DescriptionGroupChild {
+ Description(Description),
+ Text(Text),
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Description {
+ #[serde(rename = "@name")]
+ name: Name,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Name {
+ Variable,
+ Value,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct LabelFrame {
+ #[serde(rename = "@id")]
+ id: Option<String>,
+
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(rename = "location")]
+ locations: Vec<Location>,
+
+ label: Option<Label>,
+ paragraph: Option<Paragraph>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Paragraph;
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Container {
+ #[serde(rename = "@style")]
+ style: Ref<Style>,
+
+ #[serde(default, rename = "extension")]
+ extensions: Option<ContainerExtension>,
+ #[serde(default)]
+ locations: Vec<Location>,
+ #[serde(default)]
+ label_frames: Vec<LabelFrame>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename = "extension", rename_all = "camelCase")]
+struct ContainerExtension {
+ #[serde(rename = "@combinedFootnotes")]
+ combined_footnotes: Option<bool>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct LayerController {
+ #[serde(rename = "@target")]
+ target: Option<Ref<Label>>,
+}