From: Ben Pfaff Date: Thu, 1 Jan 2026 00:28:16 +0000 (-0800) Subject: successfully read just an outline X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=46096569d46b200fcf464ee6238659f2c1c1620c;p=pspp successfully read just an outline --- diff --git a/rust/pspp/src/cli/show_spv.rs b/rust/pspp/src/cli/show_spv.rs index 3a4c7f05f5..475aaa8341 100644 --- a/rust/pspp/src/cli/show_spv.rs +++ b/rust/pspp/src/cli/show_spv.rs @@ -15,12 +15,22 @@ // this program. If not, see . use anyhow::Result; +use binrw::{BinRead, error::ContextExt}; use clap::{Args, ValueEnum}; use pspp::{ - output::{Criteria, Item, Itemlike}, - spv::SpvArchive, + output::{ + Criteria, Item, Itemlike, SpvMembers, + pivot::{Axis3, Dimension, Group, Leaf, PivotTable, value::Value}, + }, + spv::{SpvArchive, legacy_bin::LegacyBin}, +}; +use std::{ + collections::HashMap, + fmt::Display, + io::{Cursor, Read}, + path::PathBuf, + sync::Arc, }; -use std::{fmt::Display, path::PathBuf, sync::Arc}; /// Show information about SPSS viewer files (SPV files). #[derive(Args, Clone, Debug)] @@ -127,65 +137,64 @@ impl ShowSpv { } fn legacy_data(self) -> Result<()> { - todo!() /* - let spv = SpvArchive::open_file(&self.input, self.password.as_deref())?; - let outline = spv.read_outline(|w| eprintln!("{w}"))?; - for item in self.read()? { - for item in ItemRefIterator::new(&item) { - if let Some(spv_info) = &item.spv_info - && let Some(members) = &spv_info.members - && let SpvMembers::LegacyTable { xml: _, binary } = &members - { - let mut bin_member = spv_file.archive.by_name(&binary)?; - let mut bin_data = Vec::with_capacity(bin_member.size() as usize); - bin_member.read_to_end(&mut bin_data)?; - let mut cursor = Cursor::new(bin_data); - let legacy_bin = LegacyBin::read(&mut cursor).map_err(|e| { - e.with_message(format!( - "While parsing {binary:?} as legacy binary SPV member" - )) - })?; - let data = legacy_bin.decode(); - let n_values = data - .values() - .flat_map(|map| map.values()) - .map(|values| values.len()) - .max() - .unwrap_or(0); - let index = Dimension::new( - Group::new("Index") - .with_multiple(Leaf::numbers(0..n_values)) - .with_label_shown(), - ); - let variables = Dimension::new(Group::new("Variables").with_multiple( - data.iter().map(|(name, contents)| { - Group::new(name.as_str()).with_multiple(contents.keys().map(|name| { - name.replace("categories", "\ncategories") - .replace("labels", "\nlabels") - .replace("group", "\ngroup") - .replace("Label", "\nLabel") - })) - }), - )); - let mut pivot_table = - PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]); - let formats = HashMap::new(); - for (variable_index, (variable_name, values)) in - data.values().flat_map(|map| map.iter()).enumerate() - { - for (value_index, data_value) in values.iter().enumerate() { - let value = Value::new_datum(&data_value.value).with_value_label( - (variable_name == "cellFormat") - .then(|| data_value.as_format(&formats).to_string()), - ); - pivot_table.insert([value_index, variable_index], value); - } - } - println!("{pivot_table}"); + let mut archive = SpvArchive::open_file(&self.input, self.password.as_deref())?; + let outline = archive.read_outline(|w| eprintln!("{w}"))?; + for item in outline.items { + for item in item.iter_in_order() { + if let Some(spv_info) = item.spv_info() + && let Some(members) = &spv_info.members + && let SpvMembers::LegacyTable { xml: _, binary } = &members + { + let mut bin_member = archive.0.by_name(&binary)?; + let mut bin_data = Vec::with_capacity(bin_member.size() as usize); + bin_member.read_to_end(&mut bin_data)?; + let mut cursor = Cursor::new(bin_data); + let legacy_bin = LegacyBin::read(&mut cursor).map_err(|e| { + e.with_message(format!( + "While parsing {binary:?} as legacy binary SPV member" + )) + })?; + let data = legacy_bin.decode(); + let n_values = data + .values() + .flat_map(|map| map.values()) + .map(|values| values.len()) + .max() + .unwrap_or(0); + let index = Dimension::new( + Group::new("Index") + .with_multiple(Leaf::numbers(0..n_values)) + .with_label_shown(), + ); + let variables = Dimension::new(Group::new("Variables").with_multiple( + data.iter().map(|(name, contents)| { + Group::new(name.as_str()).with_multiple(contents.keys().map(|name| { + name.replace("categories", "\ncategories") + .replace("labels", "\nlabels") + .replace("group", "\ngroup") + .replace("Label", "\nLabel") + })) + }), + )); + let mut pivot_table = + PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]); + let formats = HashMap::new(); + for (variable_index, (variable_name, values)) in + data.values().flat_map(|map| map.iter()).enumerate() + { + for (value_index, data_value) in values.iter().enumerate() { + let value = Value::new_datum(&data_value.value).with_value_label( + (variable_name == "cellFormat") + .then(|| data_value.as_format(&formats).to_string()), + ); + pivot_table.insert([value_index, variable_index], value); + } + } + println!("{pivot_table}"); + } + } } - } - } - Ok(())*/ + Ok(()) } } diff --git a/rust/pspp/src/output.rs b/rust/pspp/src/output.rs index c1f5987450..a2f1d69532 100644 --- a/rust/pspp/src/output.rs +++ b/rust/pspp/src/output.rs @@ -84,12 +84,12 @@ pub struct Item { } pub trait Itemlike { - fn label(&self) -> Cow<'static, str>; + fn label(&self) -> Cow<'_, str>; fn command_name(&self) -> Option<&str>; fn subtype(&self) -> Option; fn is_shown(&self) -> bool; fn page_break_before(&self) -> bool; - fn spv_info(&self) -> Option<&SpvInfo>; + fn spv_info(&self) -> Option>; fn iter_in_order(&self) -> ItemRefIterator<'_, Self> where Self: Sized; @@ -122,12 +122,14 @@ pub trait Itemlike { "Page Title" => Class::PageTitle, _ => Class::Texts, }, + ItemKind::Model => Class::Models, + ItemKind::Tree => Class::Trees, } } } impl Itemlike for Item { - fn label(&self) -> Cow<'static, str> { + fn label(&self) -> Cow<'_, str> { match &self.label { Some(label) => Cow::from(label.clone()), None => self.details.label(), @@ -156,8 +158,10 @@ impl Itemlike for Item { self.page_break_before } - fn spv_info(&self) -> Option<&SpvInfo> { - self.spv_info.as_deref() + fn spv_info(&self) -> Option> { + self.spv_info + .as_ref() + .map(|spv_info| Cow::Borrowed(&**spv_info)) } fn iter_in_order(&self) -> ItemRefIterator<'_, Item> { @@ -296,6 +300,8 @@ pub enum ItemKind { Message(Severity), Table, Text, + Model, + Tree, } impl ItemKind { @@ -307,6 +313,8 @@ impl ItemKind { ItemKind::Message(_) => "message", ItemKind::Table => "table", ItemKind::Text => "text", + ItemKind::Model => "model", + ItemKind::Tree => "tree", } } } @@ -681,7 +689,7 @@ where // Returns the label for the heading with the given `level` in the stack // above the current item. Level 0 is the top level. Levels without a // label are skipped. - pub fn heading(&self, level: usize) -> Option> { + pub fn heading(&self, level: usize) -> Option> { self.stack .iter() .map(|(item, _index)| item.as_ref().label()) diff --git a/rust/pspp/src/spv/read/structure.rs b/rust/pspp/src/spv/read/structure.rs index 14d9ab6c44..231c1e906f 100644 --- a/rust/pspp/src/spv/read/structure.rs +++ b/rust/pspp/src/spv/read/structure.rs @@ -1,6 +1,8 @@ use std::{ + borrow::Cow, io::{BufRead, BufReader, Cursor, Read, Seek}, mem::take, + sync::Arc, }; use anyhow::Context; @@ -10,7 +12,7 @@ use zip::ZipArchive; use crate::{ output::{ - Details, Item, SpvInfo, SpvMembers, Text, + Details, Item, ItemKind, ItemRefIterator, Itemlike, SpvInfo, SpvMembers, Text, page::PageSetup, pivot::{ Length, @@ -68,7 +70,7 @@ pub struct OutlineHeading { structure_member: String, expand: bool, label: String, - children: Vec, + children: Vec>, command_name: Option, } @@ -92,7 +94,7 @@ impl OutlineItem { { match self { OutlineItem::Container(container) => { - let mut spv_info = container.spv_info(); + let mut spv_info = container.spv_info().clone(); let result = match container.content { Content::Table(table) => table.decode(archive, &mut *warn), Content::Graph(_) => Err(Error::GraphTodo), @@ -120,7 +122,7 @@ impl OutlineItem { heading .children .into_iter() - .map(|child| child.read_item(archive, &mut *warn)) + .map(|child| Arc::unwrap_or_clone(child).read_item(archive, &mut *warn)) .collect::() .with_show(expand) .with_label(label) @@ -131,6 +133,79 @@ impl OutlineItem { } } +impl Itemlike for OutlineItem { + fn label(&self) -> Cow<'_, str> { + match self { + OutlineItem::Heading(outline_heading) => Cow::from(outline_heading.label.as_str()), + OutlineItem::Container(container) => Cow::from(container.label.as_str()), + } + } + + fn command_name(&self) -> Option<&str> { + match self { + OutlineItem::Heading(outline_heading) => outline_heading.command_name.as_deref(), + OutlineItem::Container(container) => Some(&container.command_name), + } + } + + fn subtype(&self) -> Option { + match self { + OutlineItem::Heading(_) => None, + OutlineItem::Container(container) => container.content.subtype().map(|s| s.into()), + } + } + + fn is_shown(&self) -> bool { + match self { + OutlineItem::Heading(_) => true, + OutlineItem::Container(container) => container.show, + } + } + + fn page_break_before(&self) -> bool { + match self { + OutlineItem::Heading(_) => false, + OutlineItem::Container(container) => container.page_break_before, + } + } + + fn spv_info(&self) -> Option> { + let spv_info = match self { + OutlineItem::Heading(outline_heading) => outline_heading.spv_info(), + OutlineItem::Container(container) => container.spv_info(), + }; + Some(Cow::Owned(spv_info)) + } + + fn iter_in_order(&self) -> ItemRefIterator<'_, Self> + where + Self: Sized, + { + ItemRefIterator::new(self) + } + + fn children(&self) -> &[Arc] { + match self { + OutlineItem::Heading(outline_heading) => &outline_heading.children, + OutlineItem::Container(_) => &[], + } + } + + fn children_mut(&mut self) -> Option<&mut Vec>> { + match self { + OutlineItem::Heading(outline_heading) => Some(&mut outline_heading.children), + OutlineItem::Container(_) => None, + } + } + + fn kind(&self) -> ItemKind { + match self { + OutlineItem::Heading(_) => ItemKind::Heading, + OutlineItem::Container(container) => container.content.kind(), + } + } +} + #[derive(Clone, Debug)] pub struct Container { structure_member: String, @@ -160,6 +235,24 @@ pub enum Content { } impl Content { + fn kind(&self) -> ItemKind { + match self { + Content::Text(_) => ItemKind::Text, + Content::Table(_) => ItemKind::Table, + Content::Image(_) => ItemKind::Image, + Content::Graph(_) => ItemKind::Graph, + Content::Tree => ItemKind::Tree, + Content::Model => ItemKind::Model, + } + } + + fn subtype(&self) -> Option<&str> { + match self { + Content::Table(table) => Some(&table.subtype), + _ => None, + } + } + fn members(&self) -> Option { match self { Content::Text(_text) => None, @@ -284,7 +377,7 @@ impl Graph { } mod raw { - use std::mem::take; + use std::{mem::take, sync::Arc}; use paper_sizes::PaperSize; use serde::Deserialize; @@ -400,7 +493,11 @@ mod raw { expand: !heading.visibility.is_some(), label: take(&mut heading.label.text), command_name: heading.command_name.take(), - children: heading.decode(structure_member, warn), + children: heading + .decode(structure_member, warn) + .into_iter() + .map(Arc::new) + .collect(), })); } }