Commit d260f5a7 authored by Back Yu's avatar Back Yu Committed by Chris Roche
Browse files

solve to #79. (#80)


* solve to #79.

* fix CI failed for #80

* fix reviewing for #80

* add test cases for #80

* fix debug code for #80

* fix format bug for #80

* templates: improve by #80

* readme.md: fix comment to #80.

* templates: fix for equal min/max for #80

Signed-off-by: default avatarBack Yu <yhfszb@gmail.com>

* tests: add cases to `equal min/max len` for #80

Signed-off-by: default avatarBack Yu <yhfszb@gmail.com>

* templates: improve for #80

Signed-off-by: default avatarBack Yu <yhfszb@gmail.com>

* fix readme for #79.
parent f9d2b11e
Showing with 408 additions and 271 deletions
+408 -271
......@@ -183,9 +183,12 @@ Gogo support has the following limitations:
string x = 1 [(validate.rules).string.const = "foo"];
```
- **min_len/max_len**: these rules constrain the number of characters (Unicode code points) in the field. Note that the number of characters may differ from the number of bytes in the string. The string is considered as-is, and does not normalize.
- **len/min_len/max_len**: these rules constrain the number of characters (Unicode code points) in the field. Note that the number of characters may differ from the number of bytes in the string. The string is considered as-is, and does not normalize.
```protobuf
// x must be exactly 5 characters long
string x = 1 [(validate.rules).string.len = 5];
// x must be at least 3 characters long
string x = 1 [(validate.rules).string.min_len = 3];
......@@ -280,9 +283,12 @@ Gogo support has the following limitations:
bytes x = 1 [(validate.rules).bytes.const = "\xf0\x90\x28\xbc"];
```
- **min_len/max_len**: these rules constrain the number of bytes in the field.
- **len/min_len/max_len**: these rules constrain the number of bytes in the field.
```protobuf
// x must be exactly 3 bytes
bytes x = 1 [(validate.rules).bytes.len = 3];
// x must be at least 3 bytes long
bytes x = 1 [(validate.rules).bytes.min_len = 3];
......
......@@ -188,6 +188,8 @@ func (m Module) CheckSFixed64(r *validate.SFixed64Rules) {
}
func (m Module) CheckString(r *validate.StringRules) {
m.checkLen(r.Len, r.MinLen, r.MaxLen)
m.checkLen(r.LenBytes, r.MinBytes, r.MaxBytes)
m.checkMinMax(r.MinLen, r.MaxLen)
m.checkMinMax(r.MinBytes, r.MaxBytes)
m.checkIns(len(r.In), len(r.NotIn))
......@@ -420,6 +422,20 @@ func (m Module) checkMinMax(min, max *uint64) {
"`min` value is greater than `max` value")
}
func (m Module) checkLen(len, min, max *uint64) {
if len == nil {
return
}
m.Assert(
min == nil,
"cannot have both `len` and `min_len` rules on the same field")
m.Assert(
max == nil,
"cannot have both `len` and `max_len` rules on the same field")
}
func (m Module) checkPattern(p *string, in int) {
if p != nil {
m.Assert(in == 0, "regex `pattern` and `in` rules are incompatible")
......
......@@ -5,16 +5,38 @@ const bytesTpl = `
{{ template "const" . }}
{{ template "in" . }}
{{ if $r.Pattern }}
{{ unimplemented }}
{{/* TODO(akonradi) implement regular expression matching
if !{{ lookup $f "Pattern" }}.Match({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
{{ if or $r.Len (and $r.MinLen $r.MaxLen (eq $r.GetMinLen $r.GetMaxLen)) }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.Len }}
if (length != {{ $r.GetLen }}) {
{{ err . "value length must be " $r.GetLen " bytes" }}
}
{{ else }}
if (length != {{ $r.GetMinLen }}) {
{{ err . "value length must be " $r.GetMinLen " bytes" }}
}
{{ end }}
}
{{ else if $r.MinLen }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.MaxLen }}
if (length < {{ $r.GetMinLen }} || length > {{ $r.GetMaxLen }}) {
{{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " bytes, inclusive" }}
}
{{ else }}
if (length < {{ $r.GetMinLen }}) {
{{ err . "value length must be at least " $r.GetMinLen " bytes" }}
}
{{ end }}
}
{{ else if $r.MaxLen }}
if ({{ accessor . }}.size() > {{ $r.GetMaxLen }}) {
{{ err . "value length must be at most " $r.GetMaxLen " bytes" }}
}
*/}}
{{ end }}
{{ if $r.Prefix }}
{
const std::string prefix = {{ lit $r.GetPrefix }};
......@@ -41,31 +63,6 @@ const bytesTpl = `
}
{{ end }}
{{ if $r.MinLen }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.MaxLen }}
{{ if eq $r.GetMinLen $r.GetMaxLen }}
if (length != {{ $r.GetMinLen }}) {
{{ err . "value length must be " $r.GetMinLen " bytes" }}
}
{{ else }}
if (length < {{ $r.GetMinLen }} || length > {{ $r.GetMaxLen }}) {
{{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " bytes, inclusive" }}
}
{{ end }}
{{ else }}
if (length < {{ $r.GetMinLen }}) {
{{ err . "value length must be at least " $r.GetMinLen " bytes" }}
}
{{ end }}
}
{{ else if $r.MaxLen }}
if ({{ accessor . }}.size() > {{ $r.GetMaxLen }}) {
{{ err . "value length must be at most " $r.GetMaxLen " bytes" }}
}
{{ end }}
{{ if $r.GetIp }}
{{ unimplemented }}
{{/* TODO(akonradi) implement all of this
......@@ -88,4 +85,13 @@ const bytesTpl = `
}
*/}}
{{ end }}
{{ if $r.Pattern }}
{{ unimplemented }}
{{/* TODO(akonradi) implement regular expression matching
if !{{ lookup $f "Pattern" }}.Match({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
}
*/}}
{{ end }}
`
......@@ -4,14 +4,81 @@ const strTpl = `
{{ $f := .Field }}{{ $r := .Rules }}
{{ template "const" . }}
{{ template "in" . }}
{{ if $r.Pattern }}
{{ if or $r.Len (and $r.MinLen $r.MaxLen (eq $r.GetMinLen $r.GetMaxLen)) }}
{{ unimplemented }}
{{ if $r.Len }}
{{/* TODO(akonradi) implement UTF-8 length constraints
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetLen }} {
return {{ err . "value length must be " $r.GetLen " runes" }}
}
*/}}
{{ else }}
{{/* TODO(akonradi) implement UTF-8 length constraints
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " runes" }}
}
*/}}
{{ end }}
{{ else if $r.MinLen }}
{{ unimplemented }}
{{/* TODO(akonradi) implement UTF-8 length constraints
{{ if $r.MaxLen }}
if l := utf8.RuneCountInString({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " runes, inclusive" }}
}
{{ else }}
if utf8.RuneCountInString({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " runes" }}
}
{{ end }}
*/}}
{{ else if $r.MaxLen }}
{{ unimplemented }}
{{/* TODO(akonradi) implement regular expression constraints.
if !{{ lookup $f "Pattern" }}.MatchString({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
{{/* TODO(akonradi) implement UTF-8 length constraints
if utf8.RuneCountInString({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " runes" }}
}
*/}}
{{ end }}
{{ if or $r.LenBytes (and $r.MinBytes $r.MaxBytes (eq $r.GetMinBytes $r.GetMaxBytes)) }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.LenBytes }}
if (length != {{ $r.GetLenBytes }}) {
{{ err . "value length must be " $r.GetLenBytes " bytes" }}
}
{{ else }}
if (length != {{ $r.GetMinBytes }}) {
{{ err . "value length must be " $r.GetMinBytes " bytes" }}
}
{{ end }}
}
{{ else if $r.MinBytes }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.MaxBytes }}
{{ if eq $r.GetMinBytes $r.GetMaxBytes }}
if (length != {{ $r.GetMinBytes }}) {
{{ err . "value length must be " $r.GetMinBytes " bytes" }}
}
{{ else }}
if (length < {{ $r.GetMinBytes }} || length > {{ $r.GetMaxBytes }}) {
{{ err . "value length must be between " $r.GetMinBytes " and " $r.GetMaxBytes " bytes, inclusive" }}
}
{{ end }}
{{ else }}
if (length < {{ $r.GetMinBytes }}) {
{{ err . "value length must be at least " $r.GetMinBytes " bytes" }}
}
{{ end }}
}
{{ else if $r.MaxBytes }}
if ({{ accessor . }}.size() > {{ $r.GetMaxBytes }}) {
{{ err . "value length must be at most " $r.GetMaxBytes " bytes" }}
}
{{ end }}
{{ if $r.Prefix }}
{
const std::string prefix = {{ lit $r.GetPrefix }};
......@@ -92,56 +159,12 @@ const strTpl = `
*/}}
{{ end }}
{{ if $r.MinLen }}
{{ unimplemented }}
{{/* TODO(akonradi) implement UTF-8 length constraints
{{ if $r.MaxLen }}
{{ if eq $r.GetMinLen $r.GetMaxLen }}
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " runes" }}
}
{{ else }}
if l := utf8.RuneCountInString({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " runes, inclusive" }}
}
{{ end }}
{{ else }}
if utf8.RuneCountInString({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " runes" }}
}
{{ end }}
*/}}
{{ else if $r.MaxLen }}
{{ unimplemented }}
{{/* TODO(akonradi) implement UTF-8 length constraints
if utf8.RuneCountInString({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " runes" }}
}
*/}}
{{ end }}
{{ if $r.MinBytes }}
{
const auto length = {{ accessor . }}.size();
{{ if $r.MaxBytes }}
{{ if eq $r.GetMinBytes $r.GetMaxBytes }}
if (length != {{ $r.GetMinBytes }}) {
{{ err . "value length must be " $r.GetMinBytes " bytes" }}
}
{{ else }}
if (length < {{ $r.GetMinBytes }} || length > {{ $r.GetMaxBytes }}) {
{{ err . "value length must be between " $r.GetMinBytes " and " $r.GetMaxBytes " bytes, inclusive" }}
}
{{ end }}
{{ else }}
if (length < {{ $r.GetMinBytes }}) {
{{ err . "value length must be at least " $r.GetMinBytes " bytes" }}
}
{{ end }}
{{ if $r.Pattern }}
{{ unimplemented }}
{{/* TODO(akonradi) implement regular expression constraints.
if !{{ lookup $f "Pattern" }}.MatchString({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
}
{{ else if $r.MaxBytes }}
if ({{ accessor . }}.size() > {{ $r.GetMaxBytes }}) {
{{ err . "value length must be at most " $r.GetMaxBytes " bytes" }}
}
*/}}
{{ end }}
`
......@@ -3,13 +3,32 @@ package tpl
const bytesTpl = `
{{ $f := .Field }}{{ $r := .Rules }}
{{ if $r.Pattern }}
if !{{ lookup $f "Pattern" }}.Match({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
{{ if or $r.Len (and $r.MinLen $r.MaxLen (eq $r.GetMinLen $r.GetMaxLen)) }}
{{ if $r.Len }}
if len({{ accessor . }}) != {{ $r.GetLen }} {
return {{ err . "value length must be " $r.GetLen " bytes" }}
}
{{ else }}
if len({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " bytes" }}
}
{{ end }}
{{ else if $r.MinLen }}
{{ if $r.MaxLen }}
if l := len({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " bytes, inclusive" }}
}
{{ else }}
if len({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " bytes" }}
}
{{ end }}
{{ else if $r.MaxLen }}
if len({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " bytes" }}
}
{{ end }}
{{ if $r.Prefix }}
if !bytes.HasPrefix({{ accessor . }}, {{ lit $r.GetPrefix }}) {
return {{ err . "value does not have prefix " (byteStr $r.GetPrefix) }}
......@@ -28,28 +47,6 @@ const bytesTpl = `
}
{{ end }}
{{ if $r.MinLen }}
{{ if $r.MaxLen }}
{{ if eq $r.GetMinLen $r.GetMaxLen }}
if len({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " bytes" }}
}
{{ else }}
if l := len({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " bytes, inclusive" }}
}
{{ end }}
{{ else }}
if len({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " bytes" }}
}
{{ end }}
{{ else if $r.MaxLen }}
if len({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " bytes" }}
}
{{ end }}
{{ if $r.In }}
if _, ok := {{ lookup $f "InLookup" }}[string({{ accessor . }})]; !ok {
return {{ err . "value must be in list " $r.In }}
......@@ -79,4 +76,10 @@ const bytesTpl = `
return {{ err . "value must be a valid IPv6 address" }}
}
{{ end }}
{{ if $r.Pattern }}
if !{{ lookup $f "Pattern" }}.Match({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
}
{{ end }}
`
......@@ -5,9 +5,54 @@ const strTpl = `
{{ template "const" . }}
{{ template "in" . }}
{{ if $r.Pattern }}
if !{{ lookup $f "Pattern" }}.MatchString({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
{{ if or $r.Len (and $r.MinLen $r.MaxLen (eq $r.GetMinLen $r.GetMaxLen)) }}
{{ if $r.Len }}
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetLen }} {
return {{ err . "value length must be " $r.GetLen " runes" }}
{{ else }}
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " runes" }}
{{ end }}
}
{{ else if $r.MinLen }}
{{ if $r.MaxLen }}
if l := utf8.RuneCountInString({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " runes, inclusive" }}
}
{{ else }}
if utf8.RuneCountInString({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " runes" }}
}
{{ end }}
{{ else if $r.MaxLen }}
if utf8.RuneCountInString({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " runes" }}
}
{{ end }}
{{ if or $r.LenBytes (and $r.MinBytes $r.MaxBytes (eq $r.GetMinBytes $r.GetMaxBytes)) }}
{{ if $r.LenBytes }}
if len({{ accessor . }}) != {{ $r.GetLenBytes }} {
return {{ err . "value length must be " $r.GetLenBytes " bytes" }}
}
{{ else }}
if len({{ accessor . }}) != {{ $r.GetMinBytes }} {
return {{ err . "value length must be " $r.GetMinBytes " bytes" }}
}
{{ end }}
{{ else if $r.MinBytes }}
{{ if $r.MaxBytes }}
if l := len({{ accessor . }}); l < {{ $r.GetMinBytes }} || l > {{ $r.GetMaxBytes }} {
return {{ err . "value length must be between " $r.GetMinBytes " and " $r.GetMaxBytes " bytes, inclusive" }}
}
{{ else }}
if len({{ accessor . }}) < {{ $r.GetMinBytes }} {
return {{ err . "value length must be at least " $r.GetMinBytes " bytes" }}
}
{{ end }}
{{ else if $r.MaxBytes }}
if len({{ accessor . }}) > {{ $r.GetMaxBytes }} {
return {{ err . "value length must be at most " $r.GetMaxBytes " bytes" }}
}
{{ end }}
......@@ -61,47 +106,9 @@ const strTpl = `
}
{{ end }}
{{ if $r.MinLen }}
{{ if $r.MaxLen }}
{{ if eq $r.GetMinLen $r.GetMaxLen }}
if utf8.RuneCountInString({{ accessor . }}) != {{ $r.GetMinLen }} {
return {{ err . "value length must be " $r.GetMinLen " runes" }}
}
{{ else }}
if l := utf8.RuneCountInString({{ accessor . }}); l < {{ $r.GetMinLen }} || l > {{ $r.GetMaxLen }} {
return {{ err . "value length must be between " $r.GetMinLen " and " $r.GetMaxLen " runes, inclusive" }}
}
{{ end }}
{{ else }}
if utf8.RuneCountInString({{ accessor . }}) < {{ $r.GetMinLen }} {
return {{ err . "value length must be at least " $r.GetMinLen " runes" }}
}
{{ end }}
{{ else if $r.MaxLen }}
if utf8.RuneCountInString({{ accessor . }}) > {{ $r.GetMaxLen }} {
return {{ err . "value length must be at most " $r.GetMaxLen " runes" }}
}
{{ end }}
{{ if $r.MinBytes }}
{{ if $r.MaxBytes }}
{{ if eq $r.GetMinBytes $r.GetMaxBytes }}
if len({{ accessor . }}) != {{ $r.GetMinBytes }} {
return {{ err . "value length must be " $r.GetMinBytes " bytes" }}
}
{{ else }}
if l := len({{ accessor . }}); l < {{ $r.GetMinBytes }} || l > {{ $r.GetMaxBytes }} {
return {{ err . "value length must be between " $r.GetMinBytes " and " $r.GetMaxBytes " bytes, inclusive" }}
}
{{ end }}
{{ else }}
if len({{ accessor . }}) < {{ $r.GetMinBytes }} {
return {{ err . "value length must be at least " $r.GetMinBytes " bytes" }}
}
{{ end }}
{{ else if $r.MaxBytes }}
if len({{ accessor . }}) > {{ $r.GetMaxBytes }} {
return {{ err . "value length must be at most " $r.GetMaxBytes " bytes" }}
}
{{ end }}
{{ if $r.Pattern }}
if !{{ lookup $f "Pattern" }}.MatchString({{ accessor . }}) {
return {{ err . "value does not match regex pattern " (lit $r.GetPattern) }}
}
{{ end }}
`
......@@ -9,9 +9,11 @@ message BytesNone { bytes val = 1; }
message BytesConst { bytes val = 1 [(validate.rules).bytes.const = "foo"]; }
message BytesIn { bytes val = 1 [(validate.rules).bytes = {in: ["bar", "baz"]}]; }
message BytesNotIn { bytes val = 1 [(validate.rules).bytes = {not_in: ["fizz", "buzz"]}]; }
message BytesLen { bytes val = 1 [(validate.rules).bytes.len = 3]; }
message BytesMinLen { bytes val = 1 [(validate.rules).bytes.min_len = 3]; }
message BytesMaxLen { bytes val = 1 [(validate.rules).bytes.max_len = 5]; }
message BytesMinMaxLen { bytes val = 1 [(validate.rules).bytes = {min_len: 3, max_len: 5}]; }
message BytesEqualMinMaxLen { bytes val = 1 [(validate.rules).bytes = {min_len: 5, max_len: 5}]; }
message BytesPattern { bytes val = 1 [(validate.rules).bytes.pattern = "^[\x00-\x7F]+$"]; }
message BytesPrefix { bytes val = 1 [(validate.rules).bytes.prefix = "\x99"]; }
message BytesContains { bytes val = 1 [(validate.rules).bytes.contains = "bar"]; }
......
......@@ -9,12 +9,16 @@ message StringNone { string val = 1; }
message StringConst { string val = 1 [(validate.rules).string.const = "foo"]; }
message StringIn { string val = 1 [(validate.rules).string = {in: ["bar", "baz"]}]; }
message StringNotIn { string val = 1 [(validate.rules).string = {not_in: ["fizz", "buzz"]}]; }
message StringLen { string val = 1 [(validate.rules).string.len = 3]; }
message StringMinLen { string val = 1 [(validate.rules).string.min_len = 3]; }
message StringMaxLen { string val = 1 [(validate.rules).string.max_len = 5]; }
message StringMinMaxLen { string val = 1 [(validate.rules).string = {min_len: 3, max_len: 5}]; }
message StringEqualMinMaxLen { string val = 1 [(validate.rules).string = {min_len: 5, max_len: 5}]; }
message StringLenBytes { string val = 1 [(validate.rules).string.len_bytes = 4]; }
message StringMinBytes { string val = 1 [(validate.rules).string.min_bytes = 4]; }
message StringMaxBytes { string val = 1 [(validate.rules).string.max_bytes = 8]; }
message StringMinMaxBytes { string val = 1 [(validate.rules).string = {min_bytes: 4, max_bytes: 8}]; }
message StringEqualMinMaxBytes { string val = 1 [(validate.rules).string = {min_bytes: 4, max_bytes: 8}]; }
message StringPattern { string val = 1 [(validate.rules).string.pattern = "(?i)^[a-z0-9]+$"]; }
message StringPatternEscapes { string val = 1 [(validate.rules).string.pattern = "\\* \\\\ \\w"]; }
message StringPrefix { string val = 1 [(validate.rules).string.prefix = "foo"]; }
......
......@@ -715,6 +715,12 @@ var stringCases = []TestCase{
{"string - not in - valid", &cases.StringNotIn{Val: "quux"}, true},
{"string - not in - invalid", &cases.StringNotIn{Val: "fizz"}, false},
{"string - len - valid", &cases.StringLen{Val: "baz"}, true},
{"string - len - valid (multibyte)", &cases.StringLen{Val: "你好吖"}, true},
{"string - len - invalid (lt)", &cases.StringLen{Val: "go"}, false},
{"string - len - invalid (gt)", &cases.StringLen{Val: "fizz"}, false},
{"string - len - invalid (multibyte)", &cases.StringLen{Val: "你好"}, false},
{"string - min len - valid", &cases.StringMinLen{Val: "protoc"}, true},
{"string - min len - valid (min)", &cases.StringMinLen{Val: "baz"}, true},
{"string - min len - invalid", &cases.StringMinLen{Val: "go"}, false},
......@@ -732,6 +738,14 @@ var stringCases = []TestCase{
{"string - min/max len - invalid (below)", &cases.StringMinMaxLen{Val: "go"}, false},
{"string - min/max len - invalid (above)", &cases.StringMinMaxLen{Val: "validate"}, false},
{"string - equal min/max len - valid", &cases.StringEqualMinMaxLen{Val: "proto"}, true},
{"string - equal min/max len - invalid", &cases.StringEqualMinMaxLen{Val: "validate"}, false},
{"string - len bytes - valid", &cases.StringLenBytes{Val: "pace"}, true},
{"string - len bytes - invalid (lt)", &cases.StringLenBytes{Val: "val"}, false},
{"string - len bytes - invalid (gt)", &cases.StringLenBytes{Val: "world"}, false},
{"string - len bytes - invalid (multibyte)", &cases.StringLenBytes{Val: "世界和平"}, false},
{"string - min bytes - valid", &cases.StringMinBytes{Val: "proto"}, true},
{"string - min bytes - valid (min)", &cases.StringMinBytes{Val: "quux"}, true},
{"string - min bytes - valid (multibyte)", &cases.StringMinBytes{Val: "你好"}, true},
......@@ -749,6 +763,9 @@ var stringCases = []TestCase{
{"string - min/max bytes - invalid (below)", &cases.StringMinMaxBytes{Val: "foo"}, false},
{"string - min/max bytes - invalid (above)", &cases.StringMinMaxBytes{Val: "你好你好你"}, false},
{"string - equal min/max bytes - valid", &cases.StringEqualMinMaxBytes{Val: "protoc"}, true},
{"string - equal min/max bytes - invalid", &cases.StringEqualMinMaxBytes{Val: "foo"}, false},
{"string - pattern - valid", &cases.StringPattern{Val: "Foo123"}, true},
{"string - pattern - invalid", &cases.StringPattern{Val: "!@#$%^&*()"}, false},
{"string - pattern - invalid (empty)", &cases.StringPattern{Val: ""}, false},
......@@ -820,6 +837,10 @@ var bytesCases = []TestCase{
{"bytes - not in - valid", &cases.BytesNotIn{Val: []byte("quux")}, true},
{"bytes - not in - invalid", &cases.BytesNotIn{Val: []byte("fizz")}, false},
{"bytes - len - valid", &cases.BytesLen{Val: []byte("baz")}, true},
{"bytes - len - invalid (lt)", &cases.BytesLen{Val: []byte("go")}, false},
{"bytes - len - invalid (gt)", &cases.BytesLen{Val: []byte("fizz")}, false},
{"bytes - min len - valid", &cases.BytesMinLen{Val: []byte("fizz")}, true},
{"bytes - min len - valid (min)", &cases.BytesMinLen{Val: []byte("baz")}, true},
{"bytes - min len - invalid", &cases.BytesMinLen{Val: []byte("go")}, false},
......@@ -834,6 +855,9 @@ var bytesCases = []TestCase{
{"bytes - min/max len - invalid (below)", &cases.BytesMinMaxLen{Val: []byte("go")}, false},
{"bytes - min/max len - invalid (above)", &cases.BytesMinMaxLen{Val: []byte("validate")}, false},
{"bytes - equal min/max len - valid", &cases.BytesEqualMinMaxLen{Val: []byte("proto")}, true},
{"bytes - equal min/max len - invalid", &cases.BytesEqualMinMaxLen{Val: []byte("validate")}, false},
{"bytes - pattern - valid", &cases.BytesPattern{Val: []byte("Foo123")}, true},
{"bytes - pattern - invalid", &cases.BytesPattern{Val: []byte("你好你好")}, false},
{"bytes - pattern - invalid (empty)", &cases.BytesPattern{Val: []byte("")}, false},
......@@ -1013,10 +1037,10 @@ var oneofCases = []TestCase{
{"oneof - field - valid (Z)", &cases.OneOf{O: &cases.OneOf_Z{Z: &cases.TestOneOfMsg{Val: true}}}, true},
{"oneof - field - valid (empty)", &cases.OneOf{}, true},
{"oneof - field - invalid (X)", &cases.OneOf{O: &cases.OneOf_X{X: "fizzbuzz"}}, false},
{"oneof - field - invalid (Y)", &cases.OneOf{O: &cases.OneOf_Y{-1}}, false},
{"oneof - filed - invalid (Z)", &cases.OneOf{O: &cases.OneOf_Z{&cases.TestOneOfMsg{}}}, false},
{"oneof - field - invalid (Y)", &cases.OneOf{O: &cases.OneOf_Y{Y: -1}}, false},
{"oneof - filed - invalid (Z)", &cases.OneOf{O: &cases.OneOf_Z{Z: &cases.TestOneOfMsg{}}}, false},
{"oneof - required - valid", &cases.OneOfRequired{O: &cases.OneOfRequired_X{""}}, true},
{"oneof - required - valid", &cases.OneOfRequired{O: &cases.OneOfRequired_X{X: ""}}, true},
{"oneof - require - invalid", &cases.OneOfRequired{}, false},
}
......
......@@ -6,10 +6,11 @@ import "validate/validate.proto";
message Bytes {
bytes none = 1;
bytes min = 2 [(validate.rules).bytes.min_len = 3];
bytes max = 3 [(validate.rules).bytes.max_len = 10];
bytes min_max = 4 [(validate.rules).bytes = { min_len: 3, max_len: 10 }];
bytes eq_len = 5 [(validate.rules).bytes = { min_len: 5, max_len: 5}];
bytes len = 16 [(validate.rules).bytes.len = 5];
bytes min = 2 [(validate.rules).bytes.min_len = 3];
bytes max = 3 [(validate.rules).bytes.max_len = 10];
bytes min_max = 4 [(validate.rules).bytes = { min_len: 3, max_len: 10 }];
bytes eq_len = 5 [(validate.rules).bytes = { min_len: 5, max_len: 5}];
bytes pattern = 6 [(validate.rules).bytes.pattern = "foo+(ba+r)?"];
bytes prefix = 7 [(validate.rules).bytes.prefix = "\x99"];
......
......@@ -6,13 +6,15 @@ import "validate/validate.proto";
message String {
string none = 1;
string min = 2 [(validate.rules).string.min_len = 3];
string max = 3 [(validate.rules).string.max_len = 10];
string min_max = 4 [(validate.rules).string = { min_len: 3, max_len: 10 }];
string len = 24 [(validate.rules).string.len = 5];
string min = 2 [(validate.rules).string.min_len = 3];
string max = 3 [(validate.rules).string.max_len = 10];
string min_max = 4 [(validate.rules).string = { min_len: 3, max_len: 10 }];
string eq_len = 5 [(validate.rules).string = { min_len: 5, max_len: 5}];
string min_bytes = 6 [(validate.rules).string.min_bytes = 4];
string max_bytes = 7 [(validate.rules).string.max_bytes = 11];
string len_bytes = 25 [(validate.rules).string.len = 6];
string min_bytes = 6 [(validate.rules).string.min_bytes = 4];
string max_bytes = 7 [(validate.rules).string.max_bytes = 11];
string min_max_bytes = 8 [(validate.rules).string = { min_bytes: 4, max_bytes: 11}];
string eq_bytes = 9 [(validate.rules).string = { min_bytes: 6, max_bytes: 6}];
......
This diff is collapsed.
......@@ -457,6 +457,11 @@ message StringRules {
// Const specifies that this field must be exactly the specified value
optional string const = 1;
// Len specifies that this field must be the specified number of
// characters (Unicode code points). Note that the number of
// characters may differ from the number of bytes in the string.
optional uint64 len = 19;
// MinLen specifies that this field must be the specified number of
// characters (Unicode code points) at a minimum. Note that the number of
// characters may differ from the number of bytes in the string.
......@@ -467,6 +472,10 @@ message StringRules {
// characters may differ from the number of bytes in the string.
optional uint64 max_len = 3;
// LenBytes specifies that this field must be the specified number of bytes
// at a minimum
optional uint64 len_bytes = 20;
// MinBytes specifies that this field must be the specified number of bytes
// at a minimum
optional uint64 min_bytes = 4;
......@@ -538,6 +547,9 @@ message BytesRules {
// Const specifies that this field must be exactly the specified value
optional bytes const = 1;
// Len specifies that this field must be the specified number of bytes
optional uint64 len = 13;
// MinLen specifies that this field must be the specified number of bytes
// at a minimum
optional uint64 min_len = 2;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment