diff --git a/length.go b/length.go index 02d77f6..22987cd 100644 --- a/length.go +++ b/length.go @@ -1,15 +1,17 @@ package validate +import "unicode/utf8" + var ( ErrMustBeLonger = NewError("must contain at least %d characters") ErrMustBeShorter = NewError("must contain no more than %d characters") ) // MaxLength validates that a string is no longer than a given maximum. -// Length is measured in bytes (len), not runes. +// Length is counted in runes, so multi-byte characters count as one. func MaxLength(l int) func(string) error { return func(value string) error { - if len(value) > l { + if utf8.RuneCountInString(value) > l { return ErrMustBeShorter.With(l) } return nil @@ -17,10 +19,10 @@ func MaxLength(l int) func(string) error { } // MinLength validates that a string is at least a given minimum length. -// Length is measured in bytes (len), not runes. +// Length is counted in runes, so multi-byte characters count as one. func MinLength(l int) func(string) error { return func(value string) error { - if len(value) < l { + if utf8.RuneCountInString(value) < l { return ErrMustBeLonger.With(l) } return nil diff --git a/length_test.go b/length_test.go index defd24e..b471c38 100644 --- a/length_test.go +++ b/length_test.go @@ -20,7 +20,14 @@ func ExampleMinLength() { func TestMaxLength(t *testing.T) { testCases := map[int]map[string]error{ - 8: {"abcd": nil, "abcdefgh": nil, "abcd efg": nil, "abcdefghi": ErrMustBeShorter.With(8)}, + 8: { + "abcd": nil, + "abcdefgh": nil, + "abcd efg": nil, + "abcdéfgh": nil, // 8 runes, 9 bytes + "abcdefghi": ErrMustBeShorter.With(8), + "abcdéfghi": ErrMustBeShorter.With(8), // 9 runes, 10 bytes + }, } for setup, values := range testCases { @@ -41,7 +48,14 @@ func TestMaxLength(t *testing.T) { func TestMinLength(t *testing.T) { testCases := map[int]map[string]error{ - 8: {"abcd": ErrMustBeLonger.With(8), "abcdefgh": nil, "abcd efg": nil, "abcdefghi": nil}, + 8: { + "abcd": ErrMustBeLonger.With(8), + "abcdéfg": ErrMustBeLonger.With(8), // 7 runes, 8 bytes + "abcdefgh": nil, + "abcdéfgh": nil, // 8 runes, 9 bytes + "abcd efg": nil, + "abcdefghi": nil, + }, } for setup, values := range testCases {