coverage~bigbes/sr-ht-doltede3b0bbbeads/raw.go

Coverage
100.0% 28/28 statements
Δ
Blob
de3ca99
Uncovered nothing — every instrumented line ran
1 package beads
2
3 import "sourcecraft.dev/bigbes/sr-ht-dolt/browse"
4
5 // --- the stored rows behind one issue ----------------------------------------
6 //
7 // Everything else in this package is a reading: the Issue struct names the
8 // columns bd surfaces and drops the rest, humanizeEvent turns two JSON blobs
9 // into a sentence, an Edge keeps an id and a type out of a dependency row. Each
10 // of those is a choice, and a choice can be wrong — a column bd added that no
11 // field here models, a status this projection mapped to the wrong category, an
12 // event whose stored old_value says something the humanized line does not — and
13 // none of it is visible from the pane that made the choice.
14 //
15 // So the detail modes also carry the rows they were built from, as read. The
16 // point is to be checkable against the rendering above it, which is why nothing
17 // here is normalised, sorted or rewritten: the columns come in the order the
18 // table was read in, the values are the strings browse returned, and a cell that
19 // holds no value says so rather than rendering as one.
20
21 // RawMax caps how many rows of one related table the raw section carries. The
22 // issues row is one row by construction; a busy issue's events or comments are
23 // not, and a section that dumps three hundred audit rows is not a check on the
24 // rendering, it is a second page nobody reads. RawTable.Matched says how many
25 // rows actually belong to the issue, so a capped table is visibly capped.
26 const RawMax = 50
27
28 // RawCell is one stored cell: the column it came from, the value browse rendered
29 // for it, and whether the cell holds a value at all.
30 //
31 // Null is the whole reason this type exists rather than a plain string pair.
32 // browse renders a cell that holds no value as the text "NULL", which is exactly
33 // what a cell storing those four characters renders as, and cell() resolves that
34 // collision by flattening a real NULL to "". That is right everywhere else here
35 // — a missing closed_at is rendered as nothing — and wrong in this one place: a
36 // view whose purpose is to be compared against the database may not report an
37 // absent cell and an empty string as the same thing.
38 //
39 // Value is "" for a null cell rather than the "NULL" browse printed. Carrying
40 // that text would put the collision straight back: a consumer that renders Value
41 // without reading Null would print "NULL" for both readings again, which is the
42 // state this type exists to leave behind.
43 type RawCell struct {
44 Column string
45 Value string
46 Null bool
47 }
48
49 // RawRow is one stored row: its cells in the order the columns were read in.
50 // Column order is data here — it is the table's own order, and a raw view that
51 // sorted it would be hiding one of the things it is meant to show.
52 type RawRow struct {
53 Cells []RawCell
54 }
55
56 // RawTable is the rows one table contributed to one issue.
57 //
58 // Matched is how many rows of the table belong to the issue; Rows holds at most
59 // RawMax of them, in table order. The two differ only when the cap bit, and that
60 // difference is what the pane says out loud.
61 type RawTable struct {
62 Table string
63 Rows []RawRow
64 Matched int
65 }
66
67 // Clipped reports that the table had more rows for this issue than RawMax, so
68 // Rows is the first of them and not all of them.
69 2 func (t RawTable) Clipped() bool { return t.Matched > len(t.Rows) }
70
71 // rawRow renders one row as stored, in the page's own column order.
72 //
73 // A cell's nullness is read exactly as cell() reads it — through rowCells.isNull,
74 // which is the one place in this package that decides — so the raw section and
75 // the fields above it can never disagree about which cells hold a value.
76 181 func rawRow(columns []string, r rowCells) RawRow {
77 181 out := RawRow{Cells: make([]RawCell, 0, len(columns))}
78 879 for i, name := range columns {
79 879 if i >= len(r.values) {
80 1 // A row shorter than its header is a malformed page, not a null cell:
81 1 // there is no cell here to report either way, so it is left out.
82 1 break
83 }
84 878 if r.isNull(i) {
85 49 out.Cells = append(out.Cells, RawCell{Column: name, Null: true})
86 49 continue
87 }
88 829 out.Cells = append(out.Cells, RawCell{Column: name, Value: r.values[i]})
89 }
90 181 return out
91 }
92
93 // rawTableOf collects the rows of a page that belong to one issue, capped at
94 // RawMax and counted whole. A table with no rows for this issue yields nil — the
95 // pane lists the tables that had something to say, not every table it read.
96 81 func rawTableOf(name string, page *browse.RowPage, match func(map[string]int, rowCells) bool) *RawTable {
97 81 if page == nil {
98 11 return nil
99 11 }
100 70 cols := indexCols(page.Columns)
101 70 tbl := RawTable{Table: name}
102 8223 for _, r := range rowsOf(page) {
103 8223 if !match(cols, r) {
104 6086 continue
105 }
106 2137 tbl.Matched++
107 2137 if len(tbl.Rows) < RawMax {
108 180 tbl.Rows = append(tbl.Rows, rawRow(page.Columns, r))
109 180 }
110 }
111 70 if tbl.Matched == 0 {
112 14 return nil
113 14 }
114 56 return &tbl
115 }
116
117 // matchColumn matches the rows whose named column holds the wanted id — the
118 // shape of every per-issue table here except dependencies, which has two such
119 // columns and gets its own matcher at the call site.
120 61 func matchColumn(column, want string) func(map[string]int, rowCells) bool {
121 8191 return func(cols map[string]int, r rowCells) bool {
122 8191 return cell(cols, r, column) == want
123 8191 }
124 }
125
126 // addRaw appends a table's rows to the raw section, skipping the tables that had
127 // none.
128 81 func (d *Data) addRaw(t *RawTable) {
129 81 if t == nil {
130 25 return
131 25 }
132 56 d.Raw = append(d.Raw, *t)
133 }