tests pass
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 11 Apr 2025 14:21:58 +0000 (07:21 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 11 Apr 2025 14:21:58 +0000 (07:21 -0700)
rust/pspp/src/output/pivot/output.rs
rust/pspp/src/output/pivot/test.rs
rust/pspp/src/output/text.rs

index 05febd7cfc51587a5253edb1a793302c9f94818f..a7c13c98cc130a5d2dd25e91f9138a33927ce636 100644 (file)
@@ -421,8 +421,7 @@ impl<'a> Heading<'a> {
                     }
                 })
                 .collect::<Vec<_>>();
-
-            for (Range { start: x1, end: x2 }, name) in categories {
+            for (Range { start: x1, end: x2 }, name) in categories.iter().cloned() {
                 let y1 = v_ofs + row;
                 let y2 = y1 + 1;
                 table.put(
@@ -486,12 +485,20 @@ impl<'a> Heading<'a> {
                 // |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
                 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+
                 // ```
-                if row > 0 {
-                    table.draw_line(
-                        Border::Category(RowColBorder(region, h)),
-                        (h, y1),
-                        h_ofs..table.n[h],
-                    );
+                if row + 1 < self.height {
+                    table.draw_line(Border::Category(RowColBorder(region, h)), (h, y2), {
+                        if row == 0
+                            && !self.columns[0].groups.is_empty()
+                            && std::ptr::eq(
+                                &*self.columns[0].groups[0].name,
+                                &*self.dimension.root.name,
+                            )
+                        {
+                            h_ofs..table.n[h]
+                        } else {
+                            h_ofs + x1..h_ofs + x2
+                        }
+                    });
                 }
             }
         }
index 3e66720ee1d359c684ac1f5fa9a98d7d1a0cde24..c0e851ba405dd9a77dc1e0129db33bf403934316 100644 (file)
@@ -635,8 +635,7 @@ Empty Groups
     );
 }
 
-#[test]
-fn borders() {
+fn d4(title: &str, borders: EnumMap<Border, BorderStyle>) -> PivotTable {
     let a = Dimension::builder(
         Axis3::X,
         Group::builder("a")
@@ -665,15 +664,7 @@ fn borders() {
             .with(Group::builder("dg1").with("d1").with("d2"))
             .with("d3"),
     );
-    let borders = EnumMap::from_fn(|border| match border {
-        Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
-        | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
-            stroke: Stroke::Solid,
-            color: Color::BLUE,
-        },
-        _ => BorderStyle::none(),
-    });
-    let mut pivot_table = PivotTable::builder("Dimension Borders 1", &[a, b, c, d])
+    let mut pivot_table = PivotTable::builder(title, &[a, b, c, d])
         .with_look(Arc::new(test_look().with_borders(borders)));
     let mut i = 0;
     for d in 0..3 {
@@ -686,7 +677,22 @@ fn borders() {
             }
         }
     }
-    let pivot_table = pivot_table.build();
+    pivot_table.build()
+}
+
+#[test]
+fn dimension_borders_1() {
+    let pivot_table = d4(
+        "Dimension Borders 1",
+        EnumMap::from_fn(|border| match border {
+            Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
+            | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
+                stroke: Stroke::Solid,
+                color: Color::BLUE,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
     assert_rendering(
         &pivot_table,
         "\
@@ -713,16 +719,58 @@ dg1 d1     c1  0│ 1  2│ 3│ 4  5│ 6│ 7  8
            c3 72│73 74│75│76 77│78│79 80
 ",
     );
+}
 
-    let mut pivot_table = pivot_table.with_title("Category Borders 1");
-    pivot_table.look_mut().borders = EnumMap::from_fn(|border| match border {
-        Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
-        | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
-            stroke: Stroke::Dashed,
-            color: Color::RED,
-        },
-        _ => BorderStyle::none(),
-    });
+#[test]
+fn dimension_borders_2() {
+    let pivot_table = d4(
+        "Dimension Borders 2",
+        EnumMap::from_fn(|border| match border {
+            Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::Y))
+            | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::X)) => BorderStyle {
+                stroke: Stroke::Solid,
+                color: Color::BLUE,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
+    assert_rendering(
+        &pivot_table,
+        "\
+Dimension Borders 2
+                           b
+                     bg1
+                 b1       b2       b3
+             ╶──────────────────────────
+                  a        a        a
+                  ag1      ag1      ag1
+d      c      a1 a2 a3 a1 a2 a3 a1 a2 a3
+dg1 d1│    c1  0  1  2  3  4  5  6  7  8
+      │cg1 c2  9 10 11 12 13 14 15 16 17
+      │    c3 18 19 20 21 22 23 24 25 26
+    d2│    c1 27 28 29 30 31 32 33 34 35
+      │cg1 c2 36 37 38 39 40 41 42 43 44
+      │    c3 45 46 47 48 49 50 51 52 53
+    d3│    c1 54 55 56 57 58 59 60 61 62
+      │cg1 c2 63 64 65 66 67 68 69 70 71
+      │    c3 72 73 74 75 76 77 78 79 80
+",
+    );
+}
+
+#[test]
+fn category_borders_1() {
+    let pivot_table = d4(
+        "Category Borders 1",
+        EnumMap::from_fn(|border| match border {
+            Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
+            | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
+                stroke: Stroke::Dashed,
+                color: Color::RED,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
     assert_rendering(
         &pivot_table,
         "\
@@ -734,39 +782,85 @@ Category Borders 1
                 ┊ ag1 ┊  ┊ ag1 ┊  ┊ ag1
 d      c      a1┊a2┊a3┊a1┊a2┊a3┊a1┊a2┊a3
 dg1 d1     c1  0┊ 1┊ 2┊ 3┊ 4┊ 5┊ 6┊ 7┊ 8
-      â\95╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+      â\95\8c╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
        cg1 c2  9┊10┊11┊12┊13┊14┊15┊16┊17
-          â\95╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+          â\95\8c╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
            c3 18┊19┊20┊21┊22┊23┊24┊25┊26
-   â\95╌╌╌╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+   â\95\8c╌╌╌╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
     d2     c1 27┊28┊29┊30┊31┊32┊33┊34┊35
-      â\95╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+      â\95\8c╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
        cg1 c2 36┊37┊38┊39┊40┊41┊42┊43┊44
-          â\95╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+          â\95\8c╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
            c3 45┊46┊47┊48┊49┊50┊51┊52┊53
 ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
     d3     c1 54┊55┊56┊57┊58┊59┊60┊61┊62
-      â\95╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+      â\95\8c╌╌╌╌╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
        cg1 c2 63┊64┊65┊66┊67┊68┊69┊70┊71
-          â\95╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
+          â\95\8c╌╌╌╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌+╌╌
            c3 72┊73┊74┊75┊76┊77┊78┊79┊80
 ",
     );
+}
 
-    let mut pivot_table = pivot_table.with_title("Category and Dimension Borders 1");
-    pivot_table.look_mut().borders = EnumMap::from_fn(|border| match border {
-        Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
-        | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
-            stroke: Stroke::Solid,
-            color: Color::BLUE,
-        },
-        Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
-        | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
-            stroke: Stroke::Dashed,
-            color: Color::RED,
-        },
-        _ => BorderStyle::none(),
-    });
+#[test]
+fn category_borders_2() {
+    let pivot_table = d4(
+        "Category Borders 2",
+        EnumMap::from_fn(|border| match border {
+            Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::Y))
+            | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::X)) => BorderStyle {
+                stroke: Stroke::Dashed,
+                color: Color::RED,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
+    assert_rendering(
+        &pivot_table,
+        "\
+Category Borders 2
+                           b
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                     bg1
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                 b1       b2       b3
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                  a        a        a
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                  ag1      ag1      ag1
+                ╌╌╌╌╌╌╌  ╌╌╌╌╌╌╌  ╌╌╌╌╌╌
+d      c      a1 a2 a3 a1 a2 a3 a1 a2 a3
+dg1┊d1┊    c1  0  1  2  3  4  5  6  7  8
+   ┊  ┊cg1┊c2  9 10 11 12 13 14 15 16 17
+   ┊  ┊   ┊c3 18 19 20 21 22 23 24 25 26
+   ┊d2┊    c1 27 28 29 30 31 32 33 34 35
+   ┊  ┊cg1┊c2 36 37 38 39 40 41 42 43 44
+   ┊  ┊   ┊c3 45 46 47 48 49 50 51 52 53
+    d3┊    c1 54 55 56 57 58 59 60 61 62
+      ┊cg1┊c2 63 64 65 66 67 68 69 70 71
+      ┊   ┊c3 72 73 74 75 76 77 78 79 80
+",
+    );
+}
+
+#[test]
+fn category_and_dimension_borders_1() {
+    let pivot_table = d4(
+        "Category and Dimension Borders 1",
+        EnumMap::from_fn(|border| match border {
+            Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::X))
+            | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
+                stroke: Stroke::Solid,
+                color: Color::BLUE,
+            },
+            Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::X))
+            | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::Y)) => BorderStyle {
+                stroke: Stroke::Dashed,
+                color: Color::RED,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
     assert_rendering(
         &pivot_table,
         "\
@@ -780,20 +874,66 @@ d      c      a1│a2┊a3│a1│a2┊a3│a1│a2┊a3
 dg1 d1     c1  0│ 1┊ 2│ 3│ 4┊ 5│ 6│ 7┊ 8
       ╶─────────┼──┼──┼──┼──┼──┼──┼──┼──
        cg1 c2  9│10┊11│12│13┊14│15│16┊17
-          â\95╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
+          â\95\8c╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
            c3 18│19┊20│21│22┊23│24│25┊26
    ╶────────────┼──┼──┼──┼──┼──┼──┼──┼──
     d2     c1 27│28┊29│30│31┊32│33│34┊35
       ╶─────────┼──┼──┼──┼──┼──┼──┼──┼──
        cg1 c2 36│37┊38│39│40┊41│42│43┊44
-          â\95╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
+          â\95\8c╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
            c3 45│46┊47│48│49┊50│51│52┊53
 ────────────────┼──┼──┼──┼──┼──┼──┼──┼──
     d3     c1 54│55┊56│57│58┊59│60│61┊62
       ╶─────────┼──┼──┼──┼──┼──┼──┼──┼──
        cg1 c2 63│64┊65│66│67┊68│69│70┊71
-          â\95╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
+          â\95\8c╌╌╌╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌┼╌╌┼╌╌+╌╌
            c3 72│73┊74│75│76┊77│78│79┊80
 ",
     );
 }
+
+#[test]
+fn category_and_dimension_borders_2() {
+    let pivot_table = d4(
+        "Category and Dimension Borders 2",
+        EnumMap::from_fn(|border| match border {
+            Border::Dimension(RowColBorder(HeadingRegion::Rows, Axis2::Y))
+            | Border::Dimension(RowColBorder(HeadingRegion::Columns, Axis2::X)) => BorderStyle {
+                stroke: Stroke::Solid,
+                color: Color::BLUE,
+            },
+            Border::Category(RowColBorder(HeadingRegion::Rows, Axis2::Y))
+            | Border::Category(RowColBorder(HeadingRegion::Columns, Axis2::X)) => BorderStyle {
+                stroke: Stroke::Dashed,
+                color: Color::RED,
+            },
+            _ => BorderStyle::none(),
+        }),
+    );
+    assert_rendering(
+        &pivot_table,
+        "\
+Category and Dimension Borders 2
+                           b
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                     bg1
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                 b1       b2       b3
+             ╶──────────────────────────
+                  a        a        a
+             ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
+                  ag1      ag1      ag1
+                ╌╌╌╌╌╌╌  ╌╌╌╌╌╌╌  ╌╌╌╌╌╌
+d      c      a1 a2 a3 a1 a2 a3 a1 a2 a3
+dg1┊d1│    c1  0  1  2  3  4  5  6  7  8
+   ┊  │cg1┊c2  9 10 11 12 13 14 15 16 17
+   ┊  │   ┊c3 18 19 20 21 22 23 24 25 26
+   ┊d2│    c1 27 28 29 30 31 32 33 34 35
+   ┊  │cg1┊c2 36 37 38 39 40 41 42 43 44
+   ┊  │   ┊c3 45 46 47 48 49 50 51 52 53
+    d3│    c1 54 55 56 57 58 59 60 61 62
+      │cg1┊c2 63 64 65 66 67 68 69 70 71
+      │   ┊c3 72 73 74 75 76 77 78 79 80
+",
+    );
+}
index 49dbc46bb6cb24bfc3a9ed62bc52710c4bdd5cc1..cbecc65b2d6c3945bc584b10c4ed91162e92d14d 100644 (file)
@@ -199,7 +199,7 @@ static UNICODE_BOX: LazyLock<BoxChars> = LazyLock::new(|| {
     let d = Line::Dashed;
     use Line::{Double as D, Single as S};
     unicode_box.put(n, n, n, [' ', '╵', '╵', '║']);
-    unicode_box.put(n, n, d, ['â\95´', '╯', '╯', '╜']);
+    unicode_box.put(n, n, d, ['â\95\8c', '╯', '╯', '╜']);
     unicode_box.put(n, n, S, ['╴', '╯', '╯', '╜']);
     unicode_box.put(n, n, D, ['═', '╛', '╛', '╝']);
     unicode_box.put(n, S, n, ['╷', '│', '│', '║']);
@@ -214,7 +214,7 @@ static UNICODE_BOX: LazyLock<BoxChars> = LazyLock::new(|| {
     unicode_box.put(n, D, d, ['╖', '╢', '╢', '╢']);
     unicode_box.put(n, D, S, ['╖', '╢', '╢', '╢']);
     unicode_box.put(n, D, D, ['╗', '╣', '╣', '╣']);
-    unicode_box.put(d, n, n, ['â\95', '╰', '╰', '╙']);
+    unicode_box.put(d, n, n, ['â\95\8c', '╰', '╰', '╙']);
     unicode_box.put(d, n, d, ['╌', '┴', '┴', '╨']);
     unicode_box.put(d, n, S, ['─', '┴', '┴', '╨']);
     unicode_box.put(d, n, D, ['═', '╧', '╧', '╩']);