From 6e3d470eb5fabfb72c7576de6ca4e3d6d580158d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 12 Apr 2025 08:15:21 -0700 Subject: [PATCH] work on getting rid of builders --- rust/pspp/src/output/pivot/mod.rs | 81 ++++++++++- rust/pspp/src/output/pivot/test.rs | 225 +++++++++++++++-------------- 2 files changed, 197 insertions(+), 109 deletions(-) diff --git a/rust/pspp/src/output/pivot/mod.rs b/rust/pspp/src/output/pivot/mod.rs index 0bba40dac7..0d8b006e30 100644 --- a/rust/pspp/src/output/pivot/mod.rs +++ b/rust/pspp/src/output/pivot/mod.rs @@ -272,7 +272,7 @@ impl Iterator for AxisIterator { impl PivotTable { fn builder(title: impl Into, dimensions: Vec) -> PivotTableBuilder { - PivotTableBuilder::new(title, dimensions) + PivotTableBuilder::from_builders(title, dimensions) } fn axis_values(&self, axis: Axis3) -> AxisIterator { AxisIterator { @@ -325,6 +325,14 @@ pub struct Path<'a> { } impl Dimension { + pub fn new(root: Group) -> Self { + Dimension { + presentation_order: (0..root.len()).collect(), + root, + hide_all_labels: false, + } + } + pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -362,6 +370,38 @@ pub struct Group { } impl Group { + pub fn new(name: impl Into) -> Group { + Self { + len: 0, + name: Box::new(name.into()), + children: Vec::new(), + show_label: false, + } + } + + pub fn push(&mut self, child: impl Into) { + let mut child = child.into(); + if let Category::Group(group) = &mut child { + group.show_label = true; + } + self.len += child.len(); + self.children.push(child); + } + + pub fn with(mut self, child: impl Into) -> Self { + self.push(child); + self + } + + pub fn with_label_shown(self) -> Self { + self.with_show_label(true) + } + + pub fn with_show_label(mut self, show_label: bool) -> Self { + self.show_label = show_label; + self + } + pub fn builder(name: impl Into) -> GroupBuilder { GroupBuilder::new(name) } @@ -565,7 +605,26 @@ pub struct PivotTableBuilder { } impl PivotTableBuilder { - pub fn new(title: impl Into, dimension_builders: Vec) -> Self { + pub fn new(title: impl Into, dimensions_and_axes: Vec<(Axis3, Dimension)>) -> Self { + let mut dimensions = Vec::new(); + let mut axes = EnumMap::::default(); + for (axis, dimension) in dimensions_and_axes { + axes[axis].dimensions.push(dimensions.len()); + dimensions.push(dimension); + } + Self { + look: Settings::global().look.clone(), + title: Box::new(title.into()), + axes, + dimensions, + cells: HashMap::new(), + footnotes: Footnotes::new(), + } + } + pub fn from_builders( + title: impl Into, + dimension_builders: Vec, + ) -> Self { let mut dimensions = Vec::with_capacity(dimension_builders.len()); let mut axes = EnumMap::from_fn(|_axis| Axis { dimensions: Vec::with_capacity(dimension_builders.len()), @@ -719,6 +778,24 @@ impl Category { } } +impl From for Category { + fn from(group: Group) -> Self { + Self::Group(group) + } +} + +impl From for Category { + fn from(name: Value) -> Self { + Self::Leaf(Leaf::new(name)) + } +} + +impl From<&str> for Category { + fn from(name: &str) -> Self { + Self::Leaf(Leaf::new(Value::new_text(name))) + } +} + trait CategoryTrait { fn name(&self) -> &Value; } diff --git a/rust/pspp/src/output/pivot/test.rs b/rust/pspp/src/output/pivot/test.rs index 3aecd0481a..46eb9e63fc 100644 --- a/rust/pspp/src/output/pivot/test.rs +++ b/rust/pspp/src/output/pivot/test.rs @@ -23,12 +23,14 @@ fn color() { } fn d1(title: &str, axis: Axis3) -> PivotTable { - let mut group = GroupBuilder::new(Value::new_text("a")).with_label_shown(); - for name in ["a1", "a2", "a3"] { - group.push(Value::new_text(name)); - } - let dimension = DimensionBuilder::new(axis, group); - let mut pt = PivotTableBuilder::new(Value::new_text(title), vec![dimension]); + let dimension = Dimension::new( + Group::new("a") + .with_label_shown() + .with("a1") + .with("a2") + .with("a3"), + ); + let mut pt = PivotTableBuilder::new(Value::new_text(title), vec![(axis, dimension)]); for i in 0..3 { pt.insert(&[i], Value::new_integer(Some(i as f64))); } @@ -77,19 +79,23 @@ fn test_look() -> Look { } fn d2(title: &str, axes: [Axis3; 2], dimension_labels: Option) -> PivotTable { - let mut a = GroupBuilder::new(Value::new_text("a")).with_show_label(dimension_labels.is_some()); - for name in ["a1", "a2", "a3"] { - a.push(Value::new_text(name)); - } - let d1 = DimensionBuilder::new(axes[0], a); + let d1 = Dimension::new( + Group::new("a") + .with_show_label(dimension_labels.is_some()) + .with("a1") + .with("a2") + .with("a3"), + ); - let mut b = GroupBuilder::new(Value::new_text("b")).with_show_label(dimension_labels.is_some()); - for name in ["b1", "b2", "b3"] { - b.push(Value::new_text(name)); - } - let d2 = DimensionBuilder::new(axes[1], b); + let d2 = Dimension::new( + Group::new("b") + .with_show_label(dimension_labels.is_some()) + .with("b1") + .with("b2") + .with("b3"), + ); - let mut pt = PivotTableBuilder::new(Value::new_text(title), vec![d1, d2]); + let mut pt = PivotTableBuilder::new(Value::new_text(title), vec![(axes[0], d1), (axes[1], d2)]); let mut i = 0; for b in 0..3 { for a in 0..3 { @@ -547,22 +553,26 @@ fn footnotes() { let mut footnotes = Footnotes::new(); let f0 = footnotes.push(Footnote::new("First footnote").with_marker("*")); let f1 = footnotes.push(Footnote::new("Second footnote")); - let a = Dimension::builder( + let a = ( Axis3::X, - Group::builder(Value::new_text("A").with_footnote(&f0)) - .with_label_shown() - .with(Value::new_text("B").with_footnote(&f1)) - .with(Value::new_text("C").with_footnote(&f0).with_footnote(&f1)), + Dimension::new( + Group::new(Value::new_text("A").with_footnote(&f0)) + .with_label_shown() + .with(Value::new_text("B").with_footnote(&f1)) + .with(Value::new_text("C").with_footnote(&f0).with_footnote(&f1)), + ), ); - let d = Dimension::builder( + let d = ( Axis3::Y, - Group::builder(Value::new_text("D").with_footnote(&f1)) - .with_label_shown() - .with(Value::new_text("E").with_footnote(&f0)) - .with(Value::new_text("F").with_footnote(&f1).with_footnote(&f0)), + Dimension::new( + Group::new(Value::new_text("D").with_footnote(&f1)) + .with_label_shown() + .with(Value::new_text("E").with_footnote(&f0)) + .with(Value::new_text("F").with_footnote(&f1).with_footnote(&f0)), + ), ); let look = test_look().with_row_label_position(LabelPosition::Nested); - let mut pt = PivotTable::builder( + let mut pt = PivotTableBuilder::new( Value::new_text("Pivot Table with Alphabetic Subscript Footnotes").with_footnote(&f0), vec![a, d], ); @@ -606,7 +616,7 @@ b. Second footnote #[test] fn no_dimension() { - let pivot_table = PivotTableBuilder::new(Value::new_text("No Dimensions"), vec![]) + let pivot_table = PivotTableBuilder::new("No Dimensions", vec![]) .with_look(Arc::new(test_look())) .build(); assert_rendering( @@ -622,57 +632,44 @@ fn no_dimension() { fn empty_dimensions() { let look = Arc::new(test_look().with_omit_empty(false)); - let d1 = DimensionBuilder::new(Axis3::X, GroupBuilder::new(Value::new_text("a"))); - let pivot_table = PivotTableBuilder::new(Value::new_text("One Empty Dimension"), vec![d1]) + let d1 = (Axis3::X, Dimension::new(Group::new("a"))); + let pivot_table = PivotTableBuilder::new("One Empty Dimension", vec![d1]) .with_look(look.clone()) .build(); assert_rendering(&pivot_table, "One Empty Dimension\n"); - let d1 = DimensionBuilder::new(Axis3::X, GroupBuilder::new(Value::new_text("a"))); - let d2 = DimensionBuilder::new( - Axis3::X, - GroupBuilder::new(Value::new_text("b")).with_label_shown(), - ); + let d1 = (Axis3::X, Dimension::new(Group::new("a"))); + let d2 = (Axis3::X, Dimension::new(Group::new("b").with_label_shown())); let pivot_table = PivotTableBuilder::new(Value::new_text("Two Empty Dimensions"), vec![d1, d2]) .with_look(look.clone()) .build(); assert_rendering(&pivot_table, "Two Empty Dimensions\n"); - let d1 = DimensionBuilder::new(Axis3::X, GroupBuilder::new(Value::new_text("a"))); - let d2 = DimensionBuilder::new( + let d1 = (Axis3::X, Dimension::new(Group::new("a"))); + let d2 = (Axis3::X, Dimension::new(Group::new("b").with_label_shown())); + let d3 = ( Axis3::X, - GroupBuilder::new(Value::new_text("b")).with_label_shown(), + Dimension::new(Group::new("c").with("c1").with("c2")), ); - let d3 = DimensionBuilder::new( - Axis3::X, - GroupBuilder::new(Value::new_text("c")) - .with(Value::new_text("c1")) - .with(Value::new_text("c2")), - ); - let pivot_table = PivotTableBuilder::new( - Value::new_text("Three Dimensions, Two Empty"), - vec![d1, d2, d3], - ) - .with_look(look.clone()) - .build(); + let pivot_table = PivotTableBuilder::new("Three Dimensions, Two Empty", vec![d1, d2, d3]) + .with_look(look.clone()) + .build(); assert_rendering(&pivot_table, "Three Dimensions, Two Empty\n"); } #[test] fn empty_groups() { - let a = GroupBuilder::new(Value::new_text("a")) - .with(Value::new_text("a1")) - .with(GroupBuilder::new(Value::new_text("a2"))) - .with(Value::new_text("a3")); - let d1 = DimensionBuilder::new(Axis3::X, a); + let d1 = ( + Axis3::X, + Dimension::new(Group::new("a").with("a1").with(Group::new("a2")).with("a3")), + ); - let b = GroupBuilder::new(Value::new_text("b")) - .with(GroupBuilder::new(Value::new_text("b1"))) - .with(Value::new_text("b2")) - .with(Value::new_text("b3")); - let d2 = DimensionBuilder::new(Axis3::Y, b); + let d2 = ( + Axis3::Y, + Dimension::new(Group::new("b").with(Group::new("b1")).with("b2").with("b3")), + ); - let mut pt = PivotTableBuilder::new(Value::new_text("Empty Groups"), vec![d1, d2]); + let mut pt = PivotTableBuilder::new("Empty Groups", vec![d1, d2]); let mut i = 0; for b in 0..2 { for a in 0..2 { @@ -702,35 +699,43 @@ fn d4( borders: EnumMap, show_dimension_labels: bool, ) -> PivotTable { - let a = Dimension::builder( + let a = ( Axis3::X, - Group::builder("a") - .with_show_label(show_dimension_labels) - .with("a1") - .with(Group::builder("ag1").with("a2").with("a3")), + Dimension::new( + Group::new("a") + .with_show_label(show_dimension_labels) + .with("a1") + .with(Group::new("ag1").with("a2").with("a3")), + ), ); - let b = Dimension::builder( + let b = ( Axis3::X, - Group::builder("b") - .with_show_label(show_dimension_labels) - .with(Group::builder("bg1").with("b1").with("b2")) - .with("b3"), + Dimension::new( + Group::new("b") + .with_show_label(show_dimension_labels) + .with(Group::new("bg1").with("b1").with("b2")) + .with("b3"), + ), ); - let c = Dimension::builder( + let c = ( Axis3::Y, - Group::builder("c") - .with_show_label(show_dimension_labels) - .with("c1") - .with(Group::builder("cg1").with("c2").with("c3")), + Dimension::new( + Group::new("c") + .with_show_label(show_dimension_labels) + .with("c1") + .with(Group::new("cg1").with("c2").with("c3")), + ), ); - let d = Dimension::builder( + let d = ( Axis3::Y, - Group::builder("d") - .with_show_label(show_dimension_labels) - .with(Group::builder("dg1").with("d1").with("d2")) - .with("d3"), + Dimension::new( + Group::new("d") + .with_show_label(show_dimension_labels) + .with(Group::new("dg1").with("d1").with("d2")) + .with("d3"), + ), ); - let mut pivot_table = PivotTable::builder(title, vec![a, b, c, d]) + let mut pivot_table = PivotTableBuilder::new(title, vec![a, b, c, d]) .with_look(Arc::new(test_look().with_borders(borders))); let mut i = 0; for d in 0..3 { @@ -1041,36 +1046,42 @@ dg1┊d1│ c1 0│ 1┊ 2│ 3│ 4┊ 5│ 6│ 7┊ 8 #[test] fn small_numbers() { - let exponent = Dimension::builder( + let exponent = ( Axis3::Y, - Group::builder("exponent") - .with("0") - .with("-1") - .with("-2") - .with("-3") - .with("-4") - .with("-5") - .with("-6") - .with("-7") - .with("-8") - .with("-9") - .with_label_shown(), + Dimension::new( + Group::new("exponent") + .with("0") + .with("-1") + .with("-2") + .with("-3") + .with("-4") + .with("-5") + .with("-6") + .with("-7") + .with("-8") + .with("-9") + .with_label_shown(), + ), ); - let sign = Dimension::builder( + let sign = ( Axis3::X, - Group::builder("sign") - .with("positive") - .with("negative") - .with_label_shown(), + Dimension::new( + Group::new("sign") + .with("positive") + .with("negative") + .with_label_shown(), + ), ); - let rc = Dimension::builder( + let rc = ( Axis3::X, - Group::builder("result class") - .with((Value::new_text("general"), Class::Other)) - .with((Value::new_text("specific"), Class::Residual)) - .with_label_shown(), + Dimension::new( + Group::new("result class") + .with("general") + .with("specific") + .with_label_shown(), + ), ); - let mut pt = PivotTable::builder("small numbers", vec![exponent, sign, rc]); + let mut pt = PivotTableBuilder::new("small numbers", vec![exponent, sign, rc]); pt.insert_number(&[0, 0, 0], Some(1.0), Class::Other); pt.insert_number(&[1, 0, 0], Some(0.1), Class::Other); pt.insert_number(&[2, 0, 0], Some(0.01), Class::Other); -- 2.30.2