#![allow(dead_code)]
use std::{
cell::{Cell, RefCell},
collections::{BTreeMap, HashMap},
fmt::Debug,
marker::PhantomData,
num::NonZeroUsize,
};
use displaydoc::Display;
use enum_map::Enum;
use indexmap::IndexMap;
use ordered_float::OrderedFloat;
use serde::Deserialize;
use crate::{
data::Datum,
format::{F8_0, Type, UncheckedFormat},
output::pivot::{
CategoryLocator, Length, PivotTable,
look::{Color, VertAlign},
},
};
#[derive(Debug)]
struct Ref<T> {
references: String,
_phantom: PhantomData<T>,
}
impl<T> Ref<T> {
fn get<'a>(&self, table: &HashMap<&str, &'a T>) -> Option<&'a T> {
table.get(self.references.as_str()).map(|v| &**v)
}
}
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(Clone, Debug, Default)]
struct Map(HashMap<OrderedFloat<f64>, Datum<String>>);
impl Map {
fn apply(&self, data: &mut Vec<Datum<String>>) {
for value in data {
if let Datum::Number(Some(number)) = value
&& let Some(to) = self.0.get(&OrderedFloat(*number))
{
*value = to.clone();
}
}
}
}
#[derive(Clone, Debug, Display, thiserror::Error)]
pub enum GraphWarning {
MissingData,
UnresolvedDependencies(Vec<String>),
UnsupportedValue {
variable: String,
value: String,
},
UnsupportedApplyToConverse,
InvalidCreationDate(String),
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Visualization {
#[serde(rename = "@date")]
_date: String,
#[serde(rename = "@lang")]
_lang: String,
#[serde(rename = "@name")]
_name: String,
#[serde(rename = "@style")]
_style: Ref<Style>,
#[serde(rename = "$value")]
children: Vec<VisChild>,
}
impl Visualization {
pub fn decode_series(
&self,
data: IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
warn: &mut dyn FnMut(GraphWarning),
) -> BTreeMap<&str, Series> {
let mut variables: Vec<&dyn Variable> = self
.children
.iter()
.filter_map(|child| child.variable())
.collect();
let mut series = BTreeMap::<&str, Series>::new();
while !variables.is_empty() {
let n = variables.len();
variables.retain(|variable| !variable.decode(&data, &mut series, warn));
if n == variables.len() {
warn(GraphWarning::UnresolvedDependencies(
variables
.iter()
.map(|variable| variable.name().into())
.collect(),
));
break;
}
}
series
}
fn graph(&self) -> Result<&Graph, super::Error> {
for child in &self.children {
match child {
VisChild::Graph(g) => return Ok(g),
_ => (),
}
}
Err(super::Error::LegacyMissingGraph)
}
pub fn decode(
&self,
binary_data: IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
warn: &mut dyn FnMut(GraphWarning),
) -> Result<PivotTable, super::Error> {
dbg!(self);
let graph = self.graph()?;
let series = self.decode_series(binary_data, warn);
let styles = self
.children
.iter()
.filter_map(|child| child.style())
.filter_map(|style| style.id.as_ref().map(|id| (id.as_str(), style)))
.collect::<HashMap<_, _>>();
todo!()
}
}
pub struct Series {
pub name: String,
label: Option<String>,
categories: Vec<Option<usize>>,
pub values: Vec<Datum<String>>,
affixes: Vec<Affix>,
coordinate_to_index: RefCell<BTreeMap<usize, CategoryLocator>>,
dimension_index: Cell<Option<usize>>,
}
impl Debug for Series {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Series")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl Series {
fn new(name: String, categories: Vec<Option<usize>>, values: Vec<Datum<String>>) -> Self {
Self {
name,
label: None,
categories,
values,
affixes: Vec::new(),
coordinate_to_index: Default::default(),
dimension_index: Default::default(),
}
}
fn with_label(self, label: Option<String>) -> Self {
Self { label, ..self }
}
fn with_affixes(self, affixes: Vec<Affix>) -> Self {
Self { affixes, ..self }
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum VisChild {
SourceVariable(SourceVariable),
DerivedVariable(DerivedVariable),
CategoricalDomain(CategoricalDomain),
Graph(Graph),
LabelFrame(LabelFrame),
Container(Container),
Style(Style),
#[serde(other)]
Other,
}
impl VisChild {
fn variable(&self) -> Option<&dyn Variable> {
match self {
VisChild::SourceVariable(source_variable) => Some(source_variable),
VisChild::DerivedVariable(derived_variable) => Some(derived_variable),
_ => None,
}
}
fn style(&self) -> Option<&Style> {
match self {
Self::Style(style) => Some(style),
_ => None,
}
}
}
trait Variable {
fn decode<'a>(
&'a self,
data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
series: &mut BTreeMap<&'a str, Series>,
warn: &mut dyn FnMut(GraphWarning),
) -> bool;
fn name(&self) -> &str;
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct SourceVariable {
#[serde(rename = "@id")]
id: String,
#[serde(rename = "@source")]
source: String,
#[serde(rename = "@sourceName")]
variable: String,
#[serde(rename = "@label")]
label: Option<String>,
#[serde(rename = "@labelVariable")]
label_variable: Option<Ref<SourceVariable>>,
format: Option<Format>,
string_format: Option<StringFormat>,
}
impl SourceVariable {
fn affixes(&self) -> &[Affix] {
if let Some(format) = &self.format {
&format.affixes
} else if let Some(string_format) = &self.string_format {
&string_format.affixes
} else {
&[]
}
}
}
impl Variable for SourceVariable {
fn decode<'a>(
&'a self,
data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
series: &mut BTreeMap<&'a str, Series>,
_warn: &mut dyn FnMut(GraphWarning),
) -> bool {
let label_series = if let Some(label_variable) = &self.label_variable {
if let Some(label_series) = series.get(label_variable.references.as_str()) {
Some(label_series)
} else {
return false;
}
} else {
None
};
let (categories, mut data) = if let Some(source) = data.get(&self.source)
&& let Some(values) = source.get(&self.variable)
{
let categories = values
.iter()
.map(|value| {
value
.as_number()
.flatten()
.and_then(|v| (v >= 0.0 && v < usize::MAX as f64).then_some(v as usize))
})
.collect();
(categories, values.clone())
} else {
(Vec::new(), Vec::new())
};
if let Some(format) = &self.format {
format.mapping().apply(&mut data);
} else if let Some(string_format) = &self.string_format {
string_format.mapping().apply(&mut data);
};
if let Some(label_series) = label_series {
let format = self.format.as_ref().map_or(F8_0, |f| f.decode());
data = label_series
.values
.iter()
.map(|label| {
if label.is_number() {
Datum::String(label.display(format).with_stretch().to_string())
} else {
label.clone()
}
})
.collect();
}
series.insert(
&self.id,
Series::new(self.id.clone(), categories, data)
.with_affixes(Vec::from(self.affixes()))
.with_label(self.label.clone()),
);
true
}
fn name(&self) -> &str {
&self.variable
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct DerivedVariable {
#[serde(rename = "@id")]
id: String,
#[serde(rename = "@value")]
value: String,
format: Option<Format>,
string_format: Option<StringFormat>,
#[serde(default, rename = "valueMapEntry")]
value_map: Vec<ValueMapEntry>,
}
impl DerivedVariable {
fn mapping(&self) -> Map {
Map(self
.value_map
.iter()
.flat_map(|vme| {
vme.from
.split(';')
.filter_map(|from| from.trim().parse::<f64>().ok())
.map(|from| {
(
OrderedFloat(from),
if let Ok(to) = vme.to.trim().parse::<f64>() {
Datum::Number(Some(to))
} else {
Datum::String(vme.to.clone())
},
)
})
})
.collect())
}
}
impl Variable for DerivedVariable {
fn decode<'a>(
&'a self,
_data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
series: &mut BTreeMap<&'a str, Series>,
warn: &mut dyn FnMut(GraphWarning),
) -> bool {
let mut values = if self.id == "column" || self.id == "row" {
vec![]
} else if let Some(rest) = self.id.strip_prefix("dimension")
&& rest.parse::<usize>().is_ok()
{
vec![]
} else if self.value == "constant(0)" {
fn series_len(series: &mut BTreeMap<&str, Series>) -> Option<usize> {
series.values().find_map(|series| {
if !series.values.is_empty() {
Some(series.values.len())
} else {
None
}
})
}
if let Some(n_values) = series_len(series) {
(0..n_values).map(|_| Datum::Number(Some(0.0))).collect()
} else {
return false;
}
} else if self.value.starts_with("constant") {
vec![]
} else if let Some(rest) = self.value.strip_prefix("map(")
&& let Some(var_name) = rest.strip_suffix(")")
{
let Some(dependency) = series.get(var_name) else {
return false;
};
dependency.values.clone()
} else {
warn(GraphWarning::UnsupportedValue {
variable: self.id.clone(),
value: self.value.clone(),
});
vec![]
};
self.mapping().apply(&mut values);
if let Some(format) = &self.format {
format.mapping().apply(&mut values);
} else if let Some(string_format) = &self.string_format {
string_format.mapping().apply(&mut values);
};
series.insert(
&self.id,
Series::new(
self.id.clone(),
(0..values.len()).map(|_| Some(0)).collect(),
values,
),
);
true
}
fn name(&self) -> &str {
&self.id
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct CategoricalDomain {
#[serde(rename = "@id")]
id: Option<String>,
variable_reference: VariableReference,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct IntervalDomain {
range: IntervalRange,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct IntervalRange {
#[serde(rename = "@min")]
min: f64,
#[serde(rename = "@max")]
max: f64,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct VariableReference {
#[serde(rename = "@ref")]
reference: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct StringFormat {
#[serde(default, rename = "relabel")]
relabels: Vec<Relabel>,
#[serde(default, rename = "affix")]
affixes: Vec<Affix>,
}
impl StringFormat {
fn mapping(&self) -> Map {
Map(self
.relabels
.iter()
.map(|relabel| {
(
OrderedFloat(relabel.from),
Datum::String(relabel.to.clone()),
)
})
.collect())
}
}
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
struct Format {
#[serde(rename = "@baseFormat")]
base_format: Option<BaseFormat>,
#[serde(rename = "@mdyOrder")]
mdy_order: Option<MdyOrder>,
#[serde(rename = "@showQuarter")]
show_quarter: Option<bool>,
#[serde(rename = "@quarterPrefix")]
year_abbreviation: Option<bool>,
#[serde(rename = "@monthFormat")]
month_format: Option<MonthFormat>,
#[serde(rename = "@showWeek")]
show_week: Option<bool>,
#[serde(rename = "@showDay")]
show_day: Option<bool>,
#[serde(rename = "@showHour")]
show_hour: Option<bool>,
#[serde(rename = "@showSecond")]
show_second: Option<bool>,
#[serde(rename = "@showMillis")]
show_millis: Option<bool>,
#[serde(rename = "@maximumFractionDigits")]
maximum_fraction_digits: Option<i64>,
#[serde(rename = "@useGrouping")]
use_grouping: Option<bool>,
#[serde(rename = "@scientific")]
scientific: Option<Scientific>,
#[serde(default, rename = "@prefix")]
prefix: String,
#[serde(default, rename = "@suffix")]
suffix: String,
#[serde(rename = "@tryStringsAsNumbers", default)]
try_strings_as_numbers: bool,
#[serde(default, rename = "relabel")]
relabels: Vec<Relabel>,
#[serde(default, rename = "affix")]
affixes: Vec<Affix>,
}
impl Format {
fn mapping(&self) -> Map {
let format = self.decode();
Map(self
.relabels
.iter()
.map(|relabel| {
let value = match relabel.to.trim().parse::<f64>().ok() {
Some(to) if self.try_strings_as_numbers => Datum::Number(Some(to)),
Some(to) => Datum::String(
Datum::<String>::Number(Some(to))
.display(format)
.with_stretch()
.to_string(),
),
None => Datum::String(relabel.to.clone()),
};
(OrderedFloat(relabel.from), value)
})
.collect())
}
fn decode(&self) -> crate::format::Format {
if self.base_format.is_some() {
SignificantDateTimeFormat::from(self).decode()
} else {
SignificantNumberFormat::from(self).decode()
}
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct NumberFormat {
#[serde(rename = "@maximumFractionDigits")]
maximum_fraction_digits: Option<i64>,
#[serde(rename = "@useGrouping")]
use_grouping: Option<bool>,
#[serde(rename = "@scientific")]
scientific: Option<Scientific>,
#[serde(default, rename = "@prefix")]
prefix: String,
#[serde(default, rename = "@suffix")]
suffix: String,
#[serde(default, rename = "affix")]
affixes: Vec<Affix>,
}
struct SignificantNumberFormat<'a> {
scientific: Option<Scientific>,
prefix: &'a str,
suffix: &'a str,
use_grouping: Option<bool>,
maximum_fraction_digits: Option<i64>,
}
impl<'a> From<&'a NumberFormat> for SignificantNumberFormat<'a> {
fn from(value: &'a NumberFormat) -> Self {
Self {
scientific: value.scientific,
prefix: &value.prefix,
suffix: &value.suffix,
use_grouping: value.use_grouping,
maximum_fraction_digits: value.maximum_fraction_digits,
}
}
}
impl<'a> From<&'a Format> for SignificantNumberFormat<'a> {
fn from(value: &'a Format) -> Self {
Self {
scientific: value.scientific,
prefix: &value.prefix,
suffix: &value.suffix,
use_grouping: value.use_grouping,
maximum_fraction_digits: value.maximum_fraction_digits,
}
}
}
impl<'a> SignificantNumberFormat<'a> {
fn decode(&self) -> crate::format::Format {
let type_ = if self.scientific == Some(Scientific::True) {
Type::E
} else if self.prefix == "$" {
Type::Dollar
} else if self.suffix == "%" {
Type::Pct
} else if self.use_grouping == Some(true) {
Type::Comma
} else {
Type::F
};
let d = match self.maximum_fraction_digits {
Some(d) if (0..=15).contains(&d) => d,
_ => 2,
};
UncheckedFormat {
type_,
w: 40,
d: d as u8,
}
.fix()
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct DateTimeFormat {
#[serde(rename = "@baseFormat")]
base_format: BaseFormat,
#[serde(rename = "@mdyOrder")]
mdy_order: Option<MdyOrder>,
#[serde(rename = "@showQuarter")]
show_quarter: Option<bool>,
#[serde(rename = "@yearAbbreviation")]
year_abbreviation: Option<bool>,
#[serde(rename = "@monthFormat")]
month_format: Option<MonthFormat>,
#[serde(rename = "@showWeek")]
show_week: Option<bool>,
#[serde(rename = "@showDay")]
show_day: Option<bool>,
#[serde(rename = "@showHour")]
show_hour: Option<bool>,
#[serde(rename = "@showSecond")]
show_second: Option<bool>,
#[serde(rename = "@showMillis")]
show_millis: Option<bool>,
#[serde(default, rename = "affix")]
affixes: Vec<Affix>,
}
struct SignificantDateTimeFormat {
base_format: Option<BaseFormat>,
show_quarter: Option<bool>,
show_week: Option<bool>,
show_day: Option<bool>,
show_hour: Option<bool>,
show_second: Option<bool>,
show_millis: Option<bool>,
mdy_order: Option<MdyOrder>,
month_format: Option<MonthFormat>,
year_abbreviation: Option<bool>,
}
impl From<&Format> for SignificantDateTimeFormat {
fn from(value: &Format) -> Self {
Self {
base_format: value.base_format,
show_quarter: value.show_quarter,
show_week: value.show_week,
show_day: value.show_day,
show_hour: value.show_hour,
show_second: value.show_second,
show_millis: value.show_millis,
mdy_order: value.mdy_order,
month_format: value.month_format,
year_abbreviation: value.year_abbreviation,
}
}
}
impl From<&DateTimeFormat> for SignificantDateTimeFormat {
fn from(value: &DateTimeFormat) -> Self {
Self {
base_format: Some(value.base_format),
show_quarter: value.show_quarter,
show_week: value.show_week,
show_day: value.show_day,
show_hour: value.show_hour,
show_second: value.show_second,
show_millis: value.show_millis,
mdy_order: value.mdy_order,
month_format: value.month_format,
year_abbreviation: value.year_abbreviation,
}
}
}
impl SignificantDateTimeFormat {
fn decode(&self) -> crate::format::Format {
let type_ = match self.base_format {
Some(BaseFormat::Date) => {
let type_ = if self.show_quarter == Some(true) {
Type::QYr
} else if self.show_week == Some(true) {
Type::WkYr
} else {
match (self.mdy_order, self.month_format) {
(Some(MdyOrder::DayMonthYear), Some(MonthFormat::Number)) => Type::EDate,
(Some(MdyOrder::DayMonthYear), Some(MonthFormat::PaddedNumber)) => {
Type::EDate
}
(Some(MdyOrder::DayMonthYear), _) => Type::Date,
(Some(MdyOrder::YearMonthDay), _) => Type::SDate,
_ => Type::ADate,
}
};
let mut w = type_.min_width();
if self.year_abbreviation != Some(true) {
w += 2;
};
return UncheckedFormat { type_, w, d: 0 }.try_into().unwrap();
}
Some(BaseFormat::DateTime) => {
if self.mdy_order == Some(MdyOrder::YearMonthDay) {
Type::YmdHms
} else {
Type::DateTime
}
}
_ => {
if self.show_day == Some(true) {
Type::DTime
} else if self.show_hour == Some(true) {
Type::Time
} else {
Type::MTime
}
}
};
date_time_format(type_, self.show_second, self.show_millis)
}
}
fn date_time_format(
type_: Type,
show_second: Option<bool>,
show_millis: Option<bool>,
) -> crate::format::Format {
let mut w = type_.min_width();
let mut d = 0;
if show_second == Some(true) {
w += 3;
if show_millis == Some(true) {
d = 3;
w += d as u16 + 1;
}
}
UncheckedFormat { type_, w, d }.try_into().unwrap()
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ElapsedTimeFormat {
#[serde(rename = "@showDay")]
show_day: Option<bool>,
#[serde(rename = "@showHour")]
show_hour: Option<bool>,
#[serde(rename = "@showSecond")]
show_second: Option<bool>,
#[serde(rename = "@showMillis")]
show_millis: Option<bool>,
#[serde(default, rename = "affix")]
affixes: Vec<Affix>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum BaseFormat {
Date,
Time,
DateTime,
ElapsedTime,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum MdyOrder {
DayMonthYear,
MonthDayYear,
YearMonthDay,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum MonthFormat {
Long,
Short,
Number,
PaddedNumber,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum Scientific {
OnlyForSmall,
WhenNeeded,
True,
False,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Affix {
#[serde(rename = "@definesReference")]
defines_reference: NonZeroUsize,
}
#[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>,
#[serde(rename = "@color")]
color: Option<Color>,
#[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 = "@textAlignment")]
text_alignment: Option<TextAlignment>,
#[serde(rename = "@labelLocationVertical")]
label_location_vertical: Option<LabelLocation>,
#[serde(rename = "@visible")]
visible: Option<bool>,
#[serde(rename = "@decimal-offset")]
decimal_offset: Option<Length>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum FontWeight {
Regular,
Bold,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum FontStyle {
Regular,
Italic,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum FontUnderline {
None,
Underline,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum TextAlignment {
Left,
Right,
Center,
Decimal,
Mixed,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum LabelLocation {
Positive,
Negative,
Center,
}
impl From<LabelLocation> for VertAlign {
fn from(value: LabelLocation) -> Self {
match value {
LabelLocation::Positive => VertAlign::Top,
LabelLocation::Negative => VertAlign::Bottom,
LabelLocation::Center => VertAlign::Middle,
}
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Graph {
faceting: Faceting,
facet_layout: FacetLayout,
interval: Interval,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Faceting {
#[serde(default, rename = "$value")]
children: Vec<FacetingChild>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum FacetingChild {
Cross(Cross),
Layer(Layer),
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Cross {
#[serde(rename = "$value")]
children: [CrossChild; 2],
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum CrossChild {
Unity,
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,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct FacetLayout {
#[serde(rename = "$value")]
children: Vec<FacetLayoutChild>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum FacetLayoutChild {
SetCellProperties(SetCellProperties),
FacetLevel(FacetLevel),
#[serde(other)]
Other,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct SetCellProperties {
#[serde(rename = "@applyToConverse", default)]
apply_to_converse: bool,
#[serde(rename = "$value")]
sets: Vec<Set>,
#[serde(rename = "union")]
union_: 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")]
children: Vec<IntersectChild>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum IntersectChild {
Where(Where),
#[serde(other)]
Other,
}
#[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")]
enum Set {
SetFormat(SetFormat),
SetStyle(SetStyle),
SetFrameStyle(SetFrameStyle),
#[serde(other)]
Other,
}
#[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 SetFrameStyle {
#[serde(rename = "@target")]
target: String,
#[serde(rename = "@style")]
style: Ref<Style>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct SetFormat {
#[serde(rename = "@target")]
target: String,
#[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 Interval {}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Labeling {
#[serde(rename = "$value", default)]
children: Vec<LabelingChild>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum LabelingChild {
Formatting(Formatting),
Footnotes(Footnotes),
#[serde(other)]
Other,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Formatting {
#[serde(rename = "@variable")]
variable: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Footnotes {
#[serde(rename = "@variable")]
variable: String,
#[serde(default, rename = "@superscript")]
superscript: bool,
#[serde(default, rename = "footnoteMapping")]
mappings: Vec<FootnoteMapping>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct FootnoteMapping {
#[serde(rename = "@definesReference")]
defines_reference: Option<NonZeroUsize>,
#[serde(rename = "@from")]
from: NonZeroUsize,
#[serde(rename = "@to")]
to: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct FacetLevel {
#[serde(rename = "@level")]
level: usize,
axis: Axis,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Axis {
label: Option<Label>,
major_ticks: MajorTicks,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct MajorTicks {
#[serde(rename = "@style")]
style: Ref<Style>,
}
#[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(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Enum)]
#[serde(rename_all = "camelCase")]
enum Purpose {
Title,
SubTitle,
SubSubTitle,
Layer,
Footnote,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
enum LabelChild {
Text(Vec<Text>),
#[serde(other)]
Other,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Text {
#[serde(rename = "@usesReference")]
uses_reference: Option<NonZeroUsize>,
#[serde(rename = "@definesReference")]
defines_reference: Option<NonZeroUsize>,
#[serde(default, rename = "$text")]
text: String,
}
enum DecodedText<'a> {
FootnoteDefinition { index: usize, text: &'a str },
FootnoteReference { index: usize },
Text { text: &'a str },
}
impl Text {
fn decode(&self) -> DecodedText<'_> {
if let Some(uses_reference) = self.uses_reference {
DecodedText::FootnoteDefinition {
index: uses_reference.get() - 1,
text: &self.text,
}
} else if let Some(defines_reference) = self.defines_reference {
DecodedText::FootnoteReference {
index: defines_reference.get() - 1,
}
} else {
DecodedText::Text { text: &self.text }
}
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct LabelFrame {
label: Option<Label>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Container {
#[serde(rename = "labelFrame")]
#[serde(default)]
label_frames: Vec<LabelFrame>,
}