work on getting rid of builders
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 12 Apr 2025 15:15:21 +0000 (08:15 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 12 Apr 2025 15:15:21 +0000 (08:15 -0700)
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/output/pivot/test.rs

index 0bba40dac7b295d39e24d854057eda7a0d34498c..0d8b006e307946462cd8eee7776ba95c3a4955e7 100644 (file)
@@ -272,7 +272,7 @@ impl Iterator for AxisIterator {
 
 impl PivotTable {
     fn builder(title: impl Into<Value>, dimensions: Vec<DimensionBuilder>) -> 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<Value>) -> Group {
+        Self {
+            len: 0,
+            name: Box::new(name.into()),
+            children: Vec::new(),
+            show_label: false,
+        }
+    }
+
+    pub fn push(&mut self, child: impl Into<Category>) {
+        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<Category>) -> 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<Value>) -> GroupBuilder {
         GroupBuilder::new(name)
     }
@@ -565,7 +605,26 @@ pub struct PivotTableBuilder {
 }
 
 impl PivotTableBuilder {
-    pub fn new(title: impl Into<Value>, dimension_builders: Vec<DimensionBuilder>) -> Self {
+    pub fn new(title: impl Into<Value>, dimensions_and_axes: Vec<(Axis3, Dimension)>) -> Self {
+        let mut dimensions = Vec::new();
+        let mut axes = EnumMap::<Axis3, Axis>::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<Value>,
+        dimension_builders: Vec<DimensionBuilder>,
+    ) -> 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<Group> for Category {
+    fn from(group: Group) -> Self {
+        Self::Group(group)
+    }
+}
+
+impl From<Value> 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;
 }
index 3aecd0481a5bb63acf746486d73df5fae391a377..46eb9e63fca0a49beb83a069fd092f69dce7ccf0 100644 (file)
@@ -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<LabelPosition>) -> 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<Border, BorderStyle>,
     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);