From 914b83a8d659dcc4fec2013ff149b244930a06fd Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 24 Oct 2026 00:54:50 -0600 Subject: [PATCH] feat(csvx): BoolishTimestamp retains the original word via Raw() Unrecognized non-empty values keep the trimmed original in Raw() so callers can apply column-specific word policy post-parse (e.g. reading "disabled" as true in a Disabled column) instead of string-matching the error message. --- database/csvx/boolish.go | 15 +++++++++++++-- database/csvx/csvx_test.go | 3 +++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/database/csvx/boolish.go b/database/csvx/boolish.go index 757e192..f295888 100644 --- a/database/csvx/boolish.go +++ b/database/csvx/boolish.go @@ -9,14 +9,23 @@ import ( // BoolishTimestamp accepts optional true/false-like values and dates or // timestamps from human-edited CSV/TSV data. Valid reports that a value was // supplied; Bool reports its true-like meaning. Error records an unrecognized -// non-empty value without making the CSV decoder reject the row. +// non-empty value without making the CSV decoder reject the row. Raw reports +// the trimmed original value for unrecognized values so callers can apply +// their own column-specific word policy (e.g. reading "disabled" as true in +// a Disabled column). type BoolishTimestamp struct { Valid bool Bool bool Timestamp time.Time Error error + + raw string } +// Raw returns the trimmed original value that was parsed, retained for +// unrecognized values so callers can apply their own word policy. +func (v BoolishTimestamp) Raw() string { return v.raw } + // UnmarshalCSV parses a human value. Recognized true-like values and dates // set Bool; recognized false-like values leave it unset. Unrecognized // non-empty values are conservatively false with the error retained so the @@ -46,8 +55,10 @@ func (v *BoolishTimestamp) UnmarshalCSV(data []byte) error { } } // Unrecognized: valid but false, error recorded (Bool must not default - // to true for values this type does not understand). + // to true for values this type does not understand). The original value + // is retained for caller-side word policy. v.Bool = false + v.raw = raw v.Error = fmt.Errorf("unrecognized boolish timestamp %q", raw) return nil } diff --git a/database/csvx/csvx_test.go b/database/csvx/csvx_test.go index 481cb64..2100065 100644 --- a/database/csvx/csvx_test.go +++ b/database/csvx/csvx_test.go @@ -223,6 +223,9 @@ func TestBoolishTimestamp(t *testing.T) { if !v.Valid || v.Bool || v.Error == nil { t.Fatalf("%q: expected valid+false+error, got %+v", word, v) } + if v.Raw() != word { + t.Fatalf("%q: Raw() = %q, want the original word retained", word, v.Raw()) + } } // Empty / null sentinels: `\N` exact; word forms any case. The full