mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
feat: add permissive text and list values
This commit is contained in:
parent
ca6e271f66
commit
bd03bfb769
@ -4,6 +4,7 @@
|
|||||||
// can be read from a sheet, written to a file, and scanned from a database.
|
// can be read from a sheet, written to a file, and scanned from a database.
|
||||||
//
|
//
|
||||||
// It also provides generic Parse/Serialize functions for CSV, TSV, JSON,
|
// It also provides generic Parse/Serialize functions for CSV, TSV, JSON,
|
||||||
// and JSONL, delimiter-specific string and integer slices, plus text
|
// and JSONL, delimiter-specific string and integer slices, and permissive
|
||||||
// normalization helpers (NormalizeEmail, NormalizePhone).
|
// text/list value types, plus normalization helpers (NormalizeEmail,
|
||||||
|
// NormalizePhone).
|
||||||
package csvx
|
package csvx
|
||||||
|
|||||||
26
database/csvx/listish.go
Normal file
26
database/csvx/listish.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package csvx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listish is a list encoded as comma- or whitespace-delimited text.
|
||||||
|
type Listish []string
|
||||||
|
|
||||||
|
// UnmarshalCSV parses comma- or whitespace-delimited values.
|
||||||
|
func (s *Listish) UnmarshalCSV(data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
*s = Listish{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.FieldsFunc(string(data), func(r rune) bool {
|
||||||
|
return r == ',' || unicode.IsSpace(r)
|
||||||
|
})
|
||||||
|
if len(parts) == 0 {
|
||||||
|
*s = Listish{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*s = Listish(parts)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
31
database/csvx/listish_test.go
Normal file
31
database/csvx/listish_test.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package csvx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListishUnmarshalCSV(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in string
|
||||||
|
want Listish
|
||||||
|
}{
|
||||||
|
{name: "comma", in: "admin,hr,pm", want: Listish{"admin", "hr", "pm"}},
|
||||||
|
{name: "whitespace", in: "admin hr\tpm", want: Listish{"admin", "hr", "pm"}},
|
||||||
|
{name: "mixed", in: " admin, hr\npm ", want: Listish{"admin", "hr", "pm"}},
|
||||||
|
{name: "empty", in: " , \t", want: Listish{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var got Listish
|
||||||
|
if err := got.UnmarshalCSV([]byte(tt.in)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Fatalf("got %#v, want %#v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
60
database/csvx/textish.go
Normal file
60
database/csvx/textish.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package csvx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Textish is text with common explicit-empty words normalized to an empty value.
|
||||||
|
type Textish string
|
||||||
|
|
||||||
|
func (t Textish) Empty() bool { return string(t) == "" }
|
||||||
|
|
||||||
|
func (t Textish) String() string { return string(t) }
|
||||||
|
|
||||||
|
func normalizeTextish(value string) string {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
|
case "", "none", "disable", "disabled":
|
||||||
|
return ""
|
||||||
|
default:
|
||||||
|
return strings.TrimSpace(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Textish) Value() (driver.Value, error) { return string(t), nil }
|
||||||
|
|
||||||
|
func (t *Textish) Scan(value any) error {
|
||||||
|
if value == nil {
|
||||||
|
*t = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
*t = Textish(normalizeTextish(v))
|
||||||
|
case []byte:
|
||||||
|
*t = Textish(normalizeTextish(string(v)))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("csvx.Textish: cannot scan %T", value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Textish) MarshalCSV() ([]byte, error) { return []byte(t), nil }
|
||||||
|
|
||||||
|
func (t *Textish) UnmarshalCSV(data []byte) error {
|
||||||
|
*t = Textish(normalizeTextish(string(data)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Textish) MarshalJSON() ([]byte, error) { return json.Marshal(string(t)) }
|
||||||
|
|
||||||
|
func (t *Textish) UnmarshalJSON(data []byte) error {
|
||||||
|
var value string
|
||||||
|
if err := json.Unmarshal(data, &value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = Textish(normalizeTextish(value))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
database/csvx/textish_test.go
Normal file
22
database/csvx/textish_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package csvx
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTextishNormalizesExplicitEmptyValues(t *testing.T) {
|
||||||
|
for _, value := range []string{"", "none", "disable", "disabled", " NONE "} {
|
||||||
|
var got Textish
|
||||||
|
if err := got.UnmarshalCSV([]byte(value)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !got.Empty() {
|
||||||
|
t.Fatalf("Textish(%q) = %q, want empty", value, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var got Textish
|
||||||
|
if err := got.UnmarshalCSV([]byte("https://hooks.example.test/enrich")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got.String() != "https://hooks.example.test/enrich" {
|
||||||
|
t.Fatalf("Textish URL = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user