From: Ben Pfaff Date: Sun, 12 Oct 2025 18:10:58 +0000 (-0700) Subject: work on output items X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f6c605817d8525a73038d4beb39e15258c18766;p=pspp work on output items --- diff --git a/rust/pspp/src/output.rs b/rust/pspp/src/output.rs index 16cf4c5cca..5ac0a34b35 100644 --- a/rust/pspp/src/output.rs +++ b/rust/pspp/src/output.rs @@ -60,7 +60,7 @@ pub struct Item { /// output. command_name: Option, - /// For a group item, this is true if the group's subtree should + /// For a heading item, this is true if the heading's subtree should /// be expanded in an outline view, false otherwise. /// /// For other kinds of output items, this is true to show the item's @@ -89,15 +89,15 @@ impl Item { } } - /// Returns a new group item suitable as the root node of an output document. + /// Returns a new heading item suitable as the root node of an output document. /// - /// A root node is a group whose own properties are mostly disregarded. + /// A root node is a heading whose own properties are mostly disregarded. /// Instead of having root nodes, it would make just as much sense to just /// keep around arrays of nodes that would serve as the top level of an /// output document, but we'd need more special cases instead of just using - /// the existing support for group items. + /// the existing support for heading items. pub fn new_root() -> Self { - Self::new(Details::Group(Heading(Vec::new()))).with_label(Some(String::from("Output"))) + Self::new(Details::Heading(Heading(Vec::new()))).with_label(Some(String::from("Output"))) } pub fn label(&self) -> Cow<'static, str> { @@ -139,14 +139,47 @@ where } } -#[derive(Clone, Debug, Serialize)] +impl FromIterator for Item +where + A: Into>, +{ + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + iter.into_iter().collect::
().into_item() + } +} + +impl PivotTable { + pub fn into_item(self) -> Item { + Details::Table(Box::new(self)).into_item() + } +} + +#[derive(Clone, Debug, Default, Serialize)] pub struct Heading(pub Vec>); +impl Heading { + pub fn new() -> Self { + Self::default() + } + + pub fn with(mut self, item: impl Into>) -> Self { + self.0.push(item.into()); + self + } + + pub fn into_item(self) -> Item { + Details::Heading(self).into_item() + } +} + #[derive(Clone, Debug, Serialize)] pub enum Details { Chart, Image, - Group(Heading), + Heading(Heading), Message(Box), PageBreak, Table(Box), @@ -158,27 +191,18 @@ impl Details { Item::new(self) } - pub fn as_group(&self) -> Option<&[Arc]> { - match self { - Self::Group(heading) => Some(heading.0.as_slice()), - _ => None, - } - } - - pub fn as_mut_group(&mut self) -> Option<&mut Vec>> { + pub fn children(&self) -> &[Arc] { match self { - Self::Group(heading) => Some(&mut heading.0), - _ => None, + Self::Heading(children) => children.0.as_slice(), + _ => &[], } } - pub fn children(&self) -> impl Iterator> { + pub fn mut_children(&mut self) -> Option<&mut Vec>> { match self { - Self::Group(children) => Some(children.0.iter()), + Self::Heading(heading) => Some(&mut heading.0), _ => None, } - .into_iter() - .flatten() } pub fn as_message(&self) -> Option<&Diagnostic> { @@ -206,7 +230,7 @@ impl Details { match self { Details::Chart | Details::Image - | Details::Group(_) + | Details::Heading(_) | Details::Message(_) | Details::PageBreak | Details::Text(_) => None, @@ -218,7 +242,7 @@ impl Details { match self { Details::Chart => todo!(), Details::Image => Cow::from("Image"), - Details::Group(_) => Cow::from("Group"), + Details::Heading(_) => Cow::from("Group"), Details::Message(diagnostic) => Cow::from(diagnostic.severity.as_title_str()), Details::PageBreak => Cow::from("Page Break"), Details::Table(pivot_table) => Cow::from(pivot_table.label()), @@ -226,8 +250,8 @@ impl Details { } } - pub fn is_group(&self) -> bool { - matches!(self, Self::Group(_)) + pub fn is_heading(&self) -> bool { + matches!(self, Self::Heading(_)) } pub fn is_message(&self) -> bool { @@ -255,7 +279,7 @@ where where T: IntoIterator, { - Self::Group(Heading( + Self::Heading(Heading( iter.into_iter().map(|value| value.into()).collect(), )) } @@ -311,6 +335,10 @@ impl Text { content: value.into(), } } + + pub fn into_item(self) -> Item { + Details::Text(Box::new(self)).into_item() + } } fn text_item_table_look() -> Arc { @@ -401,16 +429,13 @@ impl ItemCursor { let Some(cur) = self.cur.take() else { return; }; - if let Some(children) = cur.details.as_group() - && let Some(first_child) = children.first() - { + if let Some(first_child) = cur.details.children().first() { self.cur = Some(first_child.clone()); self.stack.push((cur, 1)); } else { while let Some((item, index)) = self.stack.pop() { - let children = item.details.as_group().unwrap(); - if index < children.len() { - self.cur = Some(children[index].clone()); + if let Some(child) = item.details.children().get(index) { + self.cur = Some(child.clone()); self.stack.push((item, index + 1)); return; } @@ -541,7 +566,7 @@ impl Item { match &self.details { Details::Chart => Class::Charts, Details::Image => Class::Other, - Details::Group(_) => Class::OutlineHeaders, + Details::Heading(_) => Class::OutlineHeaders, Details::Message(diagnostic) => match diagnostic.severity { Severity::Note => Class::Notes, Severity::Error | Severity::Warning => Class::Warnings, @@ -735,7 +760,7 @@ impl Criteria { /// indirect) children of `item` that meet the criteria. fn apply(&self, item: Item) -> Item { fn take_children(item: &Item) -> Vec<&Item> { - item.details.children().map(|item| &**item).collect() + item.details.children().iter().map(|item| &**item).collect() } fn flatten_children<'a>( children: Vec<&'a Item>, @@ -770,7 +795,7 @@ impl Criteria { continue; } if let Some(visible) = selection.visible - && !item.details.is_group() + && !item.details.is_heading() && visible != item.show { continue; @@ -851,7 +876,7 @@ impl Criteria { } fn unflatten_item(mut item: Item, include: &mut bit_vec::Iter, out: &mut Vec>) { let include_item = include.next().unwrap(); - if let Some(children) = item.details.as_mut_group() { + if let Some(children) = item.details.mut_children() { if !include_item { unflatten_items(take(children), include, out); return; @@ -880,7 +905,7 @@ impl Criteria { unflatten_item( item, &mut include.iter(), - output.details.as_mut_group().unwrap(), + output.details.mut_children().unwrap(), ); output } diff --git a/rust/pspp/src/output/drivers/csv.rs b/rust/pspp/src/output/drivers/csv.rs index 1a48eeaa30..e9bd5166b4 100644 --- a/rust/pspp/src/output/drivers/csv.rs +++ b/rust/pspp/src/output/drivers/csv.rs @@ -197,7 +197,7 @@ impl Driver for CsvDriver { fn write(&mut self, item: &Arc) { // todo: error handling (should not unwrap) match &item.details { - Details::Chart | Details::Image | Details::Group(_) => (), + Details::Chart | Details::Image | Details::Heading(_) => (), Details::Message(diagnostic) => { self.start_item(); let text = diagnostic.to_string(); diff --git a/rust/pspp/src/output/drivers/html.rs b/rust/pspp/src/output/drivers/html.rs index 2d3492ef0e..f10f192d37 100644 --- a/rust/pspp/src/output/drivers/html.rs +++ b/rust/pspp/src/output/drivers/html.rs @@ -429,7 +429,7 @@ where match &item.details { Details::Chart => todo!(), Details::Image => todo!(), - Details::Group(_) => todo!(), + 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 107fed9c6c..97f3c28568 100644 --- a/rust/pspp/src/output/drivers/spv.rs +++ b/rust/pspp/src/output/drivers/spv.rs @@ -192,7 +192,7 @@ where match &item.details { Details::Chart => todo!(), Details::Image => todo!(), - Details::Group(children) => { + Details::Heading(children) => { let mut attributes = Vec::::new(); if let Some(command_name) = &item.command_name { attributes.push(("commandName", command_name.as_str()).into()); @@ -602,7 +602,7 @@ where self.next_heading_id += 1; self.writer .start_file( - output_viewer_name(heading_id, item.details.as_group().is_some()), + output_viewer_name(heading_id, item.details.is_heading()), SimpleFileOptions::default(), ) .unwrap(); // XXX diff --git a/rust/pspp/src/output/drivers/text.rs b/rust/pspp/src/output/drivers/text.rs index e544a37826..a2c7be650a 100644 --- a/rust/pspp/src/output/drivers/text.rs +++ b/rust/pspp/src/output/drivers/text.rs @@ -384,7 +384,7 @@ impl TextRenderer { match &item.details { Details::Chart => todo!(), Details::Image => todo!(), - Details::Group(children) => { + Details::Heading(children) => { for (index, child) in children.0.iter().enumerate() { if index > 0 { writeln!(writer)?; diff --git a/rust/pspp/src/output/pivot/tests.rs b/rust/pspp/src/output/pivot/tests.rs index e6c2245753..6f1c60b90b 100644 --- a/rust/pspp/src/output/pivot/tests.rs +++ b/rust/pspp/src/output/pivot/tests.rs @@ -19,7 +19,6 @@ use std::{fmt::Display, fs::File, path::Path, sync::Arc}; use enum_map::EnumMap; use crate::output::{ - Details, drivers::{ Driver, cairo::{CairoConfig, CairoDriver}, @@ -174,13 +173,13 @@ pub fn assert_rendering(name: &str, pivot_table: &PivotTable, expected: &str) { format!("{name} actual"), ); - let item = Arc::new(Details::Table(Box::new(pivot_table.clone())).into_item()); + let item = Arc::new(pivot_table.clone().into_item()); if let Some(dir) = std::env::var_os("PSPP_TEST_HTML_DIR") { let writer = File::create(Path::new(&dir).join(name).with_extension("html")).unwrap(); HtmlDriver::for_writer(writer).write(&item); } - let item = Arc::new(Details::Table(Box::new(pivot_table.clone())).into_item()); + let item = Arc::new(pivot_table.clone().into_item()); if let Some(dir) = std::env::var_os("PSPP_TEST_PDF_DIR") { let config = CairoConfig::new(Path::new(&dir).join(name).with_extension("pdf")); CairoDriver::new(&config).unwrap().write(&item); diff --git a/rust/pspp/src/output/spv.rs b/rust/pspp/src/output/spv.rs index 6dc1bdb3a7..3125df475a 100644 --- a/rust/pspp/src/output/spv.rs +++ b/rust/pspp/src/output/spv.rs @@ -90,10 +90,7 @@ impl Item { } } - Ok(( - items.into_iter().collect::
().into_item(), - page_setup, - )) + Ok((items.into_iter().collect(), page_setup)) } fn from_spv_reader(reader: R) -> Result<(Self, Option), Error> @@ -171,7 +168,7 @@ impl Heading { } ContainerContent::Text(container_text) => { items.push( - Details::Text(Box::new(Text::new_log(container_text.decode()))) + Text::new_log(container_text.decode()) .into_item() .with_command_name(container_text.command_name) .with_spv_info(SpvInfo::new(structure_member)), @@ -185,8 +182,7 @@ impl Heading { heading .decode(archive, structure_member)? .into_iter() - .collect::
() - .into_item() + .collect::() .with_show(show) .with_spv_info(SpvInfo::new(structure_member)), ); @@ -284,12 +280,10 @@ impl Table { let table = LightTable::read(&mut Cursor::new(data))?; let pivot_table = table.decode()?; println!("{}", &pivot_table); - Ok(Details::Table(Box::new(pivot_table)) - .into_item() - .with_spv_info( - SpvInfo::new(structure_member) - .with_members(SpvMembers::Light(self.table_structure.data_path.clone())), - )) + Ok(pivot_table.into_item().with_spv_info( + SpvInfo::new(structure_member) + .with_members(SpvMembers::Light(self.table_structure.data_path.clone())), + )) } else { todo!() } diff --git a/rust/pspp/src/pc/tests.rs b/rust/pspp/src/pc/tests.rs index b629bf1799..1a573f448d 100644 --- a/rust/pspp/src/pc/tests.rs +++ b/rust/pspp/src/pc/tests.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use crate::{ data::cases_to_output, output::{ - Details, Item, Text, + Item, Text, pivot::{PivotTable, tests::assert_lines_eq}, }, pc::PcFile, @@ -30,9 +30,9 @@ fn test_pcfile(name: &str) { output.push(PivotTable::from(&metadata).into()); output.extend(dictionary.all_pivot_tables().into_iter().map_into()); output.extend(cases_to_output(&dictionary, cases)); - output.into_iter().collect::
().into_item() + output.into_iter().collect() } - Err(error) => Details::Text(Box::new(Text::new_log(error.to_string()))).into_item(), + Err(error) => Text::new_log(error.to_string()).into_item(), }; let actual = output.to_string(); diff --git a/rust/pspp/src/por/read.rs b/rust/pspp/src/por/read.rs index 65184ca162..1bb4c80994 100644 --- a/rust/pspp/src/por/read.rs +++ b/rust/pspp/src/por/read.rs @@ -1158,7 +1158,7 @@ mod tests { use crate::{ data::cases_to_output, output::{ - Details, Item, Text, + Item, Text, pivot::{PivotTable, tests::assert_lines_eq}, }, por::{PortableFile, ReadPad}, @@ -1196,9 +1196,9 @@ mod tests { output.push(PivotTable::from(&metadata).into()); output.extend(dictionary.all_pivot_tables().into_iter().map_into()); output.extend(cases_to_output(&dictionary, cases)); - output.into_iter().collect::
().into_item() + output.into_iter().collect() } - Err(error) => Details::Text(Box::new(Text::new_log(error.to_string()))).into_item(), + Err(error) => Text::new_log(error.to_string()).into_item(), }; let actual = output.to_string(); diff --git a/rust/pspp/src/show.rs b/rust/pspp/src/show.rs index f8d10d7829..c83cddde78 100644 --- a/rust/pspp/src/show.rs +++ b/rust/pspp/src/show.rs @@ -285,9 +285,9 @@ impl Show { output.push(PivotTable::from(&metadata).into()); output.extend(dictionary.all_pivot_tables().into_iter().map_into()); output.extend(cases_to_output(&dictionary, cases)); - driver.borrow_mut().write(&Arc::new( - output.into_iter().collect::
().into_item(), - )); + driver + .borrow_mut() + .write(&Arc::new(output.into_iter().collect())); } Output::Json { .. } => { output.show_json(&dictionary)?; diff --git a/rust/pspp/src/show_pc.rs b/rust/pspp/src/show_pc.rs index baa5cc730b..19dded7002 100644 --- a/rust/pspp/src/show_pc.rs +++ b/rust/pspp/src/show_pc.rs @@ -20,7 +20,7 @@ use itertools::Itertools; use pspp::{ data::cases_to_output, output::{ - Details, Item, Text, + Item, Text, drivers::{Config, Driver}, pivot::PivotTable, }, @@ -219,9 +219,9 @@ impl ShowPc { let mut output = Vec::new(); output.extend(dictionary.all_pivot_tables().into_iter().map_into()); output.extend(cases_to_output(&dictionary, cases)); - driver.borrow_mut().write(&Arc::new( - output.into_iter().collect::
().into_item(), - )); + driver + .borrow_mut() + .write(&Arc::new(output.into_iter().collect())); } Output::Json { .. } => { output.show_json(&dictionary)?; diff --git a/rust/pspp/src/show_por.rs b/rust/pspp/src/show_por.rs index 9b474b639a..bac7c42884 100644 --- a/rust/pspp/src/show_por.rs +++ b/rust/pspp/src/show_por.rs @@ -20,7 +20,7 @@ use itertools::Itertools; use pspp::{ data::cases_to_output, output::{ - Details, Item, Text, + Item, Text, drivers::{Config, Driver}, pivot::PivotTable, }, @@ -219,9 +219,9 @@ impl ShowPor { let mut output = Vec::new(); output.extend(dictionary.all_pivot_tables().into_iter().map_into()); output.extend(cases_to_output(&dictionary, cases)); - driver.borrow_mut().write(&Arc::new( - output.into_iter().collect::
().into_item(), - )); + driver + .borrow_mut() + .write(&Arc::new(output.into_iter().collect())); } Output::Json { .. } => { output.show_json(&dictionary)?; diff --git a/rust/pspp/src/sys/tests.rs b/rust/pspp/src/sys/tests.rs index 292dfe73db..d394c0a9f1 100644 --- a/rust/pspp/src/sys/tests.rs +++ b/rust/pspp/src/sys/tests.rs @@ -30,7 +30,7 @@ use crate::{ dictionary::Dictionary, identifier::Identifier, output::{ - Details, Item, Text, + Item, Text, pivot::{Axis3, Dimension, Group, PivotTable, Value, tests::assert_lines_eq}, }, sys::{ @@ -808,9 +808,9 @@ where } output.push(pt.into()); } - output.into_iter().collect::
().into_item() + output.into_iter().collect() } - Err(error) => Details::Text(Box::new(Text::new_log(error.to_string()))).into_item(), + Err(error) => Text::new_log(error.to_string()).into_item(), }; let actual = output.to_string();