From 4faa576070c5a7d86626ded672345994eb1661af Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 3 Jan 2026 15:54:06 -0800 Subject: [PATCH] work --- rust/pspp/src/format.rs | 19 ++++++- rust/pspp/src/format/decimals.rs | 2 +- rust/pspp/src/spv/read/legacy_xml.rs | 76 +++++++++++++++++----------- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/rust/pspp/src/format.rs b/rust/pspp/src/format.rs index 4dca95e1e6..c45da56aae 100644 --- a/rust/pspp/src/format.rs +++ b/rust/pspp/src/format.rs @@ -90,19 +90,34 @@ pub enum Error { }, } +/// Format [Type] categories. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Category { - // Numeric formats. + /// F, COMMA, DOT, DOLLAR, PCT, and E. Basic, + + /// CCx. Custom, + + /// N and Z. Legacy, + + /// P, PK, IB, PIB, and RB. Binary, + + /// PIBHEX and RBHEX. Hex, + + /// DATE, ADATE, EDATE, JDATE, SDATE, QYR, MOYR, WKYR, DATETIME, and YMDHMS. Date, + + /// MTIME, TIME, and DTIME. Time, + + /// WKDAY and MONTH. DateComponent, - // String formats. + // A and AHEX. String, } diff --git a/rust/pspp/src/format/decimals.rs b/rust/pspp/src/format/decimals.rs index 12c7e80d9d..8813c00ff1 100644 --- a/rust/pspp/src/format/decimals.rs +++ b/rust/pspp/src/format/decimals.rs @@ -9,8 +9,8 @@ //! , rename it as //! `cldr-json-full.zip` in the same directory as `build.rs`, //! and touch `build.rs` to force a rebuild. -use std::{collections::HashMap, sync::LazyLock}; use crate::format::Decimal; +use std::{collections::HashMap, sync::LazyLock}; /// Map from language to decimal point. pub static LANG_TO_DECIMAL: LazyLock> = LazyLock::new(|| { diff --git a/rust/pspp/src/spv/read/legacy_xml.rs b/rust/pspp/src/spv/read/legacy_xml.rs index e11dba6772..4150ab8e21 100644 --- a/rust/pspp/src/spv/read/legacy_xml.rs +++ b/rust/pspp/src/spv/read/legacy_xml.rs @@ -213,21 +213,14 @@ impl Visualization { } } for label in footnote_labels { - for (index, text) in label.text().iter().enumerate() { - if let Some(uses_reference) = text.uses_reference { - let entry = footnote_builder - .entry(uses_reference.get() - 1) - .or_default(); - if index % 2 == 1 { - entry.content = text.text.strip_suffix('\n').unwrap_or(&text.text).into(); + for (i, text) in label.text().iter().enumerate() { + if let DecodedText::FootnoteDefinition { index, text } = text.decode() { + let entry = footnote_builder.entry(index).or_default(); + if i % 2 == 1 { + entry.content = text.strip_suffix('\n').unwrap_or(text).into(); } else { - entry.marker = Some( - text.text - .trim_end() - .strip_suffix('.') - .unwrap_or(&text.text) - .into(), - ); + entry.marker = + Some(text.trim_end().strip_suffix('.').unwrap_or(text).into()); } } } @@ -2058,9 +2051,12 @@ enum LabelChild { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct Text { + /// If this is present, then `text` defines the content or the marker for a + /// footnote. #[serde(rename = "@usesReference")] uses_reference: Option, + /// If this is present, then #[serde(rename = "@definesReference")] defines_reference: Option, @@ -2068,6 +2064,34 @@ struct Text { text: String, } +enum DecodedText<'a> { + /// Defines footnote `index` content or marker as `text`. + FootnoteDefinition { index: usize, text: &'a str }, + + /// Adds a reference to footnote `index`. + FootnoteReference { index: usize }, + + /// Text content. + 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 { @@ -2079,26 +2103,20 @@ impl LabelFrame { if !labels.is_empty() { let mut s = String::new(); let mut f = Vec::new(); - for t in labels { - if let LabelChild::Text(text) = &t.child { - for t in text { - if t.uses_reference.is_none() { - if let Some(defines_reference) = t.defines_reference - && let Some(footnote) = footnotes.get(defines_reference.get() - 1) - { - f.push(footnote); - } else { - s += &t.text; + for label in labels { + for text in label.text() { + match text.decode() { + DecodedText::FootnoteReference { index } => { + if let Some(footnote) = footnotes.get(index) { + f.push(footnote) } } + DecodedText::Text { text } => s += text, + _ => (), } } } - let mut value = Value::new_user_text(s); - for footnote in f { - value = value.with_footnote(footnote); - } - Some(value) + Some(Value::new_user_text(s).with_footnotes(f)) } else { None } -- 2.30.2