From 4e8a2aec8d4b8073813f3cd5f464a215106042a5 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 14 Oct 2025 08:50:34 -0700 Subject: [PATCH] work --- rust/doc/src/commands/set.md | 103 +++++++++- rust/doc/src/spv/light-detail.md | 4 +- rust/pspp/src/output.rs | 35 +++- rust/pspp/src/output/drivers/html.rs | 4 +- rust/pspp/src/output/drivers/spv.rs | 3 +- rust/pspp/src/output/drivers/text.rs | 3 +- rust/pspp/src/output/spv.rs | 30 ++- rust/pspp/src/output/spv/light.rs | 268 ++++++++++++++++----------- 8 files changed, 321 insertions(+), 129 deletions(-) diff --git a/rust/doc/src/commands/set.md b/rust/doc/src/commands/set.md index a7a48a9b59..774f57e197 100644 --- a/rust/doc/src/commands/set.md +++ b/rust/doc/src/commands/set.md @@ -24,7 +24,7 @@ SET /SCALEMIN=COUNT (data output) - /CC{A,B,C,D,E}={'NPRE,PRE,SUF,NSUF','NPRE.PRE.SUF.NSUF'} + /CC{A,B,C,D,E}='STRING' /DECIMAL={DOT,COMMA} /FORMAT=FMT_SPEC /LEADZERO={ON,OFF} @@ -46,13 +46,13 @@ SET /TVARS={NAMES,LABELS,BOTH} /TLOOK={NONE,FILE} -(logging) +(journal) /JOURNAL={ON,OFF} ['FILE_NAME'] (system files) /SCOMPRESSION={ON,OFF} -(miscellaneous) +(security) /SAFER=ON /LOCALE='STRING' @@ -62,7 +62,7 @@ SET /MITERATE=NUMBER /MNEST=NUMBER -(settings not yet implemented, but accepted and ignored) +(not yet implemented) /BASETEXTDIRECTION={AUTOMATIC,RIGHTTOLEFT,LEFTTORIGHT} /BLOCK='C' /BOX={'XXX','XXXXXXXXXXX'} @@ -84,6 +84,15 @@ synonymous, as are `OFF` and `NO`, when used as subcommand values. # Data Input +``` +SET + /BLANKS={SYSMIS,'.',number} + /DECIMAL={DOT,COMMA} + /FORMAT=FMT_SPEC + /EPOCH={AUTOMATIC,YEAR} + /RIB={NATIVE,MSBFIRST,LSBFIRST} +``` + The data input subcommands affect the way that data is read from data files. The data input subcommands are: @@ -128,6 +137,13 @@ files. The data input subcommands are: # Interaction +``` +SET + /MXERRS=MAX_ERRS + /MXWARNS=MAX_WARNINGS + /WORKSPACE=WORKSPACE_SIZE +``` + Interaction subcommands affect the way that PSPP interacts with an online user. The interaction subcommands are @@ -142,6 +158,18 @@ online user. The interaction subcommands are are issued, except a single initial warning advising you that warnings will not be given. The default value is 100. +# Syntax Execution + +``` +SET + /LOCALE='LOCALE' + /MXLOOPS=MAX_LOOPS + /SEED={RANDOM,SEED_VALUE} + /UNDEFINED={WARN,NOWARN} + /FUZZBITS=FUZZBITS + /SCALEMIN=COUNT +``` + Syntax execution subcommands control the way that PSPP commands execute. The syntax execution subcommands are @@ -192,6 +220,17 @@ execute. The syntax execution subcommands are # Data Output +``` +SET + /CC{A,B,C,D,E}='STRING' + /DECIMAL={DOT,COMMA} + /FORMAT=FMT_SPEC + /LEADZERO={ON,OFF} + /MDISPLAY={TEXT,TABLES} + /SMALL=NUMBER + /WIB={NATIVE,MSBFIRST,LSBFIRST} +``` + Data output subcommands affect the format of output data. These subcommands are @@ -246,6 +285,14 @@ subcommands are # Output Routing +``` +SET + /ERRORS={ON,OFF,TERMINAL,LISTING,BOTH,NONE} + /MESSAGES={ON,OFF,TERMINAL,LISTING,BOTH,NONE} + /PRINTBACK={ON,OFF,TERMINAL,LISTING,BOTH,NONE} + /RESULTS={ON,OFF,TERMINAL,LISTING,BOTH,NONE} +``` + In the PSPP text-based interface, the output routing subcommands affect where output is sent. The following values are allowed for each of these subcommands: @@ -287,6 +334,16 @@ environment. # Output Driver +``` +SET + /HEADERS={NO,YES,BLANK} + /LENGTH={NONE,N_LINES} + /WIDTH={NARROW,WIDTH,N_CHARACTERS} + /TNUMBERS={VALUES,LABELS,BOTH} + /TVARS={NAMES,LABELS,BOTH} + /TLOOK={NONE,FILE} +``` + Output driver option subcommands affect output drivers' settings. These subcommands are: @@ -327,6 +384,11 @@ These subcommands are: # Journal +``` +SET + /JOURNAL={ON,OFF} ['FILE_NAME'] +``` + Journal subcommands affect logging of commands executed to external files. These subcommands are @@ -342,6 +404,13 @@ files. These subcommands are The journal is named `pspp.jnl` by default. A different name may be specified. +# System Files + +``` +SET + /SCOMPRESSION={ON,OFF} +``` + System file subcommands affect the default format of system files produced by PSPP. These subcommands are @@ -351,6 +420,12 @@ produced by PSPP. These subcommands are # Security +``` +SET + /SAFER=ON + /LOCALE='STRING' +``` + Security subcommands affect the operations that commands are allowed to perform. The security subcommands are @@ -395,6 +470,14 @@ to perform. The security subcommands are # Macros +``` +SET + /MEXPAND={ON,OFF} + /MPRINT={ON,OFF} + /MITERATE=NUMBER + /MNEST=NUMBER +``` + The following subcommands affect the interpretation of macros. For more information, see [Macro Settings](define.md#macro-settings). @@ -419,6 +502,18 @@ more information, see [Macro Settings](define.md#macro-settings). # Not Yet Implemented +``` +SET + /BASETEXTDIRECTION={AUTOMATIC,RIGHTTOLEFT,LEFTTORIGHT} + /BLOCK='C' + /BOX={'XXX','XXXXXXXXXXX'} + /CACHE={ON,OFF} + /CELLSBREAK=NUMBER + /COMPRESSION={ON,OFF} + /CMPTRANS={ON,OFF} + /HEADER={NO,YES,BLANK} +``` + The following subcommands are not yet implemented, but PSPP accepts them and ignores the settings: diff --git a/rust/doc/src/spv/light-detail.md b/rust/doc/src/spv/light-detail.md index 6af5c0badc..206332ef21 100644 --- a/rust/doc/src/spv/light-detail.md +++ b/rust/doc/src/spv/light-detail.md @@ -780,7 +780,7 @@ really categories; the others just serve as grouping constructs. ``` Category => Value[name] (Leaf | Group) -Leaf => 00 00 00 i2 int32[leaf-index] i0 +Leaf => 00 00 bool[x24] i2 int32[leaf-index] i0 Group => bool[merge] 00 01 int32[x23] i-1 int32[n-subcategories] Category*[n-subcategories] @@ -820,6 +820,8 @@ variable (e.g. in a frequency table or crosstabulation, a group of values in a variable being tabulated) and i0 otherwise. A writer may safely write a constant 0 in this field. +`x24` is usually 0. Its meaning is unexplored. + ## Axes After the dimensions come assignment of each dimension to one of the diff --git a/rust/pspp/src/output.rs b/rust/pspp/src/output.rs index 1ac438c77a..b275f797ce 100644 --- a/rust/pspp/src/output.rs +++ b/rust/pspp/src/output.rs @@ -274,7 +274,7 @@ impl Details { pub fn label(&self) -> Cow<'static, str> { match self { - Details::Chart => todo!(), + Details::Chart => Cow::from("chart"), Details::Image => Cow::from("Image"), Details::Heading(_) => Cow::from("Group"), Details::Message(diagnostic) => Cow::from(diagnostic.severity.as_title_str()), @@ -367,6 +367,15 @@ impl From> for Details { } } +#[derive(Clone, Debug, Serialize)] +pub struct Chart; + +impl Chart { + pub fn into_item(self) -> Item { + Details::Chart.into_item() + } +} + #[derive(Clone, Debug, Serialize)] pub struct Text { type_: TextType, @@ -554,16 +563,30 @@ pub enum SpvMembers { /// `.png` file. String, ), + /// Chart members. + Graph { + /// Data member name. + data: Option, + /// XML member name. + xml: String, + /// CVS member name. + csv: Option, + }, } impl SpvMembers { pub fn iter(&self) -> impl Iterator { - let (a, b) = match self { - SpvMembers::Light(a) => (a.as_str(), None), - SpvMembers::Legacy { xml: a, binary: b } => (a.as_str(), Some(b.as_str())), - SpvMembers::Image(a) => (a.as_str(), None), + let (a, b, c) = match self { + SpvMembers::Light(a) => (Some(a), None, None), + SpvMembers::Legacy { xml, binary } => (Some(xml), Some(binary), None), + SpvMembers::Image(a) => (Some(a), None, None), + SpvMembers::Graph { data, xml, csv } => (data.as_ref(), Some(xml), csv.as_ref()), }; - once(a).chain(once(b).flatten()) + once(a) + .flatten() + .chain(once(b).flatten()) + .chain(once(c).flatten()) + .map(|s| s.as_str()) } } diff --git a/rust/pspp/src/output/drivers/html.rs b/rust/pspp/src/output/drivers/html.rs index f10f192d37..a68304d3a7 100644 --- a/rust/pspp/src/output/drivers/html.rs +++ b/rust/pspp/src/output/drivers/html.rs @@ -427,9 +427,7 @@ where fn write(&mut self, item: &Arc) { match &item.details { - Details::Chart => todo!(), - Details::Image => todo!(), - Details::Heading(_) => todo!(), + Details::Chart | Details::Image | Details::Heading(_) => todo!(), Details::Message(_diagnostic) => todo!(), Details::PageBreak => (), Details::Table(pivot_table) => { diff --git a/rust/pspp/src/output/drivers/spv.rs b/rust/pspp/src/output/drivers/spv.rs index 97f3c28568..2983d4f95d 100644 --- a/rust/pspp/src/output/drivers/spv.rs +++ b/rust/pspp/src/output/drivers/spv.rs @@ -190,8 +190,7 @@ where X: Write, { match &item.details { - Details::Chart => todo!(), - Details::Image => todo!(), + Details::Chart | Details::Image => todo!(), Details::Heading(children) => { let mut attributes = Vec::::new(); if let Some(command_name) = &item.command_name { diff --git a/rust/pspp/src/output/drivers/text.rs b/rust/pspp/src/output/drivers/text.rs index a2c7be650a..7ec73eb349 100644 --- a/rust/pspp/src/output/drivers/text.rs +++ b/rust/pspp/src/output/drivers/text.rs @@ -382,8 +382,7 @@ impl TextRenderer { W: FmtWrite, { match &item.details { - Details::Chart => todo!(), - Details::Image => todo!(), + Details::Chart | Details::Image => todo!(), Details::Heading(children) => { for (index, child) in children.0.iter().enumerate() { if index > 0 { diff --git a/rust/pspp/src/output/spv.rs b/rust/pspp/src/output/spv.rs index 073b94291e..3ab1d0fdb6 100644 --- a/rust/pspp/src/output/spv.rs +++ b/rust/pspp/src/output/spv.rs @@ -166,6 +166,9 @@ impl Heading { table.decode(archive, structure_member).unwrap(), /* XXX*/ ); } + ContainerContent::Graph(graph) => { + items.push(graph.decode(structure_member)); + } ContainerContent::Text(container_text) => { items.push( Text::new( @@ -258,14 +261,37 @@ enum TextAlign { enum ContainerContent { Table(Table), Text(ContainerText), - /* Graph(Graph), - Model(Model), + /* Model(Model), Object(Object), Image(Image), Tree(Tree),*/ } +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Graph { + #[serde(rename = "@commandName")] + command_name: String, + data_path: Option, + path: String, + csv_path: Option, +} + +impl Graph { + fn decode(&self, structure_member: &str) -> Item { + crate::output::Chart + .into_item() + .with_spv_info( + SpvInfo::new(structure_member).with_members(SpvMembers::Graph { + data: self.data_path.clone(), + xml: self.path.clone(), + csv: self.csv_path.clone(), + }), + ) + } +} + #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct Table { diff --git a/rust/pspp/src/output/spv/light.rs b/rust/pspp/src/output/spv/light.rs index c7be973dad..c65481c81d 100644 --- a/rust/pspp/src/output/spv/light.rs +++ b/rust/pspp/src/output/spv/light.rs @@ -7,7 +7,7 @@ use std::{ sync::Arc, }; -use binrw::{BinRead, BinResult, Endian, Error as BinError, VecArgs, binread}; +use binrw::{BinRead, BinResult, Endian, Error as BinError, VecArgs, binread, io::TakeSeekExt}; use chrono::DateTime; use displaydoc::Display; use encoding_rs::{Encoding, WINDOWS_1252}; @@ -52,22 +52,24 @@ pub struct LightTable { header: Header, #[br(args(header.version))] titles: Titles, - #[br(parse_with(parse_counted), args(header.version))] + #[br(parse_with(parse_vec), args(header.version))] footnotes: Vec, #[br(args(header.version))] areas: Areas, - borders: Counted, - print_settings: Counted, - #[br(dbg, if(header.version == Version::V3))] - table_settings: Counted, + #[br(parse_with(parse_counted))] + borders: Borders, + #[br(parse_with(parse_counted))] + print_settings: PrintSettings, + #[br(dbg, if(header.version == Version::V3), parse_with(parse_counted))] + table_settings: TableSettings, #[br(if(header.version == Version::V1), temp)] _ts: Option>, #[br(args(header.version))] formats: Formats, - #[br(parse_with(parse_counted), args(header.version))] + #[br(parse_with(parse_vec), args(header.version))] dimensions: Vec, axes: Axes, - #[br(parse_with(parse_counted), args(header.version))] + #[br(parse_with(parse_vec), args(header.version))] cells: Vec, } @@ -297,7 +299,7 @@ where } #[binrw::parser(reader, endian)] -fn parse_counted(inner: A, ...) -> BinResult> +fn parse_vec(inner: A, ...) -> BinResult> where for<'a> T: BinRead = A>, A: Clone, @@ -456,7 +458,7 @@ struct Margins { #[br(big)] #[derive(Debug)] struct Borders { - #[br(magic(1u32), parse_with(parse_counted))] + #[br(magic(1u32), parse_with(parse_vec))] borders: Vec, #[br(parse_with(parse_bool))] @@ -572,10 +574,10 @@ struct TableSettings { show_alphabetic_markers: bool, #[br(parse_with(parse_bool))] footnote_marker_subscripts: bool, - #[br(temp, parse_with(parse_bool))] - _x6: bool, - #[br(big)] - sizing: Counted, + #[br(temp)] + _x6: u8, + #[br(big, parse_with(parse_counted))] + sizing: Sizing, notes: U32String, table_look: U32String, #[br(temp)] @@ -586,17 +588,17 @@ struct TableSettings { #[br(big)] #[derive(Debug, Default)] struct Sizing { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] row_breaks: Vec, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] column_breaks: Vec, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] row_keeps: Vec<(i32, i32)>, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] column_keeps: Vec<(i32, i32)>, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] row_point_keeps: Vec<[i32; 3]>, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] column_point_keeps: Vec<[i32; 3]>, } @@ -664,7 +666,7 @@ impl BinRead for Value { #[binread] #[derive(Default)] struct U32String { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] string: Vec, } @@ -698,7 +700,7 @@ impl Debug for U32String { #[binread] struct CountedInner { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] data: Vec, } @@ -736,16 +738,18 @@ where endian: binrw::Endian, args: Self::Args<'_>, ) -> BinResult { - let counted = CountedInner::read_options(reader, endian, ())?; - let mut cursor = counted.cursor(); - let result = ::read_options(&mut cursor, Endian::Little, args)?; - if cursor.position() < cursor.get_ref().len() as u64 { + let start = reader.stream_position()?; + let count = u32::read_options(reader, endian, ())? as u64; + let end = start + count; + let mut inner = reader.take_seek(count); + let result = ::read_options(&mut inner, Endian::Little, args)?; + let pos = inner.stream_position()?; + if pos < end { + let consumed = pos - start; return Err(binrw::Error::Custom { - pos: cursor.position(), + pos, err: Box::new(format!( - "counted data not exhausted (consumed {} bytes out of {})", - cursor.position(), - cursor.get_ref().len() + "counted data not exhausted (consumed {consumed} bytes out of {count})" )), }); } @@ -753,6 +757,16 @@ where } } +#[binrw::parser(reader, endian)] +fn parse_counted(args: A, ...) -> BinResult +where + for<'a> T: BinRead = A>, + A: Clone, + T: 'static, +{ + Ok(>::read_options(reader, endian, args)?.0) +} + /// `BinRead` for `Option` always requires the value to be there. This /// instead tries to read it and falls back to None if there's no match. #[derive(Clone, Debug)] @@ -798,7 +812,7 @@ where #[br(import(version: Version))] #[derive(Debug)] struct Formats { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] column_widths: Vec, locale: U32String, current_layer: i32, @@ -831,11 +845,11 @@ impl Formats { } fn x2(&self) -> Option<&X2> { - self.v3.as_ref().map(|v3| &*v3.x1_x2.x2) + self.v3.as_ref().map(|v3| &v3.x1_x2.x2) } fn x3(&self) -> Option<&X3> { - self.v3.as_ref().map(|v3| &*v3.x3) + self.v3.as_ref().map(|v3| &v3.x3) } fn charset(&self) -> Option<&U32String> { @@ -864,8 +878,10 @@ impl Formats { #[derive(Debug)] struct FormatsV3 { #[br(dbg)] - x1_x2: Counted, - x3: Counted, + #[br(parse_with(parse_counted))] + x1_x2: X1X2, + #[br(parse_with(parse_counted))] + x3: X3, } #[binread] @@ -873,7 +889,8 @@ struct FormatsV3 { #[derive(Debug)] struct X1X2 { x1: X1, - x2: Counted, + #[br(parse_with(parse_counted))] + x2: X2, } #[binread] @@ -961,13 +978,14 @@ fn parse_show() -> BinResult> { #[br(little)] #[derive(Debug)] struct X2 { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] row_heights: Vec, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] style_map: Vec<(i64, i16)>, - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] styles: Vec, - tail: Counted>, + #[br(parse_with(parse_counted))] + tail: Optional<[u8; 8]>, } #[binread] @@ -980,6 +998,8 @@ struct X3 { y1: Y1, #[br(dbg)] small: f64, + #[br(magic = 1u8, temp)] + _one: (), #[br(dbg)] inner: Optional, y2: Y2, @@ -1039,7 +1059,7 @@ impl Y0 { #[br(little)] #[derive(Debug)] struct CustomCurrency { - #[br(parse_with(parse_counted))] + #[br(parse_with(parse_vec))] ccs: Vec, } @@ -1059,7 +1079,7 @@ impl CustomCurrency { #[binread] #[br(little)] -#[br(return_unexpected_error, import(version: Version))] +#[br( import(version: Version))] #[derive(Debug)] enum RawValue { #[br(magic = 1u8)] @@ -1122,10 +1142,11 @@ enum RawValue { c: U32String, }, Template { - #[br(parse_with(parse_optional), args(version))] + #[br(dbg, parse_with(parse_optional), args(version))] mods: Option, + #[br(dbg)] template: U32String, - #[br(parse_with(parse_counted), args(version))] + #[br(parse_with(parse_vec), args(version))] args: Vec, }, } @@ -1234,16 +1255,35 @@ impl RawValue { } } -#[binread] -#[br(little)] -#[br(import(version: Version))] #[derive(Debug)] -enum Argument { - Singleton(#[br(magic(0u32), args(version))] Value), - Multiple { - #[br(magic(0u32), parse_with(parse_counted), args(version))] - values: Vec, - }, +struct Argument(Vec); + +impl BinRead for Argument { + type Args<'a> = (Version,); + + fn read_options( + reader: &mut R, + endian: Endian, + (version,): (Version,), + ) -> BinResult { + let count = u32::read_options(reader, endian, ())? as usize; + dbg!(count); + if count == 0 { + Ok(Self(vec![Value::read_options(reader, endian, (version,))?])) + } else { + let zero = u32::read_options(reader, endian, ())?; + assert_eq!(zero, 0); + let values = >::read_options( + reader, + endian, + VecArgs { + count, + inner: (version,), + }, + )?; + Ok(Self(values)) + } + } } impl Argument { @@ -1252,13 +1292,10 @@ impl Argument { encoding: &'static Encoding, footnotes: &pivot::Footnotes, ) -> Vec { - match self { - Argument::Singleton(value) => vec![value.decode(encoding, footnotes)], - Argument::Multiple { values } => values - .iter() - .map(|value| value.decode(encoding, footnotes)) - .collect(), - } + self.0 + .iter() + .map(|value| value.decode(encoding, footnotes)) + .collect() } } @@ -1266,56 +1303,65 @@ impl Argument { #[br(little, import(version: Version))] #[derive(Debug)] struct ValueMods { - #[br(parse_with(parse_counted))] + #[br(dbg, parse_with(parse_vec))] refs: Vec, - #[br(parse_with(parse_counted))] + #[br(dbg, parse_with(parse_vec))] subscripts: Vec, #[br(if(version == Version::V1))] v1: Option, - #[br(if(version == Version::V3))] - v3: Counted, StylePair)>>, + #[br(if(version == Version::V3), parse_with(parse_counted))] + v3: ValueModsV3, +} + +#[binread] +#[br(little)] +#[derive(Debug, Default)] +struct ValueModsV3 { + #[br(parse_with(parse_counted))] + template_string: Optional, + style_pair: StylePair, } impl ValueMods { fn decode(&self, encoding: &'static Encoding, footnotes: &pivot::Footnotes) -> ValueStyle { - let style_pair = self.v3.as_ref().map(|v3| &v3.1); - let font_style = style_pair - .and_then(|style_pair| style_pair.font_style.as_ref()) - .map(|font_style| pivot::FontStyle { - bold: font_style.bold, - italic: font_style.italic, - underline: font_style.underline, - markup: false, - font: font_style.typeface.decode(encoding), - fg: font_style.fg, - bg: font_style.bg, - size: (font_style.size as i32) * 4 / 3, - }); - let cell_style = style_pair - .and_then(|style_pair| style_pair.cell_style.as_ref()) - .map(|cell_style| { - pivot::CellStyle { - horz_align: match cell_style.halign { - 0 => Some(HorzAlign::Center), - 2 => Some(HorzAlign::Left), - 4 => Some(HorzAlign::Right), - 6 => Some(HorzAlign::Decimal { - offset: cell_style.decimal_offset, - decimal: Decimal::Dot, /*XXX*/ - }), - _ => None, - }, - vert_align: match cell_style.valign { - 0 => VertAlign::Middle, - 3 => VertAlign::Bottom, - _ => VertAlign::Top, - }, - margins: enum_map! { - Axis2::X => [cell_style.left_margin as i32, cell_style.right_margin as i32], - Axis2::Y => [cell_style.top_margin as i32, cell_style.bottom_margin as i32], - }, - } - }); + let font_style = + self.v3 + .style_pair + .font_style + .as_ref() + .map(|font_style| pivot::FontStyle { + bold: font_style.bold, + italic: font_style.italic, + underline: font_style.underline, + markup: false, + font: font_style.typeface.decode(encoding), + fg: font_style.fg, + bg: font_style.bg, + size: (font_style.size as i32) * 4 / 3, + }); + let cell_style = self.v3.style_pair.cell_style.as_ref().map(|cell_style| { + pivot::CellStyle { + horz_align: match cell_style.halign { + 0 => Some(HorzAlign::Center), + 2 => Some(HorzAlign::Left), + 4 => Some(HorzAlign::Right), + 6 => Some(HorzAlign::Decimal { + offset: cell_style.decimal_offset, + decimal: Decimal::Dot, /*XXX*/ + }), + _ => None, + }, + vert_align: match cell_style.valign { + 0 => VertAlign::Middle, + 3 => VertAlign::Bottom, + _ => VertAlign::Top, + }, + margins: enum_map! { + Axis2::X => [cell_style.left_margin as i32, cell_style.right_margin as i32], + Axis2::Y => [cell_style.top_margin as i32, cell_style.bottom_margin as i32], + }, + } + }); ValueStyle { cell_style, font_style, @@ -1338,9 +1384,9 @@ impl ValueMods { } fn template_id(&self, encoding: &'static Encoding) -> Option { self.v3 + .template_string .as_ref() - .map(|v3| &*v3.0) - .and_then(|ts| ts.id.as_ref()) + .and_then(|template_string| template_string.id.as_ref()) .map(|s| s.decode(encoding)) } } @@ -1349,7 +1395,8 @@ impl ValueMods { #[br(little)] #[derive(Debug)] struct TemplateString { - _sponge: Counted, + #[br(parse_with(parse_counted), temp)] + _sponge: Sponge, #[br(parse_with(parse_optional))] id: Option, } @@ -1369,7 +1416,7 @@ impl BinRead for Sponge { #[binread] #[br(little)] -#[derive(Debug)] +#[derive(Debug, Default)] struct StylePair { #[br(parse_with(parse_optional))] font_style: Option, @@ -1429,7 +1476,7 @@ struct Dimension { hide_all_labels: bool, #[br(magic(1u8), temp)] _dim_index: i32, - #[br(parse_with(parse_counted), args(version))] + #[br(parse_with(parse_vec), args(version))] categories: Vec, } @@ -1477,7 +1524,9 @@ impl Category { #[derive(Debug)] enum Child { Leaf { - #[br(magic(b"\0\0\0\x02\0\0\0"))] + #[br(magic(0u16), parse_with(parse_bool), temp)] + _x24: bool, + #[br(magic(b"\x02\0\0\0"))] leaf_index: u32, #[br(magic(0u32), temp)] _tail: (), @@ -1487,7 +1536,7 @@ enum Child { merge: bool, #[br(temp, magic(b"\0\x01"))] _x23: i32, - #[br(magic(-1i32), parse_with(parse_counted), args(version))] + #[br(magic(-1i32), parse_with(parse_vec), args(version))] subcategories: Vec>, }, } @@ -1554,6 +1603,7 @@ impl Axes { #[br(little, import(version: Version))] #[derive(Debug)] struct Cell { + #[br(dbg)] index: u64, #[br(if(version == Version::V1), temp)] _zero: Optional, -- 2.30.2