From 49f32f16b196d3a5f13ba46fee92113aebc49f76 Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Sat, 25 Apr 2026 11:16:55 -0500 Subject: [PATCH] strings: add StringsOr for optional slice of strings --- forms.go | 13 +++++++++++++ forms_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/forms.go b/forms.go index f0ab38b..d9949fb 100644 --- a/forms.go +++ b/forms.go @@ -123,6 +123,8 @@ func (p *stringsParser[T]) Parse(values []string) error { } // Strings is used to extract a form data value into a slice of Go strings. +// +// If the form value is missing, then an error is returned during parsing. func Strings[T StringType](s *[]T) Parser { return &stringsParser[T]{ required: true, @@ -130,6 +132,17 @@ func Strings[T StringType](s *[]T) Parser { } } +// StringsOr is used to extract multiple form values for a given key into a slice of Go strings. +// +// If the form value is missing, then the given alt value is used instead. +func StringsOr[T StringType](s *[]T, alt []T) Parser { + *s = alt + return &stringsParser[T]{ + required: false, + destination: s, + } +} + // Secret is used to extract a form data value into a Go conceal.Text. If the // value is missing then an error is returned during parsing. func Secret(s **conceal.Text) Parser { diff --git a/forms_test.go b/forms_test.go index 67c95b6..8f900f6 100644 --- a/forms_test.go +++ b/forms_test.go @@ -364,7 +364,7 @@ func Test_Parse_IntType_IntOr(t *testing.T) { must.Eq(t, 100, age) } -func Test_Parse_StringType_Strings(t *testing.T) { +func Test_Parse_StringType_Strings_present(t *testing.T) { t.Parallel() data := url.Values{ @@ -379,3 +379,50 @@ func Test_Parse_StringType_Strings(t *testing.T) { must.NoError(t, err) must.Eq(t, []string{"alice", "bob", "carol"}, names) } + +func Test_Parse_StringType_Strings_missing(t *testing.T) { + t.Parallel() + + data := url.Values{ + "names": []string{"alice", "bob", "carol"}, + } + + var jobs []string + + err := ParseValues(data, Schema{ + "jobs": Strings(&jobs), + }) + must.Error(t, err) +} + +func Test_Parse_StringType_StringsOr_present(t *testing.T) { + t.Parallel() + + data := url.Values{ + "names": []string{"alice", "bob", "carol"}, + } + + var names []string + + err := ParseValues(data, Schema{ + "names": StringsOr(&names, []string{"zed", "yulia"}), + }) + must.NoError(t, err) + must.Eq(t, []string{"alice", "bob", "carol"}, names) +} + +func Test_Parse_StringType_StringsOr_missing(t *testing.T) { + t.Parallel() + + data := url.Values{ + "names": []string{"alice", "bob", "carol"}, + } + + var jobs []string + + err := ParseValues(data, Schema{ + "jobs": StringsOr(&jobs, []string{"janitor", "cashier"}), + }) + must.NoError(t, err) + must.Eq(t, []string{"janitor", "cashier"}, jobs) +}