1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 20:52:22 +00:00

vendor: add go-proto-validators

This commit is contained in:
chai2010 2018-07-14 21:43:54 +08:00
parent 619a2b30c8
commit f98d1084f1
23 changed files with 2868 additions and 1 deletions

2
vendor/README.md vendored
View File

@ -10,4 +10,4 @@ TODO
- [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway): [v1.4.1](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.4.1) - [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway): [v1.4.1](https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.4.1)
- [grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware): [v1.0.0](https://github.com/grpc-ecosystem/go-grpc-middleware/releases/tag/v1.0.0) - [grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware): [v1.0.0](https://github.com/grpc-ecosystem/go-grpc-middleware/releases/tag/v1.0.0)
- [pubsub](https://godoc.org/github.com/docker/docker/pkg/pubsub): [v17.03.2-ce](https://github.com/moby/moby/releases/tag/v17.03.2-ce) - [pubsub](https://godoc.org/github.com/docker/docker/pkg/pubsub): [v17.03.2-ce](https://github.com/moby/moby/releases/tag/v17.03.2-ce)
- [go-proto-validators](https://github.com/mwitkow/go-proto-validators): [0950a7](https://github.com/mwitkow/go-proto-validators/tree/0950a79900071e9f3f5979b78078c599376422fd)

89
vendor/github.com/mwitkow/go-proto-validators/.gitignore generated vendored Executable file
View File

@ -0,0 +1,89 @@
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Go template
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
### Vim template
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
### Linux template
*~
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# Created by .ignore support plugin (hsz.mobi)

18
vendor/github.com/mwitkow/go-proto-validators/.travis.yml generated vendored Executable file
View File

@ -0,0 +1,18 @@
language: go
sudo: false
go:
- 1.5
- 1.6
before_install:
- ./install_protoc.sh
- export PATH=$PATH:$HOME/soft/protobuf
install:
- go get github.com/stretchr/testify
- go get github.com/gogo/protobuf/protoc-gen-gogo
- go get github.com/golang/protobuf/protoc-gen-go
script:
- make test

201
vendor/github.com/mwitkow/go-proto-validators/LICENSE.txt generated vendored Executable file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

45
vendor/github.com/mwitkow/go-proto-validators/Makefile generated vendored Executable file
View File

@ -0,0 +1,45 @@
# Copyright 2016 Michal Witkowski. All Rights Reserved.
# See LICENSE for licensing terms.
export PATH := ${GOPATH}/bin:${PATH}
install:
@echo "--- Installing govalidators to GOPATH"
go install github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
regenerate_test_gogo:
@echo "Regenerating test .proto files with gogo imports"
(protoc \
--proto_path=${GOPATH}/src \
--proto_path=test \
--gogo_out=test/gogo \
--govalidators_out=gogoimport=true:test/gogo test/*.proto)
regenerate_test_golang:
@echo "--- Regenerating test .proto files with golang imports"
(protoc \
--proto_path=${GOPATH}/src \
--proto_path=test \
--go_out=test/golang \
--govalidators_out=test/golang test/*.proto)
regenerate_example: install
@echo "--- Regenerating example directory"
(protoc \
--proto_path=${GOPATH}/src \
--proto_path=. \
--go_out=. \
--govalidators_out=. examples/*.proto)
test: install regenerate_test_gogo regenerate_test_golang
@echo "Running tests"
go test -v ./...
regenerate:
@echo "--- Regenerating validator.proto"
(protoc \
--proto_path=${GOPATH}/src \
--proto_path=${GOPATH}/src/github.com/gogo/protobuf/protobuf \
--proto_path=. \
--gogo_out=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor:. \
validator.proto)

132
vendor/github.com/mwitkow/go-proto-validators/README.md generated vendored Executable file
View File

@ -0,0 +1,132 @@
# Golang ProtoBuf Validator Compiler
[![Travis Build](https://travis-ci.org/mwitkow/go-proto-validators.svg)](https://travis-ci.org/mwitkow/go-proto-validators)
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
A `protoc` plugin that generates `Validate() error` functions on Go proto `struct`s based on field options inside `.proto`
files. The validation functions are code-generated and thus don't suffer on performance from tag-based reflection on
deeply-nested messages.
## Paint me a code picture
Let's take the following `proto3` snippet:
```proto
syntax = "proto3";
package validator.examples;
import "github.com/mwitkow/go-proto-validators/validator.proto";
message InnerMessage {
// some_integer can only be in range (1, 100).
int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}];
// some_float can only be in range (0;1).
double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}];
}
message OuterMessage {
// important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).
string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
// proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.
InnerMessage inner = 2 [(validator.field) = {msg_exists : true}];
}
```
First, the **`required` keyword is back** for `proto3`, under the guise of `msg_exists`. The painful `if-nil` checks are taken care of!
Second, the expected values in fields are now part of the contract `.proto` file. No more hunting down conditions in code!
Third, the generated code is understandable and has clear understandable error messages. Take a look:
```go
func (this *InnerMessage) Validate() error {
if !(this.SomeInteger > 0) {
return fmt.Errorf("validation error: InnerMessage.SomeInteger must be greater than '0'")
}
if !(this.SomeInteger < 100) {
return fmt.Errorf("validation error: InnerMessage.SomeInteger must be less than '100'")
}
if !(this.SomeFloat >= 0) {
return fmt.Errorf("validation error: InnerMessage.SomeFloat must be greater than or equal to '0'")
}
if !(this.SomeFloat <= 1) {
return fmt.Errorf("validation error: InnerMessage.SomeFloat must be less than or equal to '1'")
}
return nil
}
var _regex_OuterMessage_ImportantString = regexp.MustCompile("^[a-z]{2,5}$")
func (this *OuterMessage) Validate() error {
if !_regex_OuterMessage_ImportantString.MatchString(this.ImportantString) {
return fmt.Errorf("validation error: OuterMessage.ImportantString must conform to regex '^[a-z]{2,5}$'")
}
if nil == this.Inner {
return fmt.Errorf("validation error: OuterMessage.Inner message must exist")
}
if this.Inner != nil {
if err := validators.CallValidatorIfExists(this.Inner); err != nil {
return err
}
}
return nil
}
```
## Installing and using
The `protoc` compiler expects to find plugins named `proto-gen-XYZ` on the execution `$PATH`. So first:
```sh
export PATH=${PATH}:${GOPATH}/bin
```
Then, do the usual
```sh
go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
```
Your `protoc` builds probably look very simple like:
```sh
protoc \
--proto_path=. \
--go_out=. \
*.proto
```
That's fine, until you encounter `.proto` includes. Because `go-proto-validators` uses field options inside the `.proto`
files themselves, it's `.proto` definition (and the Google `descriptor.proto` itself) need to on the `protoc` include
path. Hence the above becomes:
```sh
protoc \
--proto_path=${GOPATH}/src \
--proto_path=${GOPATH}/src/github.com/google/protobuf/src \
--proto_path=. \
--go_out=. \
--govalidators_out=. \
*.proto
```
Or with gogo protobufs:
```sh
protoc \
--proto_path=${GOPATH}/src \
--proto_path=${GOPATH}/src/github.com/gogo/protobuf/protobuf \
--proto_path=. \
--gogo_out=. \
--govalidators_out=gogoimport=true:. \
*.proto
```
Basically the magical incantation (apart from includes) is the `--govalidators_out`. That triggers the
`protoc-gen-govalidators` plugin to generate `mymessage.validator.pb.go`. That's it :)
###License
`go-proto-validators` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.

View File

@ -0,0 +1 @@
!*.pb.go

View File

@ -0,0 +1,83 @@
// Code generated by protoc-gen-go.
// source: examples/nested.proto
// DO NOT EDIT!
/*
Package validator_examples is a generated protocol buffer package.
It is generated from these files:
examples/nested.proto
It has these top-level messages:
InnerMessage
OuterMessage
*/
package validator_examples
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/mwitkow/go-proto-validators"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
const _ = proto.ProtoPackageIsVersion1
type InnerMessage struct {
// some_integer can only be in range (1, 100).
SomeInteger int32 `protobuf:"varint,1,opt,name=some_integer,json=someInteger" json:"some_integer,omitempty"`
}
func (m *InnerMessage) Reset() { *m = InnerMessage{} }
func (m *InnerMessage) String() string { return proto.CompactTextString(m) }
func (*InnerMessage) ProtoMessage() {}
func (*InnerMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type OuterMessage struct {
// important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).
ImportantString string `protobuf:"bytes,1,opt,name=important_string,json=importantString" json:"important_string,omitempty"`
// proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.
Inner *InnerMessage `protobuf:"bytes,2,opt,name=inner" json:"inner,omitempty"`
}
func (m *OuterMessage) Reset() { *m = OuterMessage{} }
func (m *OuterMessage) String() string { return proto.CompactTextString(m) }
func (*OuterMessage) ProtoMessage() {}
func (*OuterMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *OuterMessage) GetInner() *InnerMessage {
if m != nil {
return m.Inner
}
return nil
}
func init() {
proto.RegisterType((*InnerMessage)(nil), "validator.examples.InnerMessage")
proto.RegisterType((*OuterMessage)(nil), "validator.examples.OuterMessage")
}
var fileDescriptor0 = []byte{
// 245 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0xad, 0x48, 0xcc,
0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0xcf, 0x4b, 0x2d, 0x2e, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f,
0xc9, 0x17, 0x12, 0x2a, 0x4b, 0xcc, 0xc9, 0x4c, 0x49, 0x2c, 0xc9, 0x2f, 0xd2, 0x83, 0x29, 0x90,
0x32, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0xcf, 0x2d, 0xcf, 0x2c,
0xc9, 0xce, 0x2f, 0xd7, 0x4f, 0xcf, 0xd7, 0x05, 0x6b, 0xd0, 0x85, 0xab, 0x2f, 0xd6, 0x47, 0x68,
0x05, 0x4b, 0x29, 0x59, 0x73, 0xf1, 0x78, 0xe6, 0xe5, 0xa5, 0x16, 0xf9, 0xa6, 0x16, 0x17, 0x27,
0xa6, 0xa7, 0x0a, 0x69, 0x73, 0xf1, 0x14, 0xe7, 0xe7, 0xa6, 0xc6, 0x67, 0xe6, 0x95, 0xa4, 0xa6,
0xa7, 0x16, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x3a, 0x71, 0x3c, 0xba, 0x2f, 0xcf, 0x22, 0xc0,
0x20, 0x91, 0x12, 0xc4, 0x0d, 0x92, 0xf5, 0x84, 0x48, 0x2a, 0xf5, 0x32, 0x72, 0xf1, 0xf8, 0x97,
0x96, 0x20, 0x74, 0xdb, 0x72, 0x09, 0x64, 0xe6, 0x16, 0xe4, 0x17, 0x95, 0x24, 0xe6, 0x95, 0xc4,
0x17, 0x97, 0x14, 0x65, 0xe6, 0xa5, 0x83, 0x4d, 0xe0, 0x74, 0x12, 0x02, 0x9a, 0xc0, 0xc7, 0xc5,
0x13, 0x17, 0x9d, 0xa8, 0x5b, 0x15, 0x5b, 0x6d, 0xa4, 0x63, 0x5a, 0xab, 0x12, 0xc4, 0x0f, 0x57,
0x1b, 0x0c, 0x56, 0x2a, 0x64, 0xc7, 0xc5, 0x9a, 0x09, 0x72, 0x8c, 0x04, 0x13, 0x50, 0x0f, 0xb7,
0x91, 0x82, 0x1e, 0xa6, 0x47, 0xf5, 0x90, 0x5d, 0xeb, 0xc4, 0x06, 0x34, 0x15, 0xa8, 0x36, 0x08,
0xa2, 0x2d, 0x89, 0x0d, 0xec, 0x27, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x08, 0x68, 0xd9,
0xd5, 0x38, 0x01, 0x00, 0x00,
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package validator.examples;
import "github.com/mwitkow/go-proto-validators/validator.proto";
message InnerMessage {
// some_integer can only be in range (1, 100).
int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}];
}
message OuterMessage {
// important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).
string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
// proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.
InnerMessage inner = 2 [(validator.field) = {msg_exists : true}];
}

View File

@ -0,0 +1,54 @@
// Code generated by protoc-gen-gogo.
// source: examples/nested.proto
// DO NOT EDIT!
/*
Package validator_examples is a generated protocol buffer package.
It is generated from these files:
examples/nested.proto
It has these top-level messages:
InnerMessage
OuterMessage
*/
package validator_examples
import regexp "regexp"
import fmt "fmt"
import github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators"
import proto "github.com/golang/protobuf/proto"
import math "math"
import _ "github.com/mwitkow/go-proto-validators"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
func (this *InnerMessage) Validate() error {
if !(this.SomeInteger > 0) {
return fmt.Errorf("validation error: InnerMessage.SomeInteger must be greater than '0'")
}
if !(this.SomeInteger < 100) {
return fmt.Errorf("validation error: InnerMessage.SomeInteger must be less than '100'")
}
return nil
}
var _regex_OuterMessage_ImportantString = regexp.MustCompile("^[a-z]{2,5}$")
func (this *OuterMessage) Validate() error {
if !_regex_OuterMessage_ImportantString.MatchString(this.ImportantString) {
return fmt.Errorf("validation error: OuterMessage.ImportantString must conform to regex " + "^[a-z]{2,5}$")
}
if nil == this.Inner {
return fmt.Errorf("validation error: OuterMessage.Inner message must exist")
}
if this.Inner != nil {
if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Inner); err != nil {
return err
}
}
return nil
}

39
vendor/github.com/mwitkow/go-proto-validators/helper.go generated vendored Executable file
View File

@ -0,0 +1,39 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package validator
import "strings"
// Validator is a general interface that allows a message to be validated.
type Validator interface {
Validate() error
}
func CallValidatorIfExists(candidate interface{}) error {
if validator, ok := candidate.(Validator); ok {
return validator.Validate()
}
return nil
}
type fieldError struct {
fieldStack []string
nestedErr error
}
func (f *fieldError) Error() string {
return "invalid field " + strings.Join(f.fieldStack, ".") + ": " + f.nestedErr.Error()
}
// FieldError wraps a given Validator error providing a message call stack.
func FieldError(fieldName string, err error) error {
if fErr, ok := err.(*fieldError); ok {
fErr.fieldStack = append([]string{fieldName}, fErr.fieldStack...)
return err
}
return &fieldError{
fieldStack: []string{fieldName},
nestedErr: err,
}
}

View File

@ -0,0 +1,22 @@
#!/bin/bash
# Copyright 2016 Michal Witkowski. All Rights Reserved.
# See LICENSE for licensing terms.
#
# This script installs protobuf compiler `protoc` into PATH.
version=${PROTOBUF_VERSION:-"3.0.0-beta-2"}
dst_dir="${HOME}/soft/protobuf"
# Fail on issues.
set -e
echo "Downloading and installing protoc ${version}"
mkdir -p ${dst_dir}
wget https://github.com/google/protobuf/releases/download/v${version}/protoc-${version}-linux-x86_64.zip -O ${dst_dir}/dist.zip
cd ${dst_dir}
unzip -o dist.zip
echo "Proto in \$PROTOBUF_DIR=${PROTOBUF_DIR}"

View File

@ -0,0 +1,591 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
/*
The validator plugin generates a Validate method for each message.
By default, if none of the message's fields are annotated with the gogo validator annotation, it returns a nil.
In case some of the fields are annotated, the Validate function returns nil upon sucessful validation, or an error
describing why the validation failed.
The Validate method is called recursively for all submessage of the message.
TODO(michal): ADD COMMENTS.
Equal is enabled using the following extensions:
- equal
- equal_all
While VerboseEqual is enable dusing the following extensions:
- verbose_equal
- verbose_equal_all
The equal plugin also generates a test given it is enabled using one of the following extensions:
- testgen
- testgen_all
Let us look at:
github.com/gogo/protobuf/test/example/example.proto
Btw all the output can be seen at:
github.com/gogo/protobuf/test/example/*
The following message:
given to the equal plugin, will generate the following code:
and the following test code:
*/
package plugin
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
"github.com/gogo/protobuf/gogoproto"
"github.com/gogo/protobuf/proto"
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
"github.com/gogo/protobuf/vanity"
"github.com/mwitkow/go-proto-validators"
)
type plugin struct {
*generator.Generator
generator.PluginImports
regexPkg generator.Single
fmtPkg generator.Single
protoPkg generator.Single
validatorPkg generator.Single
useGogoImport bool
}
func NewPlugin(useGogoImport bool) generator.Plugin {
return &plugin{useGogoImport: useGogoImport}
}
func (p *plugin) Name() string {
return "validator"
}
func (p *plugin) Init(g *generator.Generator) {
p.Generator = g
}
func (p *plugin) Generate(file *generator.FileDescriptor) {
if !p.useGogoImport {
vanity.TurnOffGogoImport(file.FileDescriptorProto)
}
p.PluginImports = generator.NewPluginImports(p.Generator)
p.regexPkg = p.NewImport("regexp")
p.fmtPkg = p.NewImport("fmt")
p.validatorPkg = p.NewImport("github.com/mwitkow/go-proto-validators")
for _, msg := range file.Messages() {
if msg.DescriptorProto.GetOptions().GetMapEntry() {
continue
}
p.generateRegexVars(file, msg)
if gogoproto.IsProto3(file.FileDescriptorProto) {
p.generateProto3Message(file, msg)
} else {
p.generateProto2Message(file, msg)
}
}
}
func getFieldValidatorIfAny(field *descriptor.FieldDescriptorProto) *validator.FieldValidator {
if field.Options != nil {
v, err := proto.GetExtension(field.Options, validator.E_Field)
if err == nil && v.(*validator.FieldValidator) != nil {
return (v.(*validator.FieldValidator))
}
}
return nil
}
func (p *plugin) isSupportedInt(field *descriptor.FieldDescriptorProto) bool {
switch *(field.Type) {
case descriptor.FieldDescriptorProto_TYPE_INT32, descriptor.FieldDescriptorProto_TYPE_INT64:
return true
case descriptor.FieldDescriptorProto_TYPE_UINT32, descriptor.FieldDescriptorProto_TYPE_UINT64:
return true
case descriptor.FieldDescriptorProto_TYPE_SINT32, descriptor.FieldDescriptorProto_TYPE_SINT64:
return true
}
return false
}
func (p *plugin) isSupportedFloat(field *descriptor.FieldDescriptorProto) bool {
switch *(field.Type) {
case descriptor.FieldDescriptorProto_TYPE_FLOAT, descriptor.FieldDescriptorProto_TYPE_DOUBLE:
return true
case descriptor.FieldDescriptorProto_TYPE_FIXED32, descriptor.FieldDescriptorProto_TYPE_FIXED64:
return true
case descriptor.FieldDescriptorProto_TYPE_SFIXED32, descriptor.FieldDescriptorProto_TYPE_SFIXED64:
return true
}
return false
}
func (p *plugin) generateRegexVars(file *generator.FileDescriptor, message *generator.Descriptor) {
ccTypeName := generator.CamelCaseSlice(message.TypeName())
for _, field := range message.Field {
validator := getFieldValidatorIfAny(field)
if validator != nil && validator.Regex != nil {
fieldName := p.GetFieldName(message, field)
p.P(`var `, p.regexName(ccTypeName, fieldName), ` = `, p.regexPkg.Use(), `.MustCompile(`, "`", *validator.Regex, "`", `)`)
}
}
}
func (p *plugin) generateProto2Message(file *generator.FileDescriptor, message *generator.Descriptor) {
ccTypeName := generator.CamelCaseSlice(message.TypeName())
p.P(`func (this *`, ccTypeName, `) Validate() error {`)
p.In()
for _, field := range message.Field {
fieldName := p.GetFieldName(message, field)
fieldValidator := getFieldValidatorIfAny(field)
if fieldValidator == nil && !field.IsMessage() {
continue
}
if p.validatorWithMessageExists(fieldValidator) {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a proto2 message, validator.msg_exists has no effect\n", ccTypeName, fieldName)
}
variableName := "this." + fieldName
repeated := field.IsRepeated()
nullable := gogoproto.IsNullable(field)
// For proto2 syntax, only Gogo generates non-pointer fields
nonpointer := gogoproto.ImportsGoGoProto(file.FileDescriptorProto) && !gogoproto.IsNullable(field)
if repeated {
p.generateRepeatedCountValidator(variableName, ccTypeName, fieldName, fieldValidator)
if field.IsMessage() || p.validatorWithNonRepeatedConstraint(fieldValidator) {
p.P(`for _, item := range `, variableName, `{`)
p.In()
variableName = "item"
}
} else if nullable {
p.P(`if `, variableName, ` != nil {`)
p.In()
if !field.IsBytes() {
variableName = "*(" + variableName + ")"
}
} else if nonpointer {
// can use the field directly
} else if !field.IsMessage() {
variableName = `this.Get` + fieldName + `()`
}
if !repeated && fieldValidator != nil {
if fieldValidator.RepeatedCountMin != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is not repeated, validator.min_elts has no effects\n", ccTypeName, fieldName)
}
if fieldValidator.RepeatedCountMax != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is not repeated, validator.max_elts has no effects\n", ccTypeName, fieldName)
}
}
if field.IsString() {
p.generateStringValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if p.isSupportedInt(field) {
p.generateIntValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if p.isSupportedFloat(field) {
p.generateFloatValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if field.IsBytes() {
p.generateLengthValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if field.IsMessage() {
if repeated && nullable {
variableName = "*(item)"
}
p.P(`if err := `, p.validatorPkg.Use(), `.CallValidatorIfExists(&(`, variableName, `)); err != nil {`)
p.In()
p.P(`return `, p.validatorPkg.Use(), `.FieldError("`, fieldName, `", err)`)
p.Out()
p.P(`}`)
}
if repeated {
// end the repeated loop
if field.IsMessage() || p.validatorWithNonRepeatedConstraint(fieldValidator) {
// This internal 'if' cannot be refactored as it would change semantics with respect to the corresponding prelude 'if's
p.Out()
p.P(`}`)
}
} else if nullable {
// end the if around nullable
p.Out()
p.P(`}`)
}
}
p.P(`return nil`)
p.Out()
p.P(`}`)
}
func (p *plugin) generateProto3Message(file *generator.FileDescriptor, message *generator.Descriptor) {
ccTypeName := generator.CamelCaseSlice(message.TypeName())
p.P(`func (this *`, ccTypeName, `) Validate() error {`)
p.In()
for _, field := range message.Field {
fieldValidator := getFieldValidatorIfAny(field)
if fieldValidator == nil && !field.IsMessage() {
continue
}
isOneOf := field.OneofIndex != nil
fieldName := p.GetOneOfFieldName(message, field)
variableName := "this." + fieldName
repeated := field.IsRepeated()
// Golang's proto3 has no concept of unset primitive fields
nullable := (gogoproto.IsNullable(field) || !gogoproto.ImportsGoGoProto(file.FileDescriptorProto)) && field.IsMessage()
if p.fieldIsProto3Map(file, message, field) {
p.P(`// Validation of proto3 map<> fields is unsupported.`)
continue
}
if isOneOf {
p.In()
oneOfName := p.GetFieldName(message, field)
oneOfType := p.OneOfTypeName(message, field)
//if x, ok := m.GetType().(*OneOfMessage3_OneInt); ok {
p.P(`if oneOfNester, ok := this.Get` + oneOfName + `().(* ` + oneOfType + `); ok {`)
variableName = "oneOfNester." + p.GetOneOfFieldName(message, field)
}
if repeated {
p.generateRepeatedCountValidator(variableName, ccTypeName, fieldName, fieldValidator)
if field.IsMessage() || p.validatorWithNonRepeatedConstraint(fieldValidator) {
p.P(`for _, item := range `, variableName, `{`)
p.In()
variableName = "item"
}
} else if fieldValidator != nil {
if fieldValidator.RepeatedCountMin != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is not repeated, validator.min_elts has no effects\n", ccTypeName, fieldName)
}
if fieldValidator.RepeatedCountMax != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is not repeated, validator.max_elts has no effects\n", ccTypeName, fieldName)
}
}
if field.IsString() {
p.generateStringValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if p.isSupportedInt(field) {
p.generateIntValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if p.isSupportedFloat(field) {
p.generateFloatValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if field.IsBytes() {
p.generateLengthValidator(variableName, ccTypeName, fieldName, fieldValidator)
} else if field.IsMessage() {
if p.validatorWithMessageExists(fieldValidator) {
if nullable && !repeated {
p.P(`if nil == `, variableName, `{`)
p.In()
p.P(`return `, p.validatorPkg.Use(), `.FieldError("`, fieldName, `",`, p.fmtPkg.Use(), `.Errorf("message must exist"))`)
p.Out()
p.P(`}`)
} else if repeated {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is repeated, validator.msg_exists has no effect\n", ccTypeName, fieldName)
} else if !nullable {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a nullable=false, validator.msg_exists has no effect\n", ccTypeName, fieldName)
}
}
if nullable {
p.P(`if `, variableName, ` != nil {`)
p.In()
} else {
// non-nullable fields in proto3 store actual structs, we need pointers to operate on interfaces
variableName = "&(" + variableName + ")"
}
p.P(`if err := `, p.validatorPkg.Use(), `.CallValidatorIfExists(`, variableName, `); err != nil {`)
p.In()
p.P(`return `, p.validatorPkg.Use(), `.FieldError("`, fieldName, `", err)`)
p.Out()
p.P(`}`)
if nullable {
p.Out()
p.P(`}`)
}
}
if repeated && (field.IsMessage() || p.validatorWithNonRepeatedConstraint(fieldValidator)) {
// end the repeated loop
p.Out()
p.P(`}`)
}
if isOneOf {
// end the oneof if statement
p.Out()
p.P(`}`)
}
}
p.P(`return nil`)
p.Out()
p.P(`}`)
}
func (p *plugin) generateIntValidator(variableName string, ccTypeName string, fieldName string, fv *validator.FieldValidator) {
if fv.IntGt != nil {
p.P(`if !(`, variableName, ` > `, fv.IntGt, `) {`)
p.In()
errorStr := fmt.Sprintf(`be greater than '%d'`, fv.GetIntGt())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.IntLt != nil {
p.P(`if !(`, variableName, ` < `, fv.IntLt, `) {`)
p.In()
errorStr := fmt.Sprintf(`be less than '%d'`, fv.GetIntLt())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
}
func (p *plugin) generateLengthValidator(variableName string, ccTypeName string, fieldName string, fv *validator.FieldValidator) {
if fv.LengthGt != nil {
p.P(`if !( len(`, variableName, `) > `, fv.LengthGt, `) {`)
p.In()
errorStr := fmt.Sprintf(`length be greater than '%d'`, fv.GetLengthGt())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.LengthLt != nil {
p.P(`if !( len(`, variableName, `) < `, fv.LengthLt, `) {`)
p.In()
errorStr := fmt.Sprintf(`length be less than '%d'`, fv.GetLengthLt())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.LengthEq != nil {
p.P(`if !( len(`, variableName, `) == `, fv.LengthEq, `) {`)
p.In()
errorStr := fmt.Sprintf(`length be not equal '%d'`, fv.GetLengthEq())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
}
func (p *plugin) generateFloatValidator(variableName string, ccTypeName string, fieldName string, fv *validator.FieldValidator) {
upperIsStrict := true
lowerIsStrict := true
// First check for incompatible constraints (i.e flt_lt & flt_lte both defined, etc) and determine the real limits.
if fv.FloatEpsilon != nil && fv.FloatLt == nil && fv.FloatGt == nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v has no 'float_lt' or 'float_gt' field so setting 'float_epsilon' has no effect.", ccTypeName, fieldName)
}
if fv.FloatLt != nil && fv.FloatLte != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v has both 'float_lt' and 'float_lte' constraints, only the strictest will be used.", ccTypeName, fieldName)
strictLimit := fv.GetFloatLt()
if fv.FloatEpsilon != nil {
strictLimit += fv.GetFloatEpsilon()
}
if fv.GetFloatLte() < strictLimit {
upperIsStrict = false
}
} else if fv.FloatLte != nil {
upperIsStrict = false
}
if fv.FloatGt != nil && fv.FloatGte != nil {
fmt.Fprintf(os.Stderr, "WARNING: field %v.%v has both 'float_gt' and 'float_gte' constraints, only the strictest will be used.", ccTypeName, fieldName)
strictLimit := fv.GetFloatGt()
if fv.FloatEpsilon != nil {
strictLimit -= fv.GetFloatEpsilon()
}
if fv.GetFloatGte() > strictLimit {
lowerIsStrict = false
}
} else if fv.FloatGte != nil {
lowerIsStrict = false
}
// Generate the constraint checking code.
errorStr := ""
compareStr := ""
if fv.FloatGt != nil || fv.FloatGte != nil {
compareStr = fmt.Sprint(`if !(`, variableName)
if lowerIsStrict {
errorStr = fmt.Sprintf(`be strictly greater than '%g'`, fv.GetFloatGt())
if fv.FloatEpsilon != nil {
errorStr += fmt.Sprintf(` with a tolerance of '%g'`, fv.GetFloatEpsilon())
compareStr += fmt.Sprint(` + `, fv.GetFloatEpsilon())
}
compareStr += fmt.Sprint(` > `, fv.GetFloatGt(), `) {`)
} else {
errorStr = fmt.Sprintf(`be greater than or equal to '%g'`, fv.GetFloatGte())
compareStr += fmt.Sprint(` >= `, fv.GetFloatGte(), `) {`)
}
p.P(compareStr)
p.In()
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.FloatLt != nil || fv.FloatLte != nil {
compareStr = fmt.Sprint(`if !(`, variableName)
if upperIsStrict {
errorStr = fmt.Sprintf(`be strictly lower than '%g'`, fv.GetFloatLt())
if fv.FloatEpsilon != nil {
errorStr += fmt.Sprintf(` with a tolerance of '%g'`, fv.GetFloatEpsilon())
compareStr += fmt.Sprint(` - `, fv.GetFloatEpsilon())
}
compareStr += fmt.Sprint(` < `, fv.GetFloatLt(), `) {`)
} else {
errorStr = fmt.Sprintf(`be lower than or equal to '%g'`, fv.GetFloatLte())
compareStr += fmt.Sprint(` <= `, fv.GetFloatLte(), `) {`)
}
p.P(compareStr)
p.In()
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
}
func (p *plugin) generateStringValidator(variableName string, ccTypeName string, fieldName string, fv *validator.FieldValidator) {
if fv.Regex != nil {
p.P(`if !`, p.regexName(ccTypeName, fieldName), `.MatchString(`, variableName, `) {`)
p.In()
errorStr := "be a string conforming to regex " + strconv.Quote(fv.GetRegex())
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.StringNotEmpty != nil && fv.GetStringNotEmpty() {
p.P(`if `, variableName, ` == "" {`)
p.In()
errorStr := "not be an empty string"
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
p.generateLengthValidator(variableName, ccTypeName, fieldName, fv)
}
func (p *plugin) generateRepeatedCountValidator(variableName string, ccTypeName string, fieldName string, fv *validator.FieldValidator) {
if fv == nil {
return
}
if fv.RepeatedCountMin != nil {
compareStr := fmt.Sprint(`if len(`, variableName, `) < `, fv.GetRepeatedCountMin(), ` {`)
p.P(compareStr)
p.In()
errorStr := fmt.Sprint(`contain at least `, fv.GetRepeatedCountMin(), ` elements`)
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
if fv.RepeatedCountMax != nil {
compareStr := fmt.Sprint(`if len(`, variableName, `) > `, fv.GetRepeatedCountMax(), ` {`)
p.P(compareStr)
p.In()
errorStr := fmt.Sprint(`contain at most `, fv.GetRepeatedCountMax(), ` elements`)
p.generateErrorString(variableName, fieldName, errorStr, fv)
p.Out()
p.P(`}`)
}
}
func (p *plugin) generateErrorString(variableName string, fieldName string, specificError string, fv *validator.FieldValidator) {
if fv.GetHumanError() == "" {
p.P(`return `, p.validatorPkg.Use(), `.FieldError("`, fieldName, `",`, p.fmtPkg.Use(), ".Errorf(`value '%v' must ", specificError, "`", `, `, variableName, `))`)
} else {
p.P(`return `, p.validatorPkg.Use(), `.FieldError("`, fieldName, `",`, p.fmtPkg.Use(), ".Errorf(`", fv.GetHumanError(), "`))")
}
}
func (p *plugin) fieldIsProto3Map(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) bool {
// Context from descriptor.proto
// Whether the message is an automatically generated map entry type for the
// maps field.
//
// For maps fields:
// map<KeyType, ValueType> map_field = 1;
// The parsed descriptor looks like:
// message MapFieldEntry {
// option map_entry = true;
// optional KeyType key = 1;
// optional ValueType value = 2;
// }
// repeated MapFieldEntry map_field = 1;
//
// Implementations may choose not to generate the map_entry=true message, but
// use a native map in the target language to hold the keys and values.
// The reflection APIs in such implementions still need to work as
// if the field is a repeated message field.
//
// NOTE: Do not set the option in .proto files. Always use the maps syntax
// instead. The option should only be implicitly set by the proto compiler
// parser.
if field.GetType() != descriptor.FieldDescriptorProto_TYPE_MESSAGE || !field.IsRepeated() {
return false
}
typeName := field.GetTypeName()
var msg *descriptor.DescriptorProto
if strings.HasPrefix(typeName, ".") {
// Fully qualified case, look up in global map, must work or fail badly.
msg = p.ObjectNamed(field.GetTypeName()).(*generator.Descriptor).DescriptorProto
} else {
// Nested, relative case.
msg = file.GetNestedMessage(message.DescriptorProto, field.GetTypeName())
}
return msg.GetOptions().GetMapEntry()
}
func (p *plugin) validatorWithAnyConstraint(fv *validator.FieldValidator) bool {
if fv == nil {
return false
}
// Need to use reflection in order to be future-proof for new types of constraints.
v := reflect.ValueOf(fv)
for i := 0; i < v.NumField(); i++ {
if v.Field(i).Interface() != nil {
return true
}
}
return false
}
func (p *plugin) validatorWithMessageExists(fv *validator.FieldValidator) bool {
return fv != nil && fv.MsgExists != nil && *(fv.MsgExists)
}
func (p *plugin) validatorWithNonRepeatedConstraint(fv *validator.FieldValidator) bool {
if fv == nil {
return false
}
// Need to use reflection in order to be future-proof for new types of constraints.
v := reflect.ValueOf(*fv)
for i := 0; i < v.NumField(); i++ {
if v.Type().Field(i).Name != "RepeatedCountMin" && v.Type().Field(i).Name != "RepeatedCountMax" && v.Field(i).Pointer() != 0 {
return true
}
}
return false
}
func (p *plugin) regexName(ccTypeName string, fieldName string) string {
return "_regex_" + ccTypeName + "_" + fieldName
}

View File

@ -0,0 +1,67 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package main
import (
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
validator_plugin "github.com/mwitkow/go-proto-validators/plugin"
)
func main() {
gen := generator.New()
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
gen.Error(err, "reading input")
}
if err := proto.Unmarshal(data, gen.Request); err != nil {
gen.Error(err, "parsing input proto")
}
if len(gen.Request.FileToGenerate) == 0 {
gen.Fail("no files to generate")
}
useGogoImport := false
// Match parsing algorithm from Generator.CommandLineParameters
for _, parameter := range strings.Split(gen.Request.GetParameter(), ",") {
kvp := strings.SplitN(parameter, "=", 2)
// We only care about key-value pairs where the key is "gogoimport"
if len(kvp) != 2 || kvp[0] != "gogoimport" {
continue
}
useGogoImport, err = strconv.ParseBool(kvp[1])
if err != nil {
gen.Error(err, "parsing gogoimport option")
}
}
gen.CommandLineParameters(gen.Request.GetParameter())
gen.WrapTypes()
gen.SetPackageNames()
gen.BuildTypeNameMap()
gen.GeneratePlugin(validator_plugin.NewPlugin(useGogoImport))
for i := 0; i < len(gen.Response.File); i++ {
gen.Response.File[i].Name = proto.String(strings.Replace(*gen.Response.File[i].Name, ".pb.go", ".validator.pb.go", -1))
}
// Send back the results.
data, err = proto.Marshal(gen.Response)
if err != nil {
gen.Error(err, "failed to marshal output proto")
}
_, err = os.Stdout.Write(data)
if err != nil {
gen.Error(err, "failed to write output proto")
}
}

View File

@ -0,0 +1,2 @@
*.pb.go
Makefile

View File

@ -0,0 +1,488 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package validatortest
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
var (
stableBytes = make([]byte, 12)
)
func buildProto3(someString string, someInt uint32, identifier string, someValue int64, someDoubleStrict float64,
someFloatStrict float32, someDouble float64, someFloat float32, nonEmptyString string, repeatedCount uint32, someStringLength string, someBytes []byte) *ValidatorMessage3 {
goodEmbeddedProto3 := &ValidatorMessage3_Embedded{
Identifier: identifier,
SomeValue: someValue,
}
goodProto3 := &ValidatorMessage3{
SomeString: someString,
SomeStringRep: []string{someString, "xyz34"},
SomeStringNoQuotes: someString,
SomeStringUnescaped: someString,
SomeInt: someInt,
SomeIntRep: []uint32{someInt, 12, 13, 14, 15, 16},
SomeIntRepNonNull: []uint32{someInt, 102},
SomeEmbedded: nil,
SomeEmbeddedNonNullable: *goodEmbeddedProto3,
SomeEmbeddedExists: goodEmbeddedProto3,
SomeEmbeddedRep: []*ValidatorMessage3_Embedded{goodEmbeddedProto3},
SomeEmbeddedRepNonNullable: []ValidatorMessage3_Embedded{*goodEmbeddedProto3},
StrictSomeDouble: someDoubleStrict,
StrictSomeDoubleRep: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeDoubleRepNonNull: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeFloat: someFloatStrict,
StrictSomeFloatRep: []float32{someFloatStrict, 0.5, 0.55, 0.6},
StrictSomeFloatRepNonNull: []float32{someFloatStrict, 0.5, 0.55, 0.6},
SomeDouble: someDouble,
SomeDoubleRep: []float64{someDouble, 0.5, 0.55, 0.6},
SomeDoubleRepNonNull: []float64{someDouble, 0.5, 0.55, 0.6},
SomeFloat: someFloat,
SomeFloatRep: []float32{someFloat, 0.5, 0.55, 0.6},
SomeFloatRepNonNull: []float32{someFloat, 0.5, 0.55, 0.6},
SomeNonEmptyString: nonEmptyString,
SomeStringEqReq: someStringLength,
SomeStringLtReq: someStringLength,
SomeStringGtReq: someStringLength,
SomeBytesLtReq: someBytes,
SomeBytesGtReq: someBytes,
SomeBytesEqReq: someBytes,
RepeatedBaseType: []int32{},
}
goodProto3.Repeated = make([]int32, repeatedCount, repeatedCount)
return goodProto3
}
func buildProto2(someString string, someInt uint32, identifier string, someValue int64, someDoubleStrict float64,
someFloatStrict float32, someDouble float64, someFloat float32, nonEmptyString string, repeatedCount uint32, someStringLength string, someBytes []byte) *ValidatorMessage {
goodEmbeddedProto2 := &ValidatorMessage_Embedded{
Identifier: &identifier,
SomeValue: &someValue,
}
goodProto2 := &ValidatorMessage{
StringReq: &someString,
StringReqNonNull: someString,
StringOpt: nil,
StringOptNonNull: someString,
StringUnescaped: &someString,
IntReq: &someInt,
IntReqNonNull: someInt,
IntRep: []uint32{someInt, 12, 13, 14, 15, 16},
IntRepNonNull: []uint32{someInt, 12, 13, 14, 15, 16},
EmbeddedReq: goodEmbeddedProto2,
EmbeddedNonNull: *goodEmbeddedProto2,
EmbeddedRep: []*ValidatorMessage_Embedded{goodEmbeddedProto2},
EmbeddedRepNonNullable: []ValidatorMessage_Embedded{*goodEmbeddedProto2},
StrictSomeDoubleReq: &someDoubleStrict,
StrictSomeDoubleReqNonNull: someDoubleStrict,
StrictSomeDoubleRep: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeDoubleRepNonNull: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeFloatReq: &someFloatStrict,
StrictSomeFloatReqNonNull: someFloatStrict,
StrictSomeFloatRep: []float32{someFloatStrict, 0.5, 0.55, 0.6},
StrictSomeFloatRepNonNull: []float32{someFloatStrict, 0.5, 0.55, 0.6},
SomeDoubleReq: &someDouble,
SomeDoubleReqNonNull: someDouble,
SomeDoubleRep: []float64{someDouble, 0.5, 0.55, 0.6},
SomeDoubleRepNonNull: []float64{someDouble, 0.5, 0.55, 0.6},
SomeFloatReq: &someFloat,
SomeFloatReqNonNull: someFloat,
SomeFloatRep: []float32{someFloat, 0.5, 0.55, 0.6},
SomeFloatRepNonNull: []float32{someFloat, 0.5, 0.55, 0.6},
SomeNonEmptyString: &nonEmptyString,
SomeStringEqReq: &someStringLength,
SomeStringLtReq: &someStringLength,
SomeStringGtReq: &someStringLength,
SomeBytesLtReq: someBytes,
SomeBytesGtReq: someBytes,
SomeBytesEqReq: someBytes,
RepeatedBaseType: []int32{},
}
goodProto2.Repeated = make([]int32, repeatedCount, repeatedCount)
return goodProto2
}
func TestGoodProto3(t *testing.T) {
var err error
goodProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
err = goodProto3.Validate()
if err != nil {
t.Fatalf("unexpected fail in validator: %v", err)
}
}
func TestGoodProto2(t *testing.T) {
var err error
goodProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
err = goodProto2.Validate()
if err != nil {
t.Fatalf("unexpected fail in validator: %v", err)
}
}
func TestStringRegex(t *testing.T) {
tooLong1Proto3 := buildProto3("toolong", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong2Proto3 := buildProto3("-%ab", 11, "bad#", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong2Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong1Proto2 := buildProto2("toolong", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong2Proto2 := buildProto2("-%ab", 11, "bad#", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong2Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestIntLowerBounds(t *testing.T) {
lowerThan10Proto3 := buildProto3("-%ab", 9, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan10Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan10Proto2 := buildProto2("-%ab", 9, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan10Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", -1, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", -1, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestIntUpperBounds(t *testing.T) {
greaterThan100Proto3 := buildProto3("-%ab", 11, "abba", 101, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan100Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan100Proto2 := buildProto2("-%ab", 11, "abba", 101, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan100Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestDoubleStrictLowerBounds(t *testing.T) {
lowerThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.3, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.3, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.300000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan035EpsilonProto3.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
greaterThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.300000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan035EpsilonProto2.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
}
func TestDoubleStrictUpperBounds(t *testing.T) {
greaterThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.70000000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.70000000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.6999999999, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan065EpsilonProto3.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
lowerThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.6999999999, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan065EpsilonProto2.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
}
func TestFloatStrictLowerBounds(t *testing.T) {
lowerThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.2999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.2999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.3000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := greaterThan035EpsilonProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
greaterThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.3000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := greaterThan035EpsilonProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatStrictUpperBounds(t *testing.T) {
greaterThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.7000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.7000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.6999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := lowerThan065EpsilonProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
lowerThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.6999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := lowerThan065EpsilonProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestDoubleNonStrictLowerBounds(t *testing.T) {
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.2499999, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.2499999, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.25, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.25, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestDoubleNonStrictUpperBounds(t *testing.T) {
higherThan1Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75111111, 0.5, "x", 4, "1234567890", stableBytes)
if higherThan1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
higherThan1Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75111111, 0.5, "x", 4, "1234567890", stableBytes)
if higherThan1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatNonStrictLowerBounds(t *testing.T) {
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.2499999, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.2499999, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.25, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.25, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatNonStrictUpperBounds(t *testing.T) {
higherThan1Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75111111, "x", 4, "1234567890", stableBytes)
if higherThan1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
higherThan1Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75111111, "x", 4, "1234567890", stableBytes)
if higherThan1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestStringNonEmpty(t *testing.T) {
emptyStringProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "", 4, "1234567890", stableBytes)
if emptyStringProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
emptyStringProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "", 4, "1234567890", stableBytes)
if emptyStringProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
nonEmptyStringProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := nonEmptyStringProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
nonEmptyStringProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := nonEmptyStringProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestRepeatedEltsCount(t *testing.T) {
notEnoughEltsProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 1, "1234567890", stableBytes)
if notEnoughEltsProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
notEnoughEltsProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 1, "1234567890", stableBytes)
if notEnoughEltsProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooManyEltsProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 14, "1234567890", stableBytes)
if tooManyEltsProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooManyEltsProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 14, "1234567890", stableBytes)
if tooManyEltsProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
validEltsCountProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := validEltsCountProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
validEltsCountProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := validEltsCountProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestMsgExist(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.SomeEmbedded = nil
if err := someProto3.Validate(); err != nil {
t.Fatalf("validate shouldn't fail on missing SomeEmbedded, not annotated")
}
someProto3.SomeEmbeddedExists = nil
if err := someProto3.Validate(); err == nil {
t.Fatalf("expected fail due to lacking SomeEmbeddedExists")
} else if !strings.HasPrefix(err.Error(), "invalid field SomeEmbeddedExists:") {
t.Fatalf("expected fieldError, got '%v'", err)
}
}
func TestNestedError3(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.SomeEmbeddedExists.SomeValue = 101 // should be less than 101
if err := someProto3.Validate(); err == nil {
t.Fatalf("expected fail due to nested SomeEmbeddedExists.SomeValue being wrong")
} else if !strings.HasPrefix(err.Error(), "invalid field SomeEmbeddedExists.SomeValue:") {
t.Fatalf("expected fieldError, got '%v'", err)
}
}
func TestCustomError_Proto3(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.CustomErrorInt = 30
expectedErr := "invalid field CustomErrorInt: My Custom Error"
if err := someProto3.Validate(); err == nil {
t.Fatalf("validate should fail on missing CustomErrorInt")
} else if err.Error() != expectedErr {
t.Fatalf("validation error should be '%s' but was '%s'", expectedErr, err.Error())
}
}
func TestMapAlwaysPassesUntilFixedProperly(t *testing.T) {
example := &ValidatorMapMessage3{}
if err := example.Validate(); err != nil {
t.Fatalf("map validators should always pass")
}
}
func TestOneOf_NestedMessage(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "999", // bad
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_ThreeInt{
ThreeInt: 100, // > 20
},
}
err := example.Validate()
assert.Error(t, err, "nested message in oneof should fail validation on ExternalMsg")
assert.Contains(t, err.Error(), "OneMsg.Identifier", "error must err on the ExternalMsg.Identifier")
}
func TestOneOf_NestedInt(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "abba", // good
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_ThreeInt{
ThreeInt: 19, // > 20
},
}
err := example.Validate()
assert.Error(t, err, "nested message in oneof should fail validation on ThreeInt")
assert.Contains(t, err.Error(), "ThreeInt", "error must err on the ThreeInt.ThreeInt")
}
func TestOneOf_Passes(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "abba", // good
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_FourInt{
FourInt: 101, // > 101
},
}
err := example.Validate()
assert.NoError(t, err, "This message should pass all validation")
}

View File

@ -0,0 +1,512 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package validatortest
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
var (
stableBytes = make([]byte, 12)
)
func buildProto3(someString string, someInt uint32, identifier string, someValue int64, someDoubleStrict float64,
someFloatStrict float32, someDouble float64, someFloat float32, nonEmptyString string, repeatedCount uint32,
someStringLength string, someBytes []byte) *ValidatorMessage3 {
goodEmbeddedProto3 := &ValidatorMessage3_Embedded{
Identifier: identifier,
SomeValue: someValue,
}
goodProto3 := &ValidatorMessage3{
SomeString: someString,
SomeStringRep: []string{someString, "xyz34"},
SomeStringNoQuotes: someString,
SomeStringUnescaped: someString,
SomeInt: someInt,
SomeIntRep: []uint32{someInt, 12, 13, 14, 15, 16},
SomeIntRepNonNull: []uint32{someInt, 102},
SomeEmbedded: nil,
SomeEmbeddedNonNullable: goodEmbeddedProto3,
SomeEmbeddedExists: goodEmbeddedProto3,
SomeEmbeddedRep: []*ValidatorMessage3_Embedded{goodEmbeddedProto3},
SomeEmbeddedRepNonNullable: []*ValidatorMessage3_Embedded{goodEmbeddedProto3},
StrictSomeDouble: someDoubleStrict,
StrictSomeDoubleRep: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeDoubleRepNonNull: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeFloat: someFloatStrict,
StrictSomeFloatRep: []float32{someFloatStrict, 0.5, 0.55, 0.6},
StrictSomeFloatRepNonNull: []float32{someFloatStrict, 0.5, 0.55, 0.6},
SomeDouble: someDouble,
SomeDoubleRep: []float64{someDouble, 0.5, 0.55, 0.6},
SomeDoubleRepNonNull: []float64{someDouble, 0.5, 0.55, 0.6},
SomeFloat: someFloat,
SomeFloatRep: []float32{someFloat, 0.5, 0.55, 0.6},
SomeFloatRepNonNull: []float32{someFloat, 0.5, 0.55, 0.6},
SomeNonEmptyString: nonEmptyString,
SomeStringEqReq: someStringLength,
SomeStringLtReq: someStringLength,
SomeStringGtReq: someStringLength,
SomeBytesLtReq: someBytes,
SomeBytesGtReq: someBytes,
SomeBytesEqReq: someBytes,
RepeatedBaseType: []int32{},
}
goodProto3.Repeated = make([]int32, repeatedCount, repeatedCount)
return goodProto3
}
func buildProto2(someString string, someInt uint32, identifier string, someValue int64, someDoubleStrict float64, someFloatStrict float32, someDouble float64, someFloat float32, nonEmptyString string, repeatedCount uint32, someStringLength string, someBytes []byte) *ValidatorMessage {
goodEmbeddedProto2 := &ValidatorMessage_Embedded{
Identifier: &identifier,
SomeValue: &someValue,
}
goodProto2 := &ValidatorMessage{
StringReq: &someString,
StringReqNonNull: &someString,
StringOpt: nil,
StringOptNonNull: &someString,
StringUnescaped: &someString,
IntReq: &someInt,
IntReqNonNull: &someInt,
IntRep: []uint32{someInt, 12, 13, 14, 15, 16},
IntRepNonNull: []uint32{someInt, 12, 13, 14, 15, 16},
EmbeddedReq: goodEmbeddedProto2,
EmbeddedNonNull: goodEmbeddedProto2,
EmbeddedRep: []*ValidatorMessage_Embedded{goodEmbeddedProto2},
EmbeddedRepNonNullable: []*ValidatorMessage_Embedded{goodEmbeddedProto2},
StrictSomeDoubleReq: &someDoubleStrict,
StrictSomeDoubleReqNonNull: &someDoubleStrict,
StrictSomeDoubleRep: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeDoubleRepNonNull: []float64{someDoubleStrict, 0.5, 0.55, 0.6},
StrictSomeFloatReq: &someFloatStrict,
StrictSomeFloatReqNonNull: &someFloatStrict,
StrictSomeFloatRep: []float32{someFloatStrict, 0.5, 0.55, 0.6},
StrictSomeFloatRepNonNull: []float32{someFloatStrict, 0.5, 0.55, 0.6},
SomeDoubleReq: &someDouble,
SomeDoubleReqNonNull: &someDouble,
SomeDoubleRep: []float64{someDouble, 0.5, 0.55, 0.6},
SomeDoubleRepNonNull: []float64{someDouble, 0.5, 0.55, 0.6},
SomeFloatReq: &someFloat,
SomeFloatReqNonNull: &someFloat,
SomeFloatRep: []float32{someFloat, 0.5, 0.55, 0.6},
SomeFloatRepNonNull: []float32{someFloat, 0.5, 0.55, 0.6},
SomeNonEmptyString: &nonEmptyString,
SomeStringEqReq: &someStringLength,
SomeStringLtReq: &someStringLength,
SomeStringGtReq: &someStringLength,
SomeBytesLtReq: someBytes,
SomeBytesGtReq: someBytes,
SomeBytesEqReq: someBytes,
RepeatedBaseType: []int32{},
}
goodProto2.Repeated = make([]int32, repeatedCount, repeatedCount)
return goodProto2
}
func TestGoodProto3(t *testing.T) {
var err error
goodProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
err = goodProto3.Validate()
if err != nil {
t.Fatalf("unexpected fail in validator: %v", err)
}
}
func TestGoodProto2(t *testing.T) {
var err error
goodProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
err = goodProto2.Validate()
if err != nil {
t.Fatalf("unexpected fail in validator: %v", err)
}
}
func TestStringRegex(t *testing.T) {
tooLong1Proto3 := buildProto3("toolong", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong2Proto3 := buildProto3("-%ab", 11, "bad#", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong2Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong1Proto2 := buildProto2("toolong", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooLong2Proto2 := buildProto2("-%ab", 11, "bad#", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if tooLong2Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestIntLowerBounds(t *testing.T) {
lowerThan10Proto3 := buildProto3("-%ab", 9, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan10Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan10Proto2 := buildProto2("-%ab", 9, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan10Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", -1, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", -1, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestIntUpperBounds(t *testing.T) {
greaterThan100Proto3 := buildProto3("-%ab", 11, "abba", 101, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan100Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan100Proto2 := buildProto2("-%ab", 11, "abba", 101, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan100Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
}
func TestDoubleStrictLowerBounds(t *testing.T) {
lowerThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.3, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.3, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.300000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan035EpsilonProto3.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
greaterThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.300000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan035EpsilonProto2.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
}
func TestDoubleStrictUpperBounds(t *testing.T) {
greaterThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.70000000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.70000000001, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.6999999999, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan065EpsilonProto3.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
lowerThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.6999999999, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan065EpsilonProto2.Validate() != nil {
t.Fatalf("unexpected fail in validator")
}
}
func TestFloatStrictLowerBounds(t *testing.T) {
lowerThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.2999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.2999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan035EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan035EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.3000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := greaterThan035EpsilonProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
greaterThan035EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.3000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := greaterThan035EpsilonProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatStrictUpperBounds(t *testing.T) {
greaterThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.7000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
greaterThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.7000001, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if greaterThan065EpsilonProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan065EpsilonProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.6999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := lowerThan065EpsilonProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
lowerThan065EpsilonProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.6999999, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := lowerThan065EpsilonProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestDoubleNonStrictLowerBounds(t *testing.T) {
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.2499999, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.2499999, 0.5, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.25, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.25, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestDoubleNonStrictUpperBounds(t *testing.T) {
higherThan1Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75111111, 0.5, "x", 4, "1234567890", stableBytes)
if higherThan1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
higherThan1Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75111111, 0.5, "x", 4, "1234567890", stableBytes)
if higherThan1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.75, 0.5, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatNonStrictLowerBounds(t *testing.T) {
lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.2499999, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.2499999, "x", 4, "1234567890", stableBytes)
if lowerThan0Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.25, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.25, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestFloatNonStrictUpperBounds(t *testing.T) {
higherThan1Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75111111, "x", 4, "1234567890", stableBytes)
if higherThan1Proto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
higherThan1Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75111111, "x", 4, "1234567890", stableBytes)
if higherThan1Proto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
equalTo0Proto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
equalTo0Proto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.75, "x", 4, "1234567890", stableBytes)
if err := equalTo0Proto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestStringNonEmpty(t *testing.T) {
emptyStringProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "", 4, "1234567890", stableBytes)
if emptyStringProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
emptyStringProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "", 4, "1234567890", stableBytes)
if emptyStringProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
nonEmptyStringProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := nonEmptyStringProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
nonEmptyStringProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := nonEmptyStringProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestRepeatedEltsCount(t *testing.T) {
notEnoughEltsProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 1, "1234567890", stableBytes)
if notEnoughEltsProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
notEnoughEltsProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 1, "1234567890", stableBytes)
if notEnoughEltsProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooManyEltsProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 14, "1234567890", stableBytes)
if tooManyEltsProto3.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
tooManyEltsProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 14, "1234567890", stableBytes)
if tooManyEltsProto2.Validate() == nil {
t.Fatalf("expected fail in validator, but it didn't happen")
}
validEltsCountProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := validEltsCountProto3.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
validEltsCountProto2 := buildProto2("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := validEltsCountProto2.Validate(); err != nil {
t.Fatalf("unexpected fail in validator %v", err)
}
}
func TestMsgExist(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.SomeEmbedded = nil
if err := someProto3.Validate(); err != nil {
t.Fatalf("validate shouldn't fail on missing SomeEmbedded, not annotated")
}
someProto3.SomeEmbeddedExists = nil
if err := someProto3.Validate(); err == nil {
t.Fatalf("expected fail due to lacking SomeEmbeddedExists")
} else if !strings.HasPrefix(err.Error(), "invalid field SomeEmbeddedExists:") {
t.Fatalf("expected fieldError, got '%v'", err)
}
}
func TestStringLengthValidator(t *testing.T) {
StringLengthErrorProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "abc456", stableBytes)
if err := StringLengthErrorProto3.Validate(); err == nil {
t.Fatalf("validate shouldn't fail on error length")
}
StringLengthSuccess := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := StringLengthSuccess.Validate(); err != nil {
t.Fatalf("validate shouldn't fail on equal length")
}
}
func TestBytesLengthValidator(t *testing.T) {
StringLengthErrorProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "abc456", []byte("anc"))
if err := StringLengthErrorProto3.Validate(); err == nil {
t.Fatalf("validate shouldn't fail on error length")
}
StringLengthSuccess := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
if err := StringLengthSuccess.Validate(); err != nil {
t.Fatalf("validate shouldn't fail on equal length")
}
}
func TestNestedError3(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.SomeEmbeddedExists.SomeValue = 101 // should be less than 101
if err := someProto3.Validate(); err == nil {
t.Fatalf("expected fail due to nested SomeEmbeddedNonNullable.SomeValue being wrong")
} else if !strings.HasPrefix(err.Error(), "invalid field SomeEmbeddedNonNullable.SomeValue:") {
t.Fatalf("expected fieldError, got '%v'", err)
}
}
func TestCustomError_Proto3(t *testing.T) {
someProto3 := buildProto3("-%ab", 11, "abba", 99, 0.5, 0.5, 0.5, 0.5, "x", 4, "1234567890", stableBytes)
someProto3.CustomErrorInt = 30
expectedErr := "invalid field CustomErrorInt: My Custom Error"
if err := someProto3.Validate(); err == nil {
t.Fatalf("validate should fail on missing CustomErrorInt")
} else if err.Error() != expectedErr {
t.Fatalf("validation error should be '%s' but was '%s'", expectedErr, err.Error())
}
}
func TestMapAlwaysPassesUntilFixedProperly(t *testing.T) {
example := &ValidatorMapMessage3{}
if err := example.Validate(); err != nil {
t.Fatalf("map validators should always pass")
}
}
func TestOneOf_NestedMessage(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "999", // bad
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_ThreeInt{
ThreeInt: 100, // > 20
},
}
err := example.Validate()
assert.Error(t, err, "nested message in oneof should fail validation on ExternalMsg")
assert.Contains(t, err.Error(), "OneMsg.Identifier", "error must err on the ExternalMsg.Identifier")
}
func TestOneOf_NestedInt(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "abba", // good
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_ThreeInt{
ThreeInt: 19, // > 20
},
}
err := example.Validate()
assert.Error(t, err, "nested message in oneof should fail validation on ThreeInt")
assert.Contains(t, err.Error(), "ThreeInt", "error must err on the ThreeInt.ThreeInt")
}
func TestOneOf_Passes(t *testing.T) {
example := &OneOfMessage3{
SomeInt: 30,
Type: &OneOfMessage3_OneMsg{
OneMsg: &ExternalMsg{
Identifier: "abba", // good
SomeValue: 99, // good
},
},
Something: &OneOfMessage3_FourInt{
FourInt: 101, // > 101
},
}
err := example.Validate()
assert.NoError(t, err, "This message should pass all validation")
}

View File

@ -0,0 +1,78 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
syntax = "proto2";
package validatortest;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/mwitkow/go-proto-validators/validator.proto";
message ValidatorMessage {
// Embedded message test structure.
message Embedded {
optional string Identifier = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
required int64 SomeValue = 2 [(validator.field) = {int_gt: 0, int_lt: 100}];
}
// String regex constraint tests.
required string StringReq = 1 [(validator.field) = {regex: "^.{2,5}$"}];
required string StringReqNonNull = 2 [(validator.field) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false];
optional string StringOpt = 3 [(validator.field) = {regex: "^.{2,5}$"}];
optional string StringOptNonNull = 4 [(validator.field) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false];
required string StringUnescaped = 5 [(validator.field) = {regex: "[\\p{L}\\p{N}]({\\p{L}\\p{N}_- ]{0,28}[\\p{L}\\p{N}])?."}];
// Strict integer inequality constraint tests.
required uint32 IntReq = 6 [(validator.field) = {int_gt: 10}];
required uint32 IntReqNonNull = 7 [(validator.field) = {int_gt: 0}, (gogoproto.nullable) = false];
repeated uint32 IntRep = 8 [(validator.field) = {int_gt: 10}];
repeated uint32 IntRepNonNull = 9 [(validator.field) = {int_gt: 0}];
// Embedded message recursive constraint tests.
required Embedded embeddedReq = 10;
required Embedded embeddedNonNull = 11 [(gogoproto.nullable) = false];
repeated Embedded embeddedRep = 12;
repeated Embedded embeddedRepNonNullable = 13 [(gogoproto.nullable) = false];
// Custom error tests.
optional int32 CustomErrorInt = 16 [(validator.field) = {int_gt: 10, human_error: "My Custom Error"}];
// Strict floating-point inequality constraint tests.
// With this epsilon value, the limits become
// SomeFloat+0.05 > 0.35
// SomeFloat-0.05 < 0.65
required double StrictSomeDoubleReq = 17 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
required double StrictSomeDoubleReqNonNull = 18 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}, (gogoproto.nullable) = false];
repeated double StrictSomeDoubleRep = 19 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated double StrictSomeDoubleRepNonNull = 20 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
required float StrictSomeFloatReq = 21 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
required float StrictSomeFloatReqNonNull = 22 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}, (gogoproto.nullable) = false];
repeated float StrictSomeFloatRep = 23 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated float StrictSomeFloatRepNonNull = 24 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
// Non-strict floating-point inequality constraint tests.
required double SomeDoubleReq = 25 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
required double SomeDoubleReqNonNull = 26 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}, (gogoproto.nullable) = false];
repeated double SomeDoubleRep = 27 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated double SomeDoubleRepNonNull = 28 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
required float SomeFloatReq = 29 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
required float SomeFloatReqNonNull = 30 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}, (gogoproto.nullable) = false];
repeated float SomeFloatRep = 31 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated float SomeFloatRepNonNull = 32 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
// String not-empty constraint tests.
required string SomeNonEmptyString = 33 [(validator.field) = {string_not_empty: true}];
// Repeated base-type without constraint tests.
repeated int32 RepeatedBaseType = 34;
// Repeated element count constraint tests.
repeated int32 Repeated = 35 [(validator.field) = {repeated_count_min: 2, repeated_count_max: 5}];
optional string SomeStringLtReq = 36 [(validator.field) = {length_gt: 2}];
optional string SomeStringGtReq = 37 [(validator.field) = {length_lt: 12}];
optional string SomeStringEqReq = 38 [(validator.field) = {length_eq: 10}];
optional bytes SomeBytesLtReq = 39 [(validator.field) = {length_gt: 5}];
optional bytes SomeBytesGtReq = 40 [(validator.field) = {length_lt: 20}];
optional bytes SomeBytesEqReq = 41 [(validator.field) = {length_eq: 12}];
}

View File

@ -0,0 +1,72 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
syntax = "proto3";
package validatortest;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/mwitkow/go-proto-validators/validator.proto";
message ValidatorMessage3 {
// Embedded message test structure.
message Embedded {
string Identifier = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
int64 SomeValue = 2 [(validator.field) = {int_gt: 0, int_lt: 100}];
}
// String regex constraint tests.
string SomeString = 1 [(validator.field) = {regex: "^.{2,5}$"}];
repeated string SomeStringRep = 2 [(validator.field) = {regex: "^.{2,5}$"}];
string SomeStringNoQuotes = 3 [(validator.field) = {regex: "^[^\"]{2,5}$"}];
string SomeStringUnescaped = 4 [(validator.field) = {regex: "[\\p{L}\\p{N}]({\\p{L}\\p{N}_- ]{0,28}[\\p{L}\\p{N}])?."}];
// Strict integer inequality constraint tests.
uint32 SomeInt = 6 [(validator.field) = {int_gt: 10}];
repeated uint32 SomeIntRep = 7 [(validator.field) = {int_gt: 10}];
repeated uint32 SomeIntRepNonNull = 8 [(validator.field) = {int_gt: 10}];
// Embedded message existence and recursive constraint tests.
Embedded someEmbedded = 10;
Embedded someEmbeddedNonNullable = 11 [(gogoproto.nullable) = false];
Embedded someEmbeddedExists = 12 [(validator.field) = {msg_exists : true}];
repeated Embedded someEmbeddedRep = 14;
repeated Embedded someEmbeddedRepNonNullable = 15 [(gogoproto.nullable) = false];
// Custom error tests.
int32 CustomErrorInt = 16 [(validator.field) = {int_lt: 10, human_error: "My Custom Error"}];
// Strict floating-point inequality constraint tests.
// With this epsilon value, the limits become
// SomeFloat+0.05 > 0.35
// SomeFloat-0.05 < 0.65
double StrictSomeDouble = 17 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated double StrictSomeDoubleRep = 19 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated double StrictSomeDoubleRepNonNull = 20 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
float StrictSomeFloat = 21 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated float StrictSomeFloatRep = 22 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
repeated float StrictSomeFloatRepNonNull = 23 [(validator.field) = {float_gt: 0.35, float_lt: 0.65, float_epsilon: 0.05}];
// Non-strict floating-point inequality constraint tests.
double SomeDouble = 24 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated double SomeDoubleRep = 25 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated double SomeDoubleRepNonNull = 26 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
float SomeFloat = 27 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated float SomeFloatRep = 28 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
repeated float SomeFloatRepNonNull = 30 [(validator.field) = {float_gte: 0.25, float_lte: 0.75}];
// String not-empty constraint tests.
string SomeNonEmptyString = 31 [(validator.field) = {string_not_empty: true}];
// Repeated base-type without constraint tests.
repeated int32 RepeatedBaseType = 32;
// Repeated element count constraint tests.
repeated int32 Repeated = 33 [(validator.field) = {repeated_count_min: 2, repeated_count_max: 5}];
string SomeStringLtReq = 36 [(validator.field) = {length_gt: 2}];
string SomeStringGtReq = 37 [(validator.field) = {length_lt: 12}];
string SomeStringEqReq = 38 [(validator.field) = {length_eq: 10}];
bytes SomeBytesLtReq = 39 [(validator.field) = {length_gt: 5}];
bytes SomeBytesGtReq = 40 [(validator.field) = {length_lt: 20}];
bytes SomeBytesEqReq = 41 [(validator.field) = {length_eq: 12}];
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
syntax = "proto3";
package validatortest;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/mwitkow/go-proto-validators/validator.proto";
message ValueType {
string something = 1 ;
}
// This needs to be able to compile. Fixes https://github.com/mwitkow/go-proto-validators/issues/1
message ValidatorMapMessage3 {
map<string, string> SomeStringMap = 1;
message NestedType {
string something = 1 ;
}
map<string, ValueType> SomeExtMap = 2;
map<int32, ValidatorMapMessage3.NestedType> SomeNestedMap = 3;
}

View File

@ -0,0 +1,28 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
syntax = "proto3";
package validatortest;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/mwitkow/go-proto-validators/validator.proto";
message ExternalMsg {
string Identifier = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
int64 SomeValue = 2 [(validator.field) = {int_gt: 0, int_lt: 100}];
}
message OneOfMessage3 {
uint32 SomeInt = 1 [(validator.field) = {int_gt: 10}];
oneof type {
ExternalMsg one_msg = 2;
uint32 one_int = 3 [(validator.field) = {int_gt: 20}];
uint32 two_int = 4 [(validator.field) = {int_gt: 100}];
}
oneof something {
uint32 three_int = 5 [(validator.field) = {int_gt: 20}];
uint32 four_int = 6 [(validator.field) = {int_gt: 100}];
}
}

238
vendor/github.com/mwitkow/go-proto-validators/validator.pb.go generated vendored Executable file
View File

@ -0,0 +1,238 @@
// Code generated by protoc-gen-gogo.
// source: validator.proto
// DO NOT EDIT!
/*
Package validator is a generated protocol buffer package.
It is generated from these files:
validator.proto
It has these top-level messages:
FieldValidator
*/
package validator
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type FieldValidator struct {
// Uses a Golang RE2-syntax regex to match the field contents.
Regex *string `protobuf:"bytes,1,opt,name=regex" json:"regex,omitempty"`
// Field value of integer strictly greater than this value.
IntGt *int64 `protobuf:"varint,2,opt,name=int_gt,json=intGt" json:"int_gt,omitempty"`
// Field value of integer strictly smaller than this value.
IntLt *int64 `protobuf:"varint,3,opt,name=int_lt,json=intLt" json:"int_lt,omitempty"`
// Used for nested message types, requires that the message type exists.
MsgExists *bool `protobuf:"varint,4,opt,name=msg_exists,json=msgExists" json:"msg_exists,omitempty"`
// Human error specifies a user-customizable error that is visible to the user.
HumanError *string `protobuf:"bytes,5,opt,name=human_error,json=humanError" json:"human_error,omitempty"`
// Field value of double strictly greater than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
FloatGt *float64 `protobuf:"fixed64,6,opt,name=float_gt,json=floatGt" json:"float_gt,omitempty"`
// Field value of double strictly smaller than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
FloatLt *float64 `protobuf:"fixed64,7,opt,name=float_lt,json=floatLt" json:"float_lt,omitempty"`
// Field value of double describing the epsilon within which
// any comparison should be considered to be true. For example,
// when using float_gt = 0.35, using a float_epsilon of 0.05
// would mean that any value above 0.30 is acceptable. It can be
// thought of as a {float_value_condition} +- {float_epsilon}.
// If unset, no correction for floating point inaccuracies in
// comparisons will be attempted.
FloatEpsilon *float64 `protobuf:"fixed64,8,opt,name=float_epsilon,json=floatEpsilon" json:"float_epsilon,omitempty"`
// Floating-point value compared to which the field content should be greater or equal.
FloatGte *float64 `protobuf:"fixed64,9,opt,name=float_gte,json=floatGte" json:"float_gte,omitempty"`
// Floating-point value compared to which the field content should be smaller or equal.
FloatLte *float64 `protobuf:"fixed64,10,opt,name=float_lte,json=floatLte" json:"float_lte,omitempty"`
// Used for string fields, requires the string to be not empty (i.e different from "").
StringNotEmpty *bool `protobuf:"varint,11,opt,name=string_not_empty,json=stringNotEmpty" json:"string_not_empty,omitempty"`
// Repeated field with at least this number of elements.
RepeatedCountMin *int64 `protobuf:"varint,12,opt,name=repeated_count_min,json=repeatedCountMin" json:"repeated_count_min,omitempty"`
// Repeated field with at most this number of elements.
RepeatedCountMax *int64 `protobuf:"varint,13,opt,name=repeated_count_max,json=repeatedCountMax" json:"repeated_count_max,omitempty"`
// Field value of length greater than this value.
LengthGt *int64 `protobuf:"varint,14,opt,name=length_gt,json=lengthGt" json:"length_gt,omitempty"`
// Field value of length smaller than this value.
LengthLt *int64 `protobuf:"varint,15,opt,name=length_lt,json=lengthLt" json:"length_lt,omitempty"`
// Field value of integer strictly equal this value.
LengthEq *int64 `protobuf:"varint,16,opt,name=length_eq,json=lengthEq" json:"length_eq,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *FieldValidator) Reset() { *m = FieldValidator{} }
func (m *FieldValidator) String() string { return proto.CompactTextString(m) }
func (*FieldValidator) ProtoMessage() {}
func (*FieldValidator) Descriptor() ([]byte, []int) { return fileDescriptorValidator, []int{0} }
func (m *FieldValidator) GetRegex() string {
if m != nil && m.Regex != nil {
return *m.Regex
}
return ""
}
func (m *FieldValidator) GetIntGt() int64 {
if m != nil && m.IntGt != nil {
return *m.IntGt
}
return 0
}
func (m *FieldValidator) GetIntLt() int64 {
if m != nil && m.IntLt != nil {
return *m.IntLt
}
return 0
}
func (m *FieldValidator) GetMsgExists() bool {
if m != nil && m.MsgExists != nil {
return *m.MsgExists
}
return false
}
func (m *FieldValidator) GetHumanError() string {
if m != nil && m.HumanError != nil {
return *m.HumanError
}
return ""
}
func (m *FieldValidator) GetFloatGt() float64 {
if m != nil && m.FloatGt != nil {
return *m.FloatGt
}
return 0
}
func (m *FieldValidator) GetFloatLt() float64 {
if m != nil && m.FloatLt != nil {
return *m.FloatLt
}
return 0
}
func (m *FieldValidator) GetFloatEpsilon() float64 {
if m != nil && m.FloatEpsilon != nil {
return *m.FloatEpsilon
}
return 0
}
func (m *FieldValidator) GetFloatGte() float64 {
if m != nil && m.FloatGte != nil {
return *m.FloatGte
}
return 0
}
func (m *FieldValidator) GetFloatLte() float64 {
if m != nil && m.FloatLte != nil {
return *m.FloatLte
}
return 0
}
func (m *FieldValidator) GetStringNotEmpty() bool {
if m != nil && m.StringNotEmpty != nil {
return *m.StringNotEmpty
}
return false
}
func (m *FieldValidator) GetRepeatedCountMin() int64 {
if m != nil && m.RepeatedCountMin != nil {
return *m.RepeatedCountMin
}
return 0
}
func (m *FieldValidator) GetRepeatedCountMax() int64 {
if m != nil && m.RepeatedCountMax != nil {
return *m.RepeatedCountMax
}
return 0
}
func (m *FieldValidator) GetLengthGt() int64 {
if m != nil && m.LengthGt != nil {
return *m.LengthGt
}
return 0
}
func (m *FieldValidator) GetLengthLt() int64 {
if m != nil && m.LengthLt != nil {
return *m.LengthLt
}
return 0
}
func (m *FieldValidator) GetLengthEq() int64 {
if m != nil && m.LengthEq != nil {
return *m.LengthEq
}
return 0
}
var E_Field = &proto.ExtensionDesc{
ExtendedType: (*google_protobuf.FieldOptions)(nil),
ExtensionType: (*FieldValidator)(nil),
Field: 65020,
Name: "validator.field",
Tag: "bytes,65020,opt,name=field",
Filename: "validator.proto",
}
func init() {
proto.RegisterType((*FieldValidator)(nil), "validator.FieldValidator")
proto.RegisterExtension(E_Field)
}
func init() { proto.RegisterFile("validator.proto", fileDescriptorValidator) }
var fileDescriptorValidator = []byte{
// 392 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x92, 0xcf, 0x8f, 0x93, 0x40,
0x14, 0xc7, 0x83, 0xbb, 0xec, 0xc2, 0xb0, 0xdb, 0x6d, 0x26, 0x9a, 0x4c, 0x35, 0x8d, 0x44, 0x2f,
0x1c, 0x0c, 0x4d, 0x3c, 0x7a, 0xd4, 0x60, 0x2f, 0xf8, 0x23, 0x1c, 0x3c, 0x78, 0x21, 0x58, 0x5e,
0xa7, 0x93, 0x0c, 0x33, 0x74, 0xe6, 0xd5, 0xe0, 0x3f, 0xe0, 0x3f, 0xad, 0x07, 0xc3, 0x20, 0x85,
0x4d, 0x7a, 0x9c, 0xcf, 0xe7, 0xcb, 0x1b, 0x78, 0x7c, 0xc9, 0xc3, 0xcf, 0x4a, 0x8a, 0xba, 0x42,
0x6d, 0xd2, 0xd6, 0x68, 0xd4, 0x34, 0x3c, 0x83, 0xe7, 0x31, 0xd7, 0x9a, 0x4b, 0xd8, 0x38, 0xf1,
0xe3, 0xb4, 0xdf, 0xd4, 0x60, 0x77, 0x46, 0xb4, 0xe7, 0xf0, 0xab, 0xdf, 0xd7, 0x64, 0xf1, 0x51,
0x80, 0xac, 0xbf, 0x8d, 0x0f, 0xd1, 0xa7, 0xc4, 0x37, 0xc0, 0xa1, 0x63, 0x5e, 0xec, 0x25, 0x61,
0x31, 0x1c, 0xe8, 0x33, 0x72, 0x23, 0x14, 0x96, 0x1c, 0xd9, 0x93, 0xd8, 0x4b, 0xae, 0x0a, 0x5f,
0x28, 0xdc, 0xe2, 0x88, 0x25, 0xb2, 0xab, 0x33, 0xce, 0x91, 0xae, 0x09, 0x69, 0x2c, 0x2f, 0xa1,
0x13, 0x16, 0x2d, 0xbb, 0x8e, 0xbd, 0x24, 0x28, 0xc2, 0xc6, 0xf2, 0xcc, 0x01, 0xfa, 0x92, 0x44,
0x87, 0x53, 0x53, 0xa9, 0x12, 0x8c, 0xd1, 0x86, 0xf9, 0xee, 0x22, 0xe2, 0x50, 0xd6, 0x13, 0xba,
0x22, 0xc1, 0x5e, 0xea, 0xca, 0xdd, 0x77, 0x13, 0x7b, 0x89, 0x57, 0xdc, 0xba, 0xf3, 0x16, 0x27,
0x25, 0x91, 0xdd, 0xce, 0x54, 0x8e, 0xf4, 0x35, 0xb9, 0x1f, 0x14, 0xb4, 0x56, 0x48, 0xad, 0x58,
0xe0, 0xfc, 0x9d, 0x83, 0xd9, 0xc0, 0xe8, 0x0b, 0x12, 0x8e, 0xa3, 0x81, 0x85, 0x2e, 0x10, 0xfc,
0x9f, 0x0d, 0x93, 0x94, 0x08, 0x8c, 0xcc, 0x64, 0x8e, 0x40, 0x13, 0xb2, 0xb4, 0x68, 0x84, 0xe2,
0xa5, 0xd2, 0x58, 0x42, 0xd3, 0xe2, 0x2f, 0x16, 0xb9, 0x4f, 0x5b, 0x0c, 0xfc, 0xb3, 0xc6, 0xac,
0xa7, 0xf4, 0x0d, 0xa1, 0x06, 0x5a, 0xa8, 0x10, 0xea, 0x72, 0xa7, 0x4f, 0x0a, 0xcb, 0x46, 0x28,
0x76, 0xe7, 0x36, 0xb4, 0x1c, 0xcd, 0x87, 0x5e, 0x7c, 0x12, 0xea, 0x52, 0xba, 0xea, 0xd8, 0xfd,
0xa5, 0x74, 0xd5, 0xf5, 0xaf, 0x28, 0x41, 0x71, 0x3c, 0xf4, 0xbb, 0x59, 0xb8, 0x50, 0x30, 0x80,
0x2d, 0xce, 0xa4, 0x44, 0xf6, 0x30, 0x97, 0xf9, 0x5c, 0xc2, 0x91, 0x2d, 0xe7, 0x32, 0x3b, 0xbe,
0xfb, 0x4a, 0xfc, 0x7d, 0xdf, 0x03, 0xba, 0x4e, 0x87, 0xd2, 0xa4, 0x63, 0x69, 0x52, 0xd7, 0x8f,
0x2f, 0x2d, 0x0a, 0xad, 0x2c, 0xfb, 0xfb, 0xa7, 0xff, 0xd1, 0xd1, 0xdb, 0x55, 0x3a, 0xf5, 0xee,
0x71, 0x81, 0x8a, 0x61, 0xd0, 0xfb, 0xe8, 0xfb, 0xd4, 0xc4, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff,
0xec, 0x56, 0x30, 0x88, 0xa6, 0x02, 0x00, 0x00,
}

View File

@ -0,0 +1,66 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
// Protocol Buffers extensions for defining auto-generateable validators for messages.
// TODO(mwitkow): Add example.
syntax = "proto2";
package validator;
import "google/protobuf/descriptor.proto";
option go_package = "validator";
// TODO(mwitkow): Email protobuf-global-extension-registry@google.com to get an extension ID.
extend google.protobuf.FieldOptions {
optional FieldValidator field = 65020;
}
message FieldValidator {
// Uses a Golang RE2-syntax regex to match the field contents.
optional string regex = 1;
// Field value of integer strictly greater than this value.
optional int64 int_gt = 2;
// Field value of integer strictly smaller than this value.
optional int64 int_lt = 3;
// Used for nested message types, requires that the message type exists.
optional bool msg_exists = 4;
// Human error specifies a user-customizable error that is visible to the user.
optional string human_error = 5;
// Field value of double strictly greater than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
optional double float_gt = 6;
// Field value of double strictly smaller than this value.
// Note that this value can only take on a valid floating point
// value. Use together with float_epsilon if you need something more specific.
optional double float_lt = 7;
// Field value of double describing the epsilon within which
// any comparison should be considered to be true. For example,
// when using float_gt = 0.35, using a float_epsilon of 0.05
// would mean that any value above 0.30 is acceptable. It can be
// thought of as a {float_value_condition} +- {float_epsilon}.
// If unset, no correction for floating point inaccuracies in
// comparisons will be attempted.
optional double float_epsilon = 8;
// Floating-point value compared to which the field content should be greater or equal.
optional double float_gte = 9;
// Floating-point value compared to which the field content should be smaller or equal.
optional double float_lte = 10;
// Used for string fields, requires the string to be not empty (i.e different from "").
optional bool string_not_empty = 11;
// Repeated field with at least this number of elements.
optional int64 repeated_count_min = 12;
// Repeated field with at most this number of elements.
optional int64 repeated_count_max = 13;
// Field value of length greater than this value.
optional int64 length_gt = 14;
// Field value of length smaller than this value.
optional int64 length_lt = 15;
// Field value of integer strictly equal this value.
optional int64 length_eq = 16;
}