/// output.
command_name: Option<String>,
- /// 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
}
}
- /// 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> {
}
}
-#[derive(Clone, Debug, Serialize)]
+impl<A> FromIterator<A> for Item
+where
+ A: Into<Arc<Item>>,
+{
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = A>,
+ {
+ iter.into_iter().collect::<Details>().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<Arc<Item>>);
+impl Heading {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn with(mut self, item: impl Into<Arc<Item>>) -> 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<Diagnostic>),
PageBreak,
Table(Box<PivotTable>),
Item::new(self)
}
- pub fn as_group(&self) -> Option<&[Arc<Item>]> {
- match self {
- Self::Group(heading) => Some(heading.0.as_slice()),
- _ => None,
- }
- }
-
- pub fn as_mut_group(&mut self) -> Option<&mut Vec<Arc<Item>>> {
+ pub fn children(&self) -> &[Arc<Item>] {
match self {
- Self::Group(heading) => Some(&mut heading.0),
- _ => None,
+ Self::Heading(children) => children.0.as_slice(),
+ _ => &[],
}
}
- pub fn children(&self) -> impl Iterator<Item = &Arc<Item>> {
+ pub fn mut_children(&mut self) -> Option<&mut Vec<Arc<Item>>> {
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> {
match self {
Details::Chart
| Details::Image
- | Details::Group(_)
+ | Details::Heading(_)
| Details::Message(_)
| Details::PageBreak
| Details::Text(_) => None,
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()),
}
}
- 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 {
where
T: IntoIterator<Item = A>,
{
- Self::Group(Heading(
+ Self::Heading(Heading(
iter.into_iter().map(|value| value.into()).collect(),
))
}
content: value.into(),
}
}
+
+ pub fn into_item(self) -> Item {
+ Details::Text(Box::new(self)).into_item()
+ }
}
fn text_item_table_look() -> Arc<Look> {
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;
}
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,
/// 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>,
continue;
}
if let Some(visible) = selection.visible
- && !item.details.is_group()
+ && !item.details.is_heading()
&& visible != item.show
{
continue;
}
fn unflatten_item(mut item: Item, include: &mut bit_vec::Iter, out: &mut Vec<Arc<Item>>) {
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;
unflatten_item(
item,
&mut include.iter(),
- output.details.as_mut_group().unwrap(),
+ output.details.mut_children().unwrap(),
);
output
}
fn write(&mut self, item: &Arc<Item>) {
// 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();
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) => {
match &item.details {
Details::Chart => todo!(),
Details::Image => todo!(),
- Details::Group(children) => {
+ Details::Heading(children) => {
let mut attributes = Vec::<Attribute>::new();
if let Some(command_name) = &item.command_name {
attributes.push(("commandName", command_name.as_str()).into());
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
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)?;
use enum_map::EnumMap;
use crate::output::{
- Details,
drivers::{
Driver,
cairo::{CairoConfig, CairoDriver},
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);
}
}
- Ok((
- items.into_iter().collect::<Details>().into_item(),
- page_setup,
- ))
+ Ok((items.into_iter().collect(), page_setup))
}
fn from_spv_reader<R>(reader: R) -> Result<(Self, Option<PageSetup>), Error>
}
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)),
heading
.decode(archive, structure_member)?
.into_iter()
- .collect::<Details>()
- .into_item()
+ .collect::<Item>()
.with_show(show)
.with_spv_info(SpvInfo::new(structure_member)),
);
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!()
}
use crate::{
data::cases_to_output,
output::{
- Details, Item, Text,
+ Item, Text,
pivot::{PivotTable, tests::assert_lines_eq},
},
pc::PcFile,
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::<Details>().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();
use crate::{
data::cases_to_output,
output::{
- Details, Item, Text,
+ Item, Text,
pivot::{PivotTable, tests::assert_lines_eq},
},
por::{PortableFile, ReadPad},
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::<Details>().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();
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::<Details>().into_item(),
- ));
+ driver
+ .borrow_mut()
+ .write(&Arc::new(output.into_iter().collect()));
}
Output::Json { .. } => {
output.show_json(&dictionary)?;
use pspp::{
data::cases_to_output,
output::{
- Details, Item, Text,
+ Item, Text,
drivers::{Config, Driver},
pivot::PivotTable,
},
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::<Details>().into_item(),
- ));
+ driver
+ .borrow_mut()
+ .write(&Arc::new(output.into_iter().collect()));
}
Output::Json { .. } => {
output.show_json(&dictionary)?;
use pspp::{
data::cases_to_output,
output::{
- Details, Item, Text,
+ Item, Text,
drivers::{Config, Driver},
pivot::PivotTable,
},
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::<Details>().into_item(),
- ));
+ driver
+ .borrow_mut()
+ .write(&Arc::new(output.into_iter().collect()));
}
Output::Json { .. } => {
output.show_json(&dictionary)?;
dictionary::Dictionary,
identifier::Identifier,
output::{
- Details, Item, Text,
+ Item, Text,
pivot::{Axis3, Dimension, Group, PivotTable, Value, tests::assert_lines_eq},
},
sys::{
}
output.push(pt.into());
}
- output.into_iter().collect::<Details>().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();