Repository: mennanov/fieldmask-utils
Branch: master
Commit: 975f889ba7b9
Files: 19
Total size: 131.3 KB
Directory structure:
gitextract__r76onfn/
├── .github/
│ ├── dependabot.yaml
│ └── workflows/
│ ├── blockwatch.yml
│ ├── coverage.yml
│ ├── linter.yml
│ └── tests.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
├── copy.go
├── copy_proto_test.go
├── copy_test.go
├── go.mod
├── go.sum
├── mask.go
├── mask_test.go
├── revive.toml
└── testproto/
├── test.pb.go
└── test.proto
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: monthly
groups:
all-dependencies:
patterns:
- "*"
================================================
FILE: .github/workflows/blockwatch.yml
================================================
name: blockwatch
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
blockwatch:
runs-on: ubuntu-latest
steps:
- uses: mennanov/blockwatch-action@v1
================================================
FILE: .github/workflows/coverage.yml
================================================
name: Tests coverage
on: [ "push", "pull_request" ]
jobs:
build:
name: Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go 1.22
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run tests with coverage
run: go test -v -coverprofile=coverage.txt -covermode=atomic ./...
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
================================================
FILE: .github/workflows/linter.yml
================================================
name: Linter
on: [ "push", "pull_request" ]
jobs:
build:
name: Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go 1.22
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Go vet
run: go vet ./...
================================================
FILE: .github/workflows/tests.yml
================================================
name: Go tests
on: [ push, pull_request ]
jobs:
go-test:
runs-on: ubuntu-latest
strategy:
matrix:
go-version:
#
- '1.17'
- '1.18'
- '1.19'
- '1.20'
- '1.21'
- '1.22'
#
steps:
- uses: actions/checkout@v4
- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
cache: true
- name: Run tests
run: go test -v ./...
================================================
FILE: .gitignore
================================================
.idea/
vendor/
coverage.out
coverage.txt
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: local
hooks:
- id: blockwatch
name: blockwatch
entry: bash -c 'git diff --patch --cached --unified=0 | blockwatch'
language: system
stages: [ pre-commit ]
pass_filenames: false
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Renat Mennanov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
## Protobuf Field Mask utils for Go
[](https://github.com/mennanov/fieldmask-utils/actions/workflows/tests.yml)
[](https://codecov.io/gh/mennanov/fieldmask-utils)
Features:
* Copy from any Go struct to any compatible Go struct with a field mask applied
* Copy from any Go struct to a `map[string]interface{}` with a field mask applied
* Extensible masks (e.g. inverse mask: copy all except those mentioned, etc.)
* Supports [Protobuf Any](https://developers.google.com/protocol-buffers/docs/proto3#any) message types.
If you're looking for a simple FieldMask library to work with protobuf messages only (not arbitrary structs) consider this tiny repo: [https://github.com/mennanov/fmutils](https://github.com/mennanov/fmutils)
### Examples
Copy from a protobuf message to a protobuf message:
```proto
// testproto/test.proto
message UpdateUserRequest {
User user = 1;
google.protobuf.FieldMask field_mask = 2;
}
```
```go
package main
import fieldmask_utils "github.com/mennanov/fieldmask-utils"
// A function that maps field mask field names to the names used in Go structs.
// It has to be implemented according to your needs.
// Scroll down for a reference on how to apply field masks to your gRPC services.
func naming(s string) string {
if s == "foo" {
return "Foo"
}
return s
}
func main () {
var request UpdateUserRequest
userDst := &testproto.User{} // a struct to copy to
mask, _ := fieldmask_utils.MaskFromPaths(request.FieldMask.Paths, naming)
fieldmask_utils.StructToStruct(mask, request.User, userDst)
// Only the fields mentioned in the field mask will be copied to userDst, other fields are left intact
}
```
Copy from a protobuf message to a `map[string]interface{}`:
```go
package main
import fieldmask_utils "github.com/mennanov/fieldmask-utils"
func main() {
var request UpdateUserRequest
userDst := make(map[string]interface{}) // a map to copy to
mask, _ := fieldmask_utils.MaskFromProtoFieldMask(request.FieldMask, naming)
err := fieldmask_utils.StructToMap(mask, request.User, userDst)
// Only the fields mentioned in the field mask will be copied to userDst, other fields are left intact
}
```
Copy with an inverse mask:
```go
package main
import fieldmask_utils "github.com/mennanov/fieldmask-utils"
func main() {
var request UpdateUserRequest
userDst := &testproto.User{} // a struct to copy to
mask := fieldmask_utils.MaskInverse{"Id": nil, "Friends": fieldmask_utils.MaskInverse{"Username": nil}}
fieldmask_utils.StructToStruct(mask, request.User, userDst)
// Only the fields that are not mentioned in the field mask will be copied to userDst, other fields are left intact.
}
```
#### Naming function
For developers that are looking for a mechanism to apply a mask field in their update endpoints using gRPC services,
there are multiple options for the naming function described above:
- Using the `CamelCase` function provided in
the [original protobuf repository](https://github.com/golang/protobuf/blob/master/protoc-gen-go/generator/generator.go#L2648).
This repository has been deprecated and it will potentially trigger lint errors.
- You can copy-paste the `CamelCase` function to your own project or,
- You can use an [Open Source alternative](https://github.com/gojaguar/jaguar) that provides the same functionality,
already took care of [copying the code](https://github.com/gojaguar/jaguar/blob/main/strings/pascal_case.go), and also added tests.
```go
func main() {
mask := &fieldmaskpb.FieldMask{Paths: []string{"username"}}
mask.Normalize()
req := &UpdateUserRequest{
User: &User{
Id: 1234,
Username: "Test",
},
}
if !mask.IsValid(req) {
return
}
protoMask, err := fieldmask_utils.MaskFromProtoFieldMask(mask, strings.PascalCase)
if err != nil {
return
}
m := make(map[string]any)
err = fieldmask_utils.StructToMap(protoMask, req, m)
if err != nil {
return
}
fmt.Println("Resulting map:", m)
}
```
This will result in a map that contains the fields that need to be updated with their respective values.
#### Converter hooks
When trying to assign a source field to a destination using different types, one can use the Option `WithConverterHook`.
All provided converter functions will be tried in order until src is assignable to dst or an error occured.
Below you will find an example of how to convert from string to int64 when applying a mask by specifying such converter:
```go
type A struct {
Field1 string
}
type B struct {
Field1 int64
}
func main() {
src := &A{
Field1: " 42 ",
}
dst := &B{}
mask := fieldmask_utils.MaskFromString("Field1")
err = fieldmask_utils.StructToStruct(mask, src, dst,
fieldmask_utils.WithConverterHook(func(src, dst *reflect.Value) (interface{}, error) {
data := src.Interface()
// only care for this conversion
if src.Kind() != reflect.String ||
dst.Kind() != reflect.Int64 {
return data, nil
}
// cast it
raw, ok := data.(string)
if !ok {
return data, nil
}
// parse it
return strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
}))
fmt.Println("src:", src)
fmt.Println("dst:", dst)
}
```
### Limitations
1. Larger scope field masks have no effect and are not considered invalid:
field mask strings `"a", "a.b", "a.b.c"` will result in a mask `a{b{c}}`, which is the same as `"a.b.c"`.
2. Masks inside a protobuf `Map` are not supported.
3. When copying from a struct to struct the destination struct must have the same fields (or a subset)
as the source struct. Either of source or destination fields can be a pointer as long as it is a pointer to
the type of the corresponding field.
4. `oneof` fields are represented differently in `fieldmaskpb.FieldMask` compared to `fieldmask_util.Mask`. In
[FieldMask](https://pkg.go.dev/google.golang.org/protobuf/types/known/fieldmaskpb#:~:text=%23%20Field%20Masks%20and%20Oneof%20Fields)
the fields are represented using their property name, in this library they are prefixed with the `oneof` name
matching how Go generated code is laid out. This can lead to issues when converting between the two, for example
when using `MaskFromPaths` or `MaskFromProtoFieldMask`.
================================================
FILE: copy.go
================================================
// Package fieldmask_utils provides utility functions for copying data from structs using a field mask.
package fieldmask_utils
import (
"reflect"
"strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
// StructToStruct copies `src` struct to `dst` struct using the given FieldFilter.
// Only the fields where FieldFilter returns true will be copied to `dst`.
// `src` and `dst` must be coherent in terms of the field names, but it is not required for them to be of the same type.
// Unexported fields are copied only if the corresponding struct filter is empty and `dst` is assignable to `src`.
func StructToStruct(filter FieldFilter, src, dst interface{}, userOpts ...Option) error {
opts := newDefaultOptions()
for _, o := range userOpts {
o(opts)
}
dstVal := reflect.ValueOf(dst)
if dstVal.Kind() != reflect.Ptr {
return errors.Errorf("dst must be a pointer, %s given", dstVal.Kind())
}
srcVal := indirect(reflect.ValueOf(src))
if srcVal.Kind() != reflect.Struct {
return errors.Errorf("src kind must be a struct, %s given", srcVal.Kind())
}
dstVal = indirect(dstVal)
if dstVal.Kind() != reflect.Struct {
return errors.Errorf("dst kind must be a struct, %s given", dstVal.Kind())
}
return structToStruct(filter, &srcVal, &dstVal, opts)
}
func ensureCompatible(src, dst *reflect.Value) error {
srcKind := src.Kind()
if srcKind == reflect.Ptr {
srcKind = src.Type().Elem().Kind()
}
dstKind := dst.Kind()
if dstKind == reflect.Ptr {
dstKind = dst.Type().Elem().Kind()
}
if srcKind != dstKind {
return errors.Errorf("src kind %s differs from dst kind %s", srcKind, dstKind)
}
return nil
}
func structToStruct(filter FieldFilter, src, dst *reflect.Value, userOptions *options) error {
if err := ensureCompatible(src, dst); err != nil {
// incompatible, try using converters:
converted := false
for _, fn := range userOptions.ConverterHooks {
data, err := fn(src, dst)
if err != nil {
// error during conversion, pass upwards
return err
}
rdata := reflect.ValueOf(data)
if err := ensureCompatible(&rdata, dst); err != nil {
// no change using conversion, try next
continue
}
// it is convertable, replace src
src = &rdata
converted = true
break
}
if !converted {
return err
}
}
switch src.Kind() {
case reflect.Struct:
if dst.CanSet() && dst.Type().AssignableTo(src.Type()) && filter.IsEmpty() {
dst.Set(*src)
return nil
}
if dst.Kind() == reflect.Ptr {
if dst.IsNil() {
dst.Set(reflect.New(dst.Type().Elem()))
}
v := dst.Elem()
dst = &v
}
for i := 0; i < src.NumField(); i++ {
srcType := src.Type()
srcName := fieldName(userOptions.SrcTag, srcType.Field(i))
dstName := fieldName(userOptions.DstTag, srcType.Field(i))
subFilter, ok := filter.Filter(srcName)
if !ok {
// Skip this field.
continue
}
srcField := src.Field(i)
if !srcField.CanInterface() {
continue
}
dstField := dst.FieldByName(dstName)
if !dstField.CanSet() {
return errors.Errorf("Can't set a value on a destination field %s", dstName)
}
if err := structToStruct(subFilter, &srcField, &dstField, userOptions); err != nil {
return err
}
}
case reflect.Ptr:
if src.IsNil() {
// If src is nil set dst to nil too.
dst.Set(reflect.Zero(dst.Type()))
break
}
if dst.Kind() == reflect.Ptr && dst.IsNil() {
// If dst is nil create a new instance of the underlying type and set dst to the pointer of that instance.
dst.Set(reflect.New(dst.Type().Elem()))
}
if srcAny, ok := src.Interface().(*anypb.Any); ok {
dstAny, ok := dst.Interface().(*anypb.Any)
if !ok {
return errors.Errorf("dst type is %s, expected: %s ", dst.Type(), "*any.Any")
}
// If subfilter is empty then copy the entire any without any unmarshalling.
if filter.IsEmpty() && !userOptions.UnmarshalAllAny {
dst.Set(*src)
break
}
srcProto, err := srcAny.UnmarshalNew()
if err != nil {
return errors.WithStack(err)
}
srcProtoValue := reflect.ValueOf(srcProto)
if dstAny.GetTypeUrl() == "" {
dstAny.TypeUrl = srcAny.GetTypeUrl()
}
dstProto, err := dstAny.UnmarshalNew()
if err != nil {
return errors.WithStack(err)
}
dstProtoValue := reflect.ValueOf(dstProto)
if err := structToStruct(filter, &srcProtoValue, &dstProtoValue, userOptions); err != nil {
return err
}
newDstAny := new(anypb.Any)
if err := newDstAny.MarshalFrom(dstProtoValue.Interface().(proto.Message)); err != nil {
return errors.WithStack(err)
}
dst.Set(reflect.ValueOf(newDstAny))
break
}
srcElem, dstElem := src.Elem(), *dst
if dst.Kind() == reflect.Ptr {
dstElem = dst.Elem()
}
if err := structToStruct(filter, &srcElem, &dstElem, userOptions); err != nil {
return err
}
case reflect.Interface:
if src.IsNil() {
// If src is nil set dst to nil too.
dst.Set(reflect.Zero(dst.Type()))
break
}
if dst.IsNil() {
if src.Elem().Kind() != reflect.Ptr {
// Non-pointer interface implementations are not addressable.
return errors.Errorf("expected a pointer for an interface value, got %s instead", src.Elem().Kind())
}
dst.Set(reflect.New(src.Elem().Elem().Type()))
}
srcElem, dstElem := src.Elem(), dst.Elem()
if err := structToStruct(filter, &srcElem, &dstElem, userOptions); err != nil {
return err
}
case reflect.Slice:
if src.IsNil() {
// If the source slice is nil the dst slice is set to nil too.
dst.Set(reflect.Zero(dst.Type()))
break
}
dstLen := dst.Len()
srcLen := userOptions.CopyListSize(src)
for i := 0; i < srcLen; i++ {
srcItem := src.Index(i)
var dstItem reflect.Value
if i < dstLen {
// Use an existing item.
dstItem = dst.Index(i)
} else {
// Create a new item if needed.
dstItem = reflect.New(dst.Type().Elem()).Elem()
}
if err := structToStruct(filter, &srcItem, &dstItem, userOptions); err != nil {
return err
}
if i >= dstLen {
// Append newly created items to the slice.
dst.Set(reflect.Append(*dst, dstItem))
}
}
if dstLen > srcLen {
dst.SetLen(srcLen)
}
case reflect.Array:
dstLen := dst.Len()
srcLen := userOptions.CopyListSize(src)
if dstLen < srcLen {
return errors.Errorf("dst array size %d is less than src size %d", dstLen, srcLen)
}
for i := 0; i < srcLen; i++ {
srcItem := src.Index(i)
dstItem := dst.Index(i)
if err := structToStruct(filter, &srcItem, &dstItem, userOptions); err != nil {
return errors.WithStack(err)
}
}
default:
if !dst.CanSet() {
return errors.Errorf("dst %s, %s is not settable", dst, dst.Type())
}
if dst.Kind() == reflect.Ptr {
if !src.CanAddr() {
return errors.Errorf("src %s, %s is not addressable", src, src.Type())
}
dst.Set(src.Addr())
} else {
dst.Set(*src)
}
}
return nil
}
// options are used in StructToStruct and StructToMap functions to modify the copying behavior.
type options struct {
// DstTag can be used to customize the dst field name according to the field's tag, i.g. json.
DstTag string
// SrcTag can be used to customize the src field name according to the field's tag, i.g. json.
SrcTag string
// CopyListSize can control the number of elements copied from src depending on src's Value
CopyListSize func(src *reflect.Value) int
// MapVisitor is called for every filtered field in structToMap.
//
// It is called before copying the data from source to destination allowing custom processing.
// If the visitor function returns true the visited field is skipped.
MapVisitor mapVisitor
// UnmarshalAllAny is used to indicate unmarshal all any fields. Default to true to keep backward compatibility.
//
// If an any field is encountered and this flag is not set, it will only Unmarshal it if there is a subfilter for that field.
// If set it will always Unmarshal all any fields
UnmarshalAllAny bool
// ConverterHooks stores converter functions to be used when calling [StructToStruct].
//
// All converters will be tried in order to convert a src to dst in cases
// where src cannot be directly assigned to dst.
// Any value returned by converters will be used for src if it is assignable
// after conversion.
// If a converter returns an error that error is propagated to the
// initial call of StructToStruct.
ConverterHooks []func(src, dst *reflect.Value) (interface{}, error)
}
// mapVisitor is called for every filtered field in structToMap.
type mapVisitor func(
filter FieldFilter, src, dst reflect.Value,
srcFieldName, dstFieldName string, srcFieldValue reflect.Value) MapVisitorResult
type MapVisitorResult struct {
SkipToNext bool
UpdatedDst *reflect.Value
}
// Option function modifies the given options.
type Option func(*options)
// WithTag sets the destination field name
func WithTag(s string) Option {
return func(o *options) {
o.DstTag = s
}
}
// WithSrcTag sets an option that gets the source field name from the field's tag.
func WithSrcTag(s string) Option {
return func(o *options) {
o.SrcTag = s
}
}
// WithCopyListSize sets CopyListSize func you can set copy size according to src.
func WithCopyListSize(f func(src *reflect.Value) int) Option {
return func(o *options) {
o.CopyListSize = f
}
}
// WithMapVisitor sets the fields visitor function for StructToMap.
func WithMapVisitor(visitor mapVisitor) Option {
return func(o *options) {
o.MapVisitor = visitor
}
}
func WithUnmarshalAllAny(unmarshal bool) Option {
return func(o *options) {
o.UnmarshalAllAny = unmarshal
}
}
// WithConverterHook adds a converter hook to convert from src to dst.
func WithConverterHook(converter func(src, dst *reflect.Value) (interface{}, error)) Option {
return func(o *options) {
o.ConverterHooks = append(o.ConverterHooks, converter)
}
}
func newDefaultOptions() *options {
// set default CopyListSize is func which return src.Len()
return &options{
CopyListSize: func(src *reflect.Value) int { return src.Len() },
UnmarshalAllAny: true,
}
}
// fieldName gets the field name according to the field's tag, or gets StructField.Name default when the field's tag is empty.
func fieldName(tag string, f reflect.StructField) string {
if tag == "" {
return f.Name
}
lookupResult, ok := f.Tag.Lookup(tag)
if !ok {
return f.Name
}
firstComma := strings.Index(lookupResult, ",")
if firstComma == -1 {
return lookupResult
}
return lookupResult[:firstComma]
}
// StructToMap copies `src` struct to the `dst` map.
// Behavior is similar to `StructToStruct`.
// Arrays in the non-empty dst are converted to slices.
func StructToMap(filter FieldFilter, src interface{}, dst map[string]interface{}, userOpts ...Option) error {
opts := newDefaultOptions()
for _, o := range userOpts {
o(opts)
}
_, err := structToMap(filter, reflect.ValueOf(src), reflect.ValueOf(dst), opts)
return err
}
func structToMap(filter FieldFilter, src, dst reflect.Value, userOptions *options) (reflect.Value, error) {
switch src.Kind() {
case reflect.Struct:
if dst.Kind() != reflect.Map {
return dst, errors.Errorf("incompatible destination kind: %s, expected map", dst.Kind())
}
srcType := src.Type()
for i := 0; i < src.NumField(); i++ {
srcName := fieldName(userOptions.SrcTag, srcType.Field(i))
if !isExported(srcType.Field(i)) {
// Unexported fields can not be copied.
continue
}
subFilter, ok := filter.Filter(srcName)
if !ok {
// Skip this field.
continue
}
srcField := indirect(src.Field(i))
dstName := fieldName(userOptions.DstTag, srcType.Field(i))
mapValue := indirect(dst.MapIndex(reflect.ValueOf(dstName)))
if !mapValue.IsValid() {
if srcField.IsValid() {
mapValue = newValue(srcField.Type())
} else {
dstMap := dst.Interface().(map[string]interface{})
dstMap[dstName] = nil
continue
}
}
if userOptions.MapVisitor != nil {
result := userOptions.MapVisitor(filter, src, mapValue, srcName, dstName, srcField)
if result.UpdatedDst != nil {
mapValue = *result.UpdatedDst
}
if result.SkipToNext {
if result.UpdatedDst != nil {
dst.SetMapIndex(reflect.ValueOf(dstName), mapValue)
}
continue
}
}
if isPrimitive(mapValue.Kind()) {
dst.SetMapIndex(reflect.ValueOf(dstName), srcField)
continue
}
var err error
if mapValue, err = structToMap(subFilter, srcField, mapValue, userOptions); err != nil {
return dst, err
}
dst.SetMapIndex(reflect.ValueOf(dstName), mapValue)
}
case reflect.Ptr:
if src.IsNil() {
reflect.ValueOf(dst).Set(reflect.ValueOf(nil))
break
}
var err error
if dst, err = structToMap(filter, indirect(src), dst, userOptions); err != nil {
return dst, err
}
case reflect.Interface:
if src.IsNil() {
reflect.ValueOf(dst).Set(reflect.ValueOf(nil))
break
}
var err error
if dst, err = structToMap(filter, indirect(src), dst, userOptions); err != nil {
return dst, err
}
case reflect.Array, reflect.Slice:
if dstKind := dst.Kind(); dstKind != reflect.Slice && dstKind != reflect.Array {
return dst, errors.Errorf("incompatible destination kind: %s, expected slice", dst.Kind())
}
itemType := src.Type().Elem()
desiredDstLen := userOptions.CopyListSize(&src)
itemKind := itemType.Kind()
if isPrimitive(itemKind) {
// Handle this array/slice as a regular non-nested data structure: copy it entirely to dst.
if desiredDstLen < src.Len() {
dst = src.Slice(0, desiredDstLen)
} else {
dst = src
}
} else {
if dst.Kind() == reflect.Array {
// Convert the array to a slice.
sliceDst := newValue(src.Type())
for i := 0; i < dst.Len(); i++ {
sliceDst = reflect.Append(sliceDst, dst.Index(i))
}
dst = sliceDst
}
var err error
for i := 0; i < desiredDstLen; i++ {
itemExists := false
var subDst reflect.Value
if i < dst.Len() {
subDst = dst.Index(i)
itemExists = true
} else {
subDst = newValue(itemType)
}
if subDst, err = structToMap(filter, src.Index(i), subDst, userOptions); err != nil {
return subDst, err
}
if !itemExists {
dst = reflect.Append(dst, subDst)
}
}
if desiredDstLen < dst.Len() {
dst = dst.Slice(0, desiredDstLen)
}
}
case reflect.Invalid:
dst.Set(reflect.ValueOf(nil))
default:
dst.Set(src)
}
return dst, nil
}
func indirect(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
v = v.Elem()
}
return v
}
// isPrimitive checks whether the given kind is simple enough so that it can be copied directly without recursion.
func isPrimitive(kind reflect.Kind) bool {
return kind != reflect.Ptr &&
kind != reflect.Struct &&
kind != reflect.Interface &&
kind != reflect.Slice &&
kind != reflect.Array &&
kind != reflect.Map
}
// newValue creates a new value given its type.
func newValue(t reflect.Type) reflect.Value {
switch t.Kind() {
case reflect.Struct:
return reflect.MakeMap(reflect.TypeOf(map[string]interface{}{}))
case reflect.Array, reflect.Slice:
return reflect.MakeSlice(reflect.SliceOf(newValue(t.Elem()).Type()), 0, 0)
case reflect.Ptr:
return newValue(t.Elem())
default:
return reflect.New(t).Elem()
}
}
// isExported is a backport of reflect.StructField.IsExported() for the older versions of golang (<1.17).
func isExported(f reflect.StructField) bool {
return f.PkgPath == ""
}
================================================
FILE: copy_proto_test.go
================================================
package fieldmask_utils_test
import (
"testing"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
fieldmask_utils "github.com/mennanov/fieldmask-utils"
"github.com/mennanov/fieldmask-utils/testproto"
)
var testUserFull *testproto.User
var testUserPartial *testproto.User
func init() {
ts := ×tamppb.Timestamp{
Seconds: 5, // easy to verify
Nanos: 6, // easy to verify
}
serializedTs, _ := proto.Marshal(ts)
friend1 := &testproto.User{
Id: 2,
Username: "friend",
Role: testproto.Role_REGULAR,
Meta: map[string]string{"foo": "bar"},
Deactivated: true,
Permissions: []testproto.Permission{testproto.Permission_READ, testproto.Permission_WRITE},
Avatar: &testproto.Image{
OriginalUrl: "original.jpg",
ResizedUrl: "resized.jpg",
},
Images: []*testproto.Image{
{
OriginalUrl: "FRIEND original_image1.jpg",
ResizedUrl: "FRIEND resized_image1.jpg",
},
{
OriginalUrl: "FRIEND original_image2.jpg",
ResizedUrl: "FRIEND resized_image2.jpg",
},
},
Tags: []string{"FRIEND tag1", "FRIEND tag2", "FRIEND tag3"},
Name: &testproto.User_FemaleName{FemaleName: "Maggy"},
}
testUserFull = &testproto.User{
Id: 1,
Username: "username",
Role: testproto.Role_ADMIN,
Meta: map[string]string{"foo": "bar"},
Deactivated: true,
Permissions: []testproto.Permission{testproto.Permission_READ, testproto.Permission_WRITE},
Avatar: &testproto.Image{
OriginalUrl: "original.jpg",
ResizedUrl: "resized.jpg",
},
Images: []*testproto.Image{
{
OriginalUrl: "original_image1.jpg",
ResizedUrl: "resized_image1.jpg",
},
{
OriginalUrl: "original_image2.jpg",
ResizedUrl: "resized_image2.jpg",
},
},
Tags: []string{"tag1", "tag2", "tag3"},
Friends: []*testproto.User{friend1},
Name: &testproto.User_MaleName{MaleName: "John"},
Details: []*anypb.Any{
{
TypeUrl: string("example.com/example/" + proto.MessageName(ts)),
Value: serializedTs,
},
},
}
extraUser, err := anypb.New(testUserFull)
if err != nil {
panic(err)
}
testUserFull.ExtraUser = extraUser
testUserPartial = &testproto.User{
Id: 1,
Username: "username",
}
}
func TestStructToStruct_Proto(t *testing.T) {
userDst := &testproto.User{}
mask := fieldmask_utils.MaskFromString(
"Id,Avatar{OriginalUrl},Tags,Images,Permissions,Friends{Images{ResizedUrl}},Name{MaleName},ExtraUser{Id,Avatar{OriginalUrl}}")
err := fieldmask_utils.StructToStruct(mask, testUserFull, userDst)
require.NoError(t, err)
assert.Equal(t, testUserFull.Id, userDst.Id)
assert.Equal(t, testUserFull.Avatar.OriginalUrl, userDst.Avatar.OriginalUrl)
assert.Equal(t, "", userDst.Avatar.ResizedUrl)
assert.Equal(t, testUserFull.Tags, userDst.Tags)
require.Equal(t, len(testUserFull.Images), len(userDst.Images))
for i, srcImg := range testUserFull.Images {
assert.Equal(t, srcImg.OriginalUrl, userDst.Images[i].OriginalUrl)
assert.Equal(t, srcImg.ResizedUrl, userDst.Images[i].ResizedUrl)
}
assert.Equal(t, testUserFull.Name, userDst.Name)
assert.Equal(t, testUserFull.Permissions, userDst.Permissions)
assert.Equal(t, len(testUserFull.Friends), len(userDst.Friends))
assert.Equal(t, len(testUserFull.Friends[0].Images), len(userDst.Friends[0].Images))
assert.Equal(t, testUserFull.Friends[0].Images[0].ResizedUrl, userDst.Friends[0].Images[0].ResizedUrl)
assert.Equal(t, "", userDst.Friends[0].Images[0].OriginalUrl)
// Zero (default) values below.
assert.Equal(t, testproto.Role_UNKNOWN, userDst.Role)
assert.Equal(t, false, userDst.Deactivated)
assert.Equal(t, map[string]string(nil), userDst.Meta)
extraUser := &testproto.User{}
err = userDst.ExtraUser.UnmarshalTo(extraUser)
require.NoError(t, err)
assert.Equal(t, testUserFull.Id, extraUser.Id)
assert.Equal(t, testUserFull.Avatar.OriginalUrl, extraUser.Avatar.OriginalUrl)
}
func TestStructToStruct_ExistingAnyPreserved(t *testing.T) {
existingExtraUser := &testproto.User{
Id: 42,
Username: "emily",
Role: testproto.Role_REGULAR,
}
existingExtraUserAny, err := anypb.New(existingExtraUser)
require.NoError(t, err)
userDst := &testproto.User{
ExtraUser: existingExtraUserAny,
}
mask := fieldmask_utils.MaskFromString("ExtraUser{Id,Avatar{OriginalUrl}}")
err = fieldmask_utils.StructToStruct(mask, testUserFull, userDst)
require.NoError(t, err)
extraUser := &testproto.User{}
err = userDst.ExtraUser.UnmarshalTo(extraUser)
require.NoError(t, err)
assert.Equal(t, testUserFull.Id, extraUser.Id)
assert.Equal(t, testUserFull.Avatar.OriginalUrl, extraUser.Avatar.OriginalUrl)
assert.Equal(t, existingExtraUser.Username, extraUser.Username)
assert.Equal(t, existingExtraUser.Role, extraUser.Role)
}
func TestStructToStruct_PartialProtoSuccess(t *testing.T) {
userDst := &testproto.User{}
mask := fieldmask_utils.MaskFromString(
"Id,Avatar{OriginalUrl},Images,Username,Permissions,Name{MaleName}")
err := fieldmask_utils.StructToStruct(mask, testUserPartial, userDst)
assert.Nil(t, err)
assert.Equal(t, testUserPartial.Id, userDst.Id)
assert.Equal(t, testUserPartial.Username, userDst.Username)
assert.Equal(t, testUserPartial.Name, userDst.Name)
}
func TestStructToStruct_MaskInverse(t *testing.T) {
userSrc := &testproto.User{
Id: 1,
Username: "username",
Role: testproto.Role_ADMIN,
Meta: map[string]string{"foo": "bar"},
Deactivated: false,
Permissions: []testproto.Permission{testproto.Permission_EXECUTE},
Name: &testproto.User_FemaleName{FemaleName: "Dana"},
Friends: []*testproto.User{
{Id: 2, Username: "friend1"},
{Id: 3, Username: "friend2"},
},
}
userDst := &testproto.User{}
mask := fieldmask_utils.MaskInverse{"Id": nil, "Friends": fieldmask_utils.MaskInverse{"Username": nil}}
err := fieldmask_utils.StructToStruct(mask, userSrc, userDst)
require.NoError(t, err)
// Verify that Id is not copied.
assert.Equal(t, uint32(0), userDst.Id)
// Verify that Friend Usernames are not copied.
assert.Equal(t, "", userDst.Friends[0].Username)
assert.Equal(t, "", userDst.Friends[1].Username)
// Copy missed fields manually and then compare these structs.
userDst.Id = userSrc.Id
userDst.Friends[0].Username = userSrc.Friends[0].Username
userDst.Friends[1].Username = userSrc.Friends[1].Username
assert.Equal(t, userSrc, userDst)
}
func TestStructToStruct_NonProtoSuccess(t *testing.T) {
type Image struct {
OriginalUrl string
ResizedUrl string
}
type User struct {
Id uint32
Username string
Deactivated bool
Images []*Image
}
src := &User{
Id: 1,
Username: "johnny",
Deactivated: true,
Images: []*Image{
{"original_url.jpg", "resized_url.jpg"},
{"original_url.jpg", "resized_url.jpg"},
},
}
dst := &testproto.User{}
mask := fieldmask_utils.MaskFromString("")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, src.Id, dst.Id)
assert.Equal(t, src.Username, dst.Username)
assert.Equal(t, len(src.Images), len(dst.Images))
assert.Equal(t, src.Images[0].OriginalUrl, dst.Images[0].OriginalUrl)
assert.Equal(t, src.Images[0].ResizedUrl, dst.Images[0].ResizedUrl)
assert.Equal(t, src.Images[1].OriginalUrl, dst.Images[1].OriginalUrl)
assert.Equal(t, src.Images[1].ResizedUrl, dst.Images[1].ResizedUrl)
assert.Equal(t, src.Deactivated, dst.Deactivated)
}
func TestStructToStruct_MaskInverseFromMask(t *testing.T) {
userSrc := &testproto.User{
Id: 1,
Username: "username",
Role: testproto.Role_ADMIN,
Meta: map[string]string{"foo": "bar"},
Deactivated: false,
Permissions: []testproto.Permission{testproto.Permission_EXECUTE},
Name: &testproto.User_FemaleName{FemaleName: "Dana"},
Friends: []*testproto.User{
{Id: 2, Username: "friend1"},
{Id: 3, Username: "friend2"},
},
}
userDst := &testproto.User{}
// Mask to MaskInverse
mask := fieldmask_utils.MaskInverse{"Id": fieldmask_utils.Mask{}, "Friends": fieldmask_utils.Mask{"Username": fieldmask_utils.Mask{}}}
err := fieldmask_utils.StructToStruct(mask, userSrc, userDst)
require.NoError(t, err)
assert.Equal(t, &testproto.User{
Username: userSrc.Username,
Role: userSrc.Role,
Meta: userSrc.Meta,
Deactivated: userSrc.Deactivated,
Permissions: userSrc.Permissions,
Name: userSrc.Name,
Friends: []*testproto.User{
{Username: "friend1"},
{Username: "friend2"},
},
}, userDst)
}
func TestStructToStruct_NonProtoFail(t *testing.T) {
type User struct {
Id uint32
UnknownField string
Deactivated bool
}
userSrc := &User{
Id: 1,
UnknownField: "johnny",
Deactivated: true,
}
userDst := &testproto.User{}
mask := fieldmask_utils.MaskFromString("")
err := fieldmask_utils.StructToStruct(mask, userSrc, userDst)
assert.NotNil(t, err)
}
func TestStructToStruct_UnknownAnyInSrcNoSubfieldMask(t *testing.T) {
userWithUnknown := &testproto.User{
Details: []*anypb.Any{
{
TypeUrl: "example.com/example/UnknownType",
Value: []byte("unknown"),
},
},
}
emptyUser := &testproto.User{}
mask := fieldmask_utils.MaskFromString("Details")
err := fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser, fieldmask_utils.WithUnmarshalAllAny(false))
assert.NoError(t, err)
assert.Equal(t, userWithUnknown.Details, emptyUser.Details)
}
func TestStructToStruct_UnknownAnyInDstNoSubfieldMask(t *testing.T) {
userWithUnknown := &testproto.User{
Details: []*anypb.Any{
{
TypeUrl: "example.com/example/UnknownType",
Value: []byte("unknown"),
},
},
}
emptyUser := &testproto.User{}
mask := fieldmask_utils.MaskFromString("Details")
err := fieldmask_utils.StructToStruct(mask, emptyUser, userWithUnknown, fieldmask_utils.WithUnmarshalAllAny(false))
assert.NoError(t, err)
assert.Equal(t, userWithUnknown.Details, emptyUser.Details)
}
func TestStructToStruct_UnknownAnyDefault(t *testing.T) {
userWithUnknown := &testproto.User{
Details: []*anypb.Any{
{
TypeUrl: "example.com/example/UnknownType",
Value: []byte("unknown"),
},
},
}
emptyUser := &testproto.User{}
mask := fieldmask_utils.MaskFromString("Details")
err := fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser)
assert.Contains(t, err.Error(), "not found")
}
func TestStructToStruct_UnknownAnySubfieldMask(t *testing.T) {
userWithUnknown := &testproto.User{
Details: []*anypb.Any{
{
TypeUrl: "example.com/example/UnknownType",
Value: []byte("unknown"),
},
},
}
emptyUser := &testproto.User{}
mask := fieldmask_utils.MaskFromString("Details{Id}")
err := fieldmask_utils.StructToStruct(mask, userWithUnknown, emptyUser, fieldmask_utils.WithUnmarshalAllAny(false))
assert.Contains(t, err.Error(), "not found")
}
func TestStructToMap_Success(t *testing.T) {
userDst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString(
"Id,Avatar{OriginalUrl},Tags,Images,Permissions,Friends{Images{ResizedUrl}}")
err := fieldmask_utils.StructToMap(mask, testUserFull, userDst)
require.Nil(t, err)
expected := map[string]interface{}{
"Id": testUserFull.Id,
"Avatar": map[string]interface{}{
"OriginalUrl": testUserFull.Avatar.OriginalUrl,
},
"Tags": testUserFull.Tags,
"Images": []map[string]interface{}{
{"OriginalUrl": testUserFull.Images[0].OriginalUrl, "ResizedUrl": testUserFull.Images[0].ResizedUrl},
{"OriginalUrl": testUserFull.Images[1].OriginalUrl, "ResizedUrl": testUserFull.Images[1].ResizedUrl},
},
"Permissions": testUserFull.Permissions,
"Friends": []map[string]interface{}{
{
"Images": []map[string]interface{}{
{"ResizedUrl": testUserFull.Friends[0].Images[0].ResizedUrl},
{"ResizedUrl": testUserFull.Friends[0].Images[1].ResizedUrl},
},
},
},
}
assert.Equal(t, expected, userDst)
}
func TestStructToMap_PartialProtoSuccess(t *testing.T) {
userDst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString(
"Id,Avatar{OriginalUrl},Images,Username,Permissions,Name{MaleName}")
err := fieldmask_utils.StructToMap(mask, testUserPartial, userDst)
require.Nil(t, err)
expected := map[string]interface{}{
"Id": testUserPartial.Id,
"Avatar": nil,
"Images": []map[string]interface{}{},
"Username": testUserPartial.Username,
"Permissions": []testproto.Permission(nil),
"Name": nil,
}
assert.Equal(t, expected, userDst)
}
================================================
FILE: copy_test.go
================================================
package fieldmask_utils_test
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
fieldmask_utils "github.com/mennanov/fieldmask-utils"
)
func TestStructToStruct_SimpleStruct(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
src := &A{
Field1: "src field1",
Field2: 1,
}
dst := new(A)
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &A{
Field1: "src field1",
Field2: 0,
}, dst)
}
func TestStructToStruct_PtrToInt(t *testing.T) {
type A struct {
Field2 *int
}
n := 42
src := &A{
Field2: &n,
}
dst := new(A)
mask := fieldmask_utils.MaskFromString("Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &A{
Field2: src.Field2,
}, dst)
}
func TestStructToStruct_StructToPointer(t *testing.T) {
v15 := 15
v42 := 42
type N struct {
Field1 int
}
type S struct {
Field1 N
Field2 int
}
src := &S{
Field1: N{
Field1: v15,
},
Field2: v42,
}
type SN struct {
Field1 *int
}
type D struct {
Field1 *SN
Field2 *int
}
dst := new(D)
mask := fieldmask_utils.MaskFromString("Field1,Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &D{
Field1: &SN{
Field1: &v15,
},
Field2: &v42,
}, dst)
}
func TestStructToStruct_IntToPointer(t *testing.T) {
v := 42
type S struct {
Field2 int
}
src := &S{
Field2: v,
}
type D struct {
Field2 *int
}
dst := new(D)
mask := fieldmask_utils.MaskFromString("Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &D{
Field2: &v,
}, dst)
}
func TestStructToStruct_PointerToInt(t *testing.T) {
v := 42
type S struct {
Field2 *int
}
src := &S{
Field2: &v,
}
type D struct {
Field2 int
}
dst := new(D)
mask := fieldmask_utils.MaskFromString("Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &D{
Field2: 42,
}, dst)
}
func TestStructToStruct_Incompatible(t *testing.T) {
type S struct {
Field2 int
}
src := &S{
Field2: 42,
}
type D struct {
Field2 string
}
dst := new(D)
mask := fieldmask_utils.MaskFromString("Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.EqualError(t, err, "src kind int differs from dst kind string")
}
func TestStructToStruct_PtrToStruct_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
Field2 int
A *A
}
type C struct {
Field1 string
B *B
}
src := &C{
Field1: "C field1",
B: &B{
Field1: "StringerB field1",
Field2: 1,
A: &A{
Field1: "StringerA field1",
Field2: 5,
},
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("B{Field1,A{Field2}}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
Field1: "",
B: &B{
Field1: src.B.Field1,
Field2: 0,
A: &A{
Field1: "",
Field2: src.B.A.Field2,
},
},
}, dst)
}
func TestStructToStruct_PtrToStruct_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
Field2 int
A *A
}
type C struct {
Field1 string
B *B
}
src := &C{
Field1: "src C field1",
B: &B{
Field1: "StringerB field1",
Field2: 1,
A: &A{
Field1: "StringerA field1",
Field2: 5,
},
},
}
dst := &C{
Field1: "dst C field1",
B: &B{
Field1: "dst StringerB field1",
Field2: 2,
A: &A{
Field1: "dst StringerA field1",
Field2: 10,
},
},
}
mask := fieldmask_utils.MaskFromString("B{Field1,A{Field2}}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
Field1: "dst C field1",
B: &B{
Field1: src.B.Field1,
Field2: 2,
A: &A{
Field1: "dst StringerA field1",
Field2: src.B.A.Field2,
},
},
}, dst)
}
func TestStructToStruct_NestedStruct_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
Field2 int
A A
}
type C struct {
Field1 string
B B
}
src := &C{
Field1: "C field1",
B: B{
Field1: "StringerB field1",
Field2: 1,
A: A{
Field1: "StringerA field1",
Field2: 5,
},
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("B{Field1,A{Field2}}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
Field1: "",
B: B{
Field1: src.B.Field1,
Field2: 0,
A: A{
Field1: "",
Field2: src.B.A.Field2,
},
},
}, dst)
}
func TestStructToStruct_NestedStruct_EmptyDst_OptionDst(t *testing.T) {
opts := fieldmask_utils.WithTag("db")
type ASrc struct {
Field1 string
Field2 int `db:"SomeField"`
}
type BSrc struct {
Field1 string `struct:"a_name"`
A ASrc `db:"AnotherName"`
}
src := &BSrc{
Field1: "B Field1",
A: ASrc{
Field1: "A Field 1",
Field2: 1,
},
}
type ADst struct {
Field1 string
SomeField int
}
type BDst struct {
Field1 string
AnotherName ADst
}
dst := &BDst{}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst, opts)
require.NoError(t, err)
assert.Equal(t, &BDst{
Field1: src.Field1,
AnotherName: ADst{
SomeField: src.A.Field2,
},
}, dst)
}
func TestStructToStruct_NestedStruct_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
Field2 int
A A
}
type C struct {
Field1 string
B B
}
src := &C{
Field1: "src C field1",
B: B{
Field1: "src StringerB field1",
Field2: 1,
A: A{
Field1: "src StringerA field1",
Field2: 5,
},
},
}
dst := &C{
Field1: "dst C field1",
B: B{
Field1: "dst StringerB field1",
Field2: 2,
A: A{
Field1: "dst StringerA field1",
Field2: 10,
},
},
}
mask := fieldmask_utils.MaskFromString("B{Field1,A{Field2}}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
Field1: "dst C field1",
B: B{
Field1: src.B.Field1,
Field2: 2,
A: A{
Field1: "dst StringerA field1",
Field2: src.B.A.Field2,
},
},
}, dst)
}
func TestStructToStruct_SliceOfStructs_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A []A
}
src := &B{
Field1: "src StringerB field1",
A: []A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
},
}
dst := new(B)
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{
Field1: src.Field1,
A: []A{
{
Field1: "",
Field2: src.A[0].Field2,
},
{
Field1: "",
Field2: src.A[1].Field2,
},
},
}, dst)
}
func TestStructToStruct_SliceOfStructs_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A []A
}
src := &B{
Field1: "src StringerB field1",
A: []A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
{
Field1: "StringerA field1 2",
Field2: 3,
},
},
}
dst := &B{
Field1: "dst StringerB field1",
A: []A{
{
Field1: "dst StringerA field1 0",
Field2: 10,
},
{
Field1: "dst StringerA field1 1",
Field2: 20,
},
},
}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{
Field1: src.Field1,
A: []A{
{
Field1: "dst StringerA field1 0",
Field2: src.A[0].Field2,
},
{
Field1: "dst StringerA field1 1",
Field2: src.A[1].Field2,
},
{
Field1: "",
Field2: src.A[2].Field2,
},
},
}, dst)
}
func TestStructToStruct_EntireSlice_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A []A
}
src := &B{
Field1: "src StringerB field1",
A: []A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
},
}
dst := &B{
Field1: "dst StringerB field1",
A: []A{
{
Field1: "dst StringerA field1 0",
Field2: 10,
},
{
Field1: "dst StringerA field1 1",
Field2: 20,
},
{
Field1: "dst StringerA field1 2",
Field2: 30,
},
},
}
mask := fieldmask_utils.MaskFromString("Field1,A")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{
Field1: src.Field1,
A: src.A,
}, dst)
}
func TestStructToStruct_NilSrcSlice_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A []A
}
src := &B{
Field1: "src StringerB field1",
A: nil,
}
dst := &B{
Field1: "dst StringerB field1",
A: []A{
{
Field1: "dst StringerA field1 0",
Field2: 10,
},
{
Field1: "dst StringerA field1 1",
Field2: 20,
},
{
Field1: "dst StringerA field1 2",
Field2: 30,
},
},
}
mask := fieldmask_utils.MaskFromString("Field1,A")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{
Field1: src.Field1,
A: src.A,
}, dst)
}
func TestStructToStruct_SliceOfPtrsToStruct_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A []*A
}
src := &B{
Field1: "src StringerB field1",
A: []*A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
},
}
dst := new(B)
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{
Field1: src.Field1,
A: []*A{
{
Field1: "",
Field2: src.A[0].Field2,
},
{
Field1: "",
Field2: src.A[1].Field2,
},
},
}, dst)
}
func TestStructToStruct_ArrayOfStructs_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A [2]A
}
type C struct {
Field1 string
A [3]A
}
src := &B{
Field1: "src StringerB field1",
A: [2]A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
Field1: src.Field1,
A: [3]A{
{
Field1: "",
Field2: src.A[0].Field2,
},
{
Field1: "",
Field2: src.A[1].Field2,
},
{
Field1: "",
Field2: 0,
},
},
}, dst)
}
func TestStructToStruct_Array_DstLenLessThanSrc(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A [2]A
}
type C struct {
Field1 string
A [1]A
}
src := &B{
Field1: "src StringerB field1",
A: [2]A{
{
Field1: "StringerA field1 0",
Field2: 1,
},
{
Field1: "StringerA field1 1",
Field2: 2,
},
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
}
func TestStructToStruct_DifferentStructTypes(t *testing.T) {
type A struct {
Field string
}
type B struct {
Field string
}
src := &A{"value"}
dst := new(B)
mask := fieldmask_utils.MaskFromString("Field")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &B{src.Field}, dst)
}
func TestStructToStruct_DifferentStructTypesNested(t *testing.T) {
type A struct {
Field string
}
type AA struct {
Field string
}
type B struct {
A A
}
type C struct {
A AA
}
src := &B{
A: A{
Field: "value",
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("A")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
A: AA{
Field: src.A.Field,
},
}, dst)
}
func TestStructToStruct_DifferentStructTypesPtrNested(t *testing.T) {
type A struct {
Field string
}
type AA struct {
Field string
}
type B struct {
A *A
}
type C struct {
A *AA
}
src := &B{
A: &A{
Field: "value",
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("A")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
A: &AA{
Field: src.A.Field,
},
}, dst)
}
type StringerA struct {
Field string
}
func (a *StringerA) String() string {
return a.Field
}
type StringerB struct {
Field string
}
func (b *StringerB) String() string {
return b.Field
}
func TestStructToStruct_Interface_EmptyDst(t *testing.T) {
type C struct {
S fmt.Stringer
}
src := &C{
S: &StringerA{
Field: "StringerA",
},
}
dst := new(C)
mask := fieldmask_utils.MaskFromString("S")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, &C{
S: &StringerA{
Field: "StringerA",
},
}, dst)
}
func TestStructToStruct_SameInterfaces_NonEmptyDst(t *testing.T) {
type C struct {
S fmt.Stringer
}
src := &C{
S: &StringerA{
Field: "StringerA",
},
}
dst := &C{
S: &StringerA{
Field: "StringerB",
},
}
mask := fieldmask_utils.MaskFromString("S")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, src.S.String(), dst.S.String())
assert.Equal(t, &C{
S: &StringerA{
Field: "StringerA",
},
}, dst)
}
func TestStructToStruct_DifferentCompatibleInterfaces_NonEmptyDst(t *testing.T) {
type C struct {
S fmt.Stringer
}
src := &C{
S: &StringerA{
Field: "StringerA",
},
}
dst := &C{
S: &StringerB{
Field: "StringerB",
},
}
mask := fieldmask_utils.MaskFromString("S")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, src.S.String(), dst.S.String())
}
type Logger interface {
Log() string
}
type LoggerImpl struct {
Field string
}
func (d *LoggerImpl) Log() string {
return d.Field
}
func TestStructToStruct_DifferentIncompatibleInterfaces(t *testing.T) {
type C struct {
S fmt.Stringer
}
type E struct {
S Logger
}
src := &C{
S: &StringerA{
Field: "StringerA",
},
}
dst := &E{
S: &LoggerImpl{
Field: "Logger",
},
}
mask := fieldmask_utils.MaskFromString("S")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, src.S.String(), dst.S.Log())
}
func TestStructToStruct_EmptyMask(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
src := &A{
Field1: "A Field1",
Field2: 1,
}
dst := new(A)
mask := fieldmask_utils.MaskFromString("")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, src, dst)
}
type StringerImpl struct {
Name string
}
func (*StringerImpl) someMethod() {}
func (f *StringerImpl) String() string {
return f.Name
}
type StringerNonPtrImpl struct {
Name string
}
func (s StringerNonPtrImpl) String() string {
return s.Name
}
func TestStructToStruct_SameInterfacesPtr_EmptyDst(t *testing.T) {
type A struct {
Stringer fmt.Stringer
}
type B struct {
Stringer fmt.Stringer
}
src := &A{
Stringer: &StringerImpl{Name: "Jessica"},
}
dst := new(B)
mask := fieldmask_utils.MaskFromString("Stringer")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, src.Stringer.String(), dst.Stringer.String())
}
func TestStructToStruct_SameInterfacesPtr_NonEmptyDst(t *testing.T) {
type A struct {
Stringer fmt.Stringer
}
type B struct {
Stringer fmt.Stringer
}
src := &A{
Stringer: &StringerImpl{
Name: "Jessica",
},
}
dst := &B{
Stringer: &StringerImpl{
Name: "Dana",
},
}
mask := fieldmask_utils.MaskFromString("Stringer")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, dst.Stringer.String(), src.Stringer.String())
}
func TestStructToStruct_SameInterfacesNonPtr_EmptyDst(t *testing.T) {
type A struct {
Stringer fmt.Stringer
}
src := &A{
Stringer: StringerNonPtrImpl{Name: "Jessica"},
}
dst := new(A)
mask := fieldmask_utils.MaskFromString("Stringer")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
}
func TestStructToStruct_SameInterfacesNonPtr_NonEmptyDst(t *testing.T) {
type A struct {
Stringer fmt.Stringer
}
src := &A{
Stringer: StringerNonPtrImpl{Name: "Jessica"},
}
dst := &A{
Stringer: StringerNonPtrImpl{Name: "Adam"},
}
mask := fieldmask_utils.MaskFromString("Stringer")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
}
func TestStructToStruct_NonPtrDst(t *testing.T) {
type A struct {
Field int
}
src := &A{Field: 1}
dst := A{}
mask := fieldmask_utils.MaskFromString("")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
}
func TestStructToStruct_DifferentDstKind(t *testing.T) {
type A struct {
Field int
}
src := &A{Field: 1}
dst := &map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
}
func TestStructToStruct_UnexportedFieldsPtr(t *testing.T) {
type A struct {
foo string
Bar string
}
type B struct {
A *A
B string
}
src := &B{
A: &A{
foo: "foo",
Bar: "Bar",
},
B: "B",
}
dst := &B{}
mask := fieldmask_utils.MaskFromString("A,B")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, src, dst)
}
func TestStructToStruct_UnexportedFields(t *testing.T) {
type A struct {
foo string
Bar string
}
type B struct {
A A
B string
}
src := &B{
A: A{
foo: "foo",
Bar: "Bar",
},
B: "B",
}
dst := &B{}
mask := fieldmask_utils.MaskFromString("A,B")
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, src, dst)
}
func TestStructToStruct_MaskWithInverseMask(t *testing.T) {
type A struct {
Foo string
Bar string
}
type B struct {
A A
B string
C string
}
src := &B{
A: A{
Foo: "foo",
Bar: "Bar",
},
B: "B",
C: "C",
}
for _, mask := range []fieldmask_utils.FieldFilter{
fieldmask_utils.Mask{"B": nil, "A": &fieldmask_utils.MaskInverse{"Bar": nil}},
fieldmask_utils.Mask{"B": fieldmask_utils.Mask{}, "A": &fieldmask_utils.MaskInverse{"Bar": fieldmask_utils.Mask{}}},
} {
dst := &B{}
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, &B{
A: A{
Foo: src.A.Foo,
},
B: "B",
}, dst)
}
}
func TestStructToStruct_InverseMaskWithMask(t *testing.T) {
type A struct {
Foo string
Bar string
}
type B struct {
A A
B string
C string
}
src := &B{
A: A{
Foo: "foo",
Bar: "Bar",
},
B: "B",
C: "C",
}
for _, mask := range []fieldmask_utils.FieldFilter{
fieldmask_utils.MaskInverse{"B": fieldmask_utils.Mask{}, "A": &fieldmask_utils.Mask{"Bar": fieldmask_utils.Mask{}}},
fieldmask_utils.MaskInverse{"B": nil, "A": &fieldmask_utils.Mask{"Bar": nil}},
} {
dst := &B{}
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.NoError(t, err)
assert.Equal(t, &B{
A: A{
Bar: src.A.Bar,
},
C: "C",
}, dst)
}
}
func TestStructToMap_NestedStruct_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A A
}
src := &B{
Field1: "B Field1",
A: A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"A": map[string]interface{}{
"Field2": src.A.Field2,
},
}, dst)
}
func TestStructToMap_NestedStruct_EmptyDst_OptionDst(t *testing.T) {
opts := fieldmask_utils.WithTag("db")
type A struct {
Field1 string
Field2 int `db:"some_field"`
}
type B struct {
Field1 string `struct:"a_name"`
A A `db:"another_name"`
}
src := &B{
Field1: "B Field1",
A: A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst, opts)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"another_name": map[string]interface{}{
"some_field": src.A.Field2,
},
}, dst)
}
func TestStructToMap_NestedStruct_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A A
}
src := &B{
Field1: "B Field1",
A: A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := map[string]interface{}{
"A": map[string]interface{}{
"Field1": "existing value",
"Field2": 10,
},
}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"A": map[string]interface{}{
"Field1": "existing value",
"Field2": src.A.Field2,
},
}, dst)
}
func TestStructToMap_PtrToStruct_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A *A
}
src := &B{
Field1: "B Field1",
A: &A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"A": map[string]interface{}{
"Field2": src.A.Field2,
},
}, dst)
}
func TestStructToMap_PtrToStruct_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 int
}
type B struct {
Field1 string
A *A
}
src := &B{
Field1: "B Field1",
A: &A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := map[string]interface{}{
"A": map[string]interface{}{
"Field1": "existing value",
"Field2": 10,
},
}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"A": map[string]interface{}{
"Field1": "existing value",
"Field2": src.A.Field2,
},
}, dst)
}
func TestStructToMap_ArrayOfStructs_EmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
A [1]A
}
src := &B{
A: [1]A{
{
Field1: "src field1",
Field2: "src field2",
},
},
}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"A": []map[string]interface{}{
{
"Field2": src.A[0].Field2,
},
},
}, dst)
}
func TestStructToMap_SliceOfStructs_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
A []A
}
src := &B{
A: []A{
{
Field1: "src field1",
Field2: "src field2",
},
},
}
dst := map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "dst field1",
},
},
}
mask := fieldmask_utils.MaskFromString("A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "dst field1",
"Field2": src.A[0].Field2,
},
},
}, dst)
}
func TestStructToMap_EntireSlicePrimitive_NonEmptyDst(t *testing.T) {
type A struct {
Field1 []int
}
src := &A{
Field1: []int{1, 2, 4, 8},
}
dst := map[string]interface{}{
"Field1": []int{16, 32, 64},
}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
}, dst)
}
func TestStructToMap_EntireSlice_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
A []A
}
src := &B{
A: []A{
{
Field1: "src ele1 field1",
Field2: "src ele1 field2",
},
{
Field1: "src ele2 field1",
Field2: "src ele2 field2",
},
},
}
dst := map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "dst ele1 field1",
},
{
"Field2": "dst ele2 field 2",
},
{
"Field1": "dst ele3 field 3",
},
},
}
mask := fieldmask_utils.MaskFromString("A")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": src.A[0].Field1,
"Field2": src.A[0].Field2,
},
{
"Field1": src.A[1].Field1,
"Field2": src.A[1].Field2,
},
},
}, dst)
}
func TestStructToMap_NilSrcSlice_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
FieldA []A
}
src := &B{FieldA: nil}
dst := map[string]interface{}{
"FieldA": []map[string]interface{}{
{
"Field1": "dst ele1 field1",
},
{
"Field2": "dst ele2 field 2",
},
{
"Field1": "dst ele3 field 3",
},
},
}
mask := fieldmask_utils.MaskFromString("FieldA")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"FieldA": []map[string]interface{}{},
}, dst)
}
func TestStructToMap_EntireSlice_DstSliceLenIsLessThanSource(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
A []A
}
src := &B{
A: []A{
{
Field1: "src ele1 field1",
Field2: "src ele1 field2",
},
{
Field1: "src ele2 field1",
Field2: "src ele2 field2",
},
},
}
dst := map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "dst ele1 field1",
},
},
}
mask := fieldmask_utils.MaskFromString("A")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "src ele1 field1",
"Field2": "src ele1 field2",
},
{
"Field1": "src ele2 field1",
"Field2": "src ele2 field2",
},
},
}, dst)
}
func TestStructToMap_Array_NonEmptyDst(t *testing.T) {
type A struct {
Field1 string
Field2 string
}
type B struct {
A [3]A
}
src := &B{
A: [3]A{
{
Field1: "src ele1 field1",
Field2: "src ele1 field2",
},
{
Field1: "src ele2 field1",
Field2: "src ele2 field2",
},
{
Field1: "src ele3 field1",
Field2: "src ele3 field2",
},
},
}
dst := map[string]interface{}{
"A": [2]map[string]interface{}{
{
"Field1": "dst ele1 field1",
},
{
"Field1": "dst ele2 field1",
},
},
}
mask := fieldmask_utils.MaskFromString("A{Field2}")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"A": []map[string]interface{}{
{
"Field1": "dst ele1 field1",
"Field2": "src ele1 field2",
},
{
"Field1": "dst ele2 field1",
"Field2": "src ele2 field2",
},
{
"Field2": "src ele3 field2",
},
},
}, dst)
}
func TestStructToMap_ArrayPrimitive_NonEmptyDst(t *testing.T) {
type A struct {
Field1 [5]int
}
src := &A{
Field1: [5]int{1, 2, 4, 8, 10},
}
dst := map[string]interface{}{
"Field1": [4]int{16, 32, 64, 0},
}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": [5]int{1, 2, 4, 8, 10},
}, dst)
}
func TestStructToMap_EmptySliceSrc_NonEmptyArrayDst(t *testing.T) {
type A struct {
Field1 []int
}
src := &A{
Field1: []int{},
}
dst := map[string]interface{}{
"Field1": [4]int{16, 32, 64, 0},
}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
}, dst)
}
func TestStructToStruct_CopyStructSlice_WithMaxCopyListSize(t *testing.T) {
type AA struct {
Field int
}
type A struct {
Field1 []AA
}
src := &A{
Field1: []AA{{1}, {2}, {3}},
}
dst := &A{}
const copySize int = 2
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: src.Field1[:copySize],
}, dst)
}
func TestStructToStruct_CopyIntSlice_WithMaxCopyListSize(t *testing.T) {
type A struct {
Field1 []int
}
src := &A{
Field1: []int{1, 2, 3},
}
const copySize int = 2
dst := &A{}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: src.Field1[:copySize],
}, dst)
}
func TestStructToStruct_CopyIntArray_WithMaxCopyListSize(t *testing.T) {
const arraySize int = 3
type A struct {
Field1 [arraySize]int
}
src := &A{
Field1: [arraySize]int{1, 2, 3},
}
const copySize int = arraySize - 1
dst := &A{}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: [3]int{1, 2},
}, dst)
}
func TestStructToStruct_CopyStructArray_WithMaxCopyListSize(t *testing.T) {
const arraySize int = 3
type AA struct {
Field int
}
type A struct {
Field1 [3]AA
}
src := &A{
Field1: [3]AA{{1}, {2}, {3}},
}
dst := &A{}
const copySize int = arraySize - 1
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: [3]AA{{1}, {2}},
}, dst)
}
func TestStructToMap_CopyStructSlice_WithMaxCopyListSize(t *testing.T) {
type AA struct {
Field int
}
type A struct {
Field1 []AA
}
src := &A{
Field1: []AA{{1}, {2}, {3}},
}
dst := map[string]interface{}{}
const copySize int = 2
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": []map[string]interface{}{{"Field": 1}, {"Field": 2}},
}, dst)
}
func TestStructToMap_CopyIntSlice_WithMaxCopyListSize(t *testing.T) {
type A struct {
Field1 []int
}
src := &A{
Field1: []int{1, 2, 3},
}
dst := map[string]interface{}{}
const copySize int = 2
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": []int{1, 2},
}, dst)
}
func TestStructToMap_CopyStructArray_WithMaxCopyListSize(t *testing.T) {
const arraySize int = 3
type AA struct {
Field int
}
type A struct {
Field1 [3]AA
}
src := &A{
Field1: [3]AA{{1}, {2}, {3}},
}
dst := map[string]interface{}{}
const copySize int = arraySize - 1
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": []map[string]interface{}{{"Field": 1}, {"Field": 2}},
}, dst)
}
func TestStructToMap_CopyIntArray_WithMaxCopyListSize(t *testing.T) {
const arraySize int = 3
type A struct {
Field1 [arraySize]int
}
src := &A{
Field1: [arraySize]int{1, 2, 3},
}
const copySize int = arraySize - 1
dst := map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("Field1")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
return copySize
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1[:copySize],
}, dst)
}
func TestStructToMap_CopyStructWithPrivateFields_WithMapVisitor(t *testing.T) {
type A struct {
Time time.Time
Other int
}
unixTime := time.Unix(10, 10)
src := &A{Time: unixTime}
dst := map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("Time")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithMapVisitor(
func(_ fieldmask_utils.FieldFilter, _, dst reflect.Value,
srcFieldName, dstFieldName string, srcFieldValue reflect.Value) fieldmask_utils.MapVisitorResult {
if srcFieldName == "Time" {
return fieldmask_utils.MapVisitorResult{
SkipToNext: true,
UpdatedDst: &srcFieldValue,
}
}
return fieldmask_utils.MapVisitorResult{}
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Time": unixTime,
}, dst)
}
func TestStructToMap_MapVisitorVisitsOnlyFilteredFields(t *testing.T) {
type A struct {
Field1 int
Field2 string
Field3 int
}
src := &A{Field1: 42, Field2: "hello", Field3: 44}
dst := map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("Field1, Field2")
var visitedFields []string
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithMapVisitor(
func(_ fieldmask_utils.FieldFilter, _, _ reflect.Value,
srcFieldName, _ string, _ reflect.Value) fieldmask_utils.MapVisitorResult {
visitedFields = append(visitedFields, srcFieldName)
return fieldmask_utils.MapVisitorResult{}
}))
require.NoError(t, err)
assert.Equal(t, visitedFields, []string{"Field1", "Field2"})
}
func TestStructToMap_WithMapVisitor_SkipsToNextField(t *testing.T) {
type A struct {
Field1 int
Field2 string
Field3 int
}
src := &A{Field1: 42, Field2: "hello", Field3: 44}
dst := map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("Field1, Field2")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithMapVisitor(
func(_ fieldmask_utils.FieldFilter, _, _ reflect.Value,
srcFieldName, dstFieldName string, _ reflect.Value) fieldmask_utils.MapVisitorResult {
if srcFieldName == "Field1" {
updatedDst := reflect.ValueOf(33)
return fieldmask_utils.MapVisitorResult{
SkipToNext: true,
UpdatedDst: &updatedDst,
}
}
return fieldmask_utils.MapVisitorResult{}
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{"Field1": 33, "Field2": "hello"}, dst)
}
func TestStructToStruct_CopySlice_WithDiffentItemKind(t *testing.T) {
type A struct {
Field1 []int
Field2 []string
}
src := &A{
Field1: []int{1, 2, 3},
Field2: []string{"1", "2", "3"},
}
dst := &A{}
const copySize int = 1
mask := fieldmask_utils.MaskFromString("Field1,Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
if itemKind := src.Type().Elem().Kind(); itemKind == reflect.Int {
return copySize
} else {
return src.Len()
}
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: []int{1},
Field2: []string{"1", "2", "3"},
}, dst)
}
func TestStructToMap_CopySlice_WithDiffentItemKind(t *testing.T) {
type A struct {
Field1 []int
Field2 []string
}
src := &A{
Field1: []int{1, 2, 3},
Field2: []string{"1", "2", "3"},
}
dst := map[string]interface{}{}
const copySize int = 1
mask := fieldmask_utils.MaskFromString("Field1,Field2")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
if itemKind := src.Type().Elem().Kind(); itemKind == reflect.Int {
return copySize
} else {
return src.Len()
}
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": []int{1},
"Field2": []string{"1", "2", "3"},
}, dst)
}
func TestStructToStruct_CopySlice_WithDiffentItemType(t *testing.T) {
type AA struct {
Int int
}
type A struct {
Field1 []int
Field2 []AA
}
src := &A{
Field1: []int{1, 2, 3},
Field2: []AA{{1}, {2}, {3}},
}
dst := &A{}
const copySize int = 1
mask := fieldmask_utils.MaskFromString("Field1,Field2")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
if itemType := src.Type().Elem().Name(); itemType == "AA" {
return copySize
} else {
return src.Len()
}
}))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: []int{1, 2, 3},
Field2: []AA{{1}},
}, dst)
}
func TestStructToMap_CopySlice_WithDiffentItemType(t *testing.T) {
type AA struct {
Int int
}
type A struct {
Field1 []int
Field2 []AA
}
src := &A{
Field1: []int{1, 2, 3},
Field2: []AA{{1}, {2}, {3}},
}
dst := map[string]interface{}{}
const copySize int = 1
mask := fieldmask_utils.MaskFromString("Field1,Field2")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithCopyListSize(func(src *reflect.Value) int {
if itemType := src.Type().Elem().Name(); itemType == "AA" {
return copySize
} else {
return src.Len()
}
}))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": []int{1, 2, 3},
"Field2": []map[string]interface{}{{"Int": 1}},
}, dst)
}
func TestStructToStruct_WithNonStructSrcError(t *testing.T) {
type A struct{ Field int }
var src = 1
var dst = &A{}
mask := fieldmask_utils.MaskFromString("Field")
err := fieldmask_utils.StructToStruct(mask, src, dst)
require.Error(t, err)
}
func TestStructToStruct_WithMultiTagComma(t *testing.T) {
type A struct {
Field int `json:"field,omitempty"`
}
var src = A{Field: 1}
var dst = map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("Field")
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithTag("json"))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"field": 1,
}, dst)
}
func TestStructToMap_WithInterface(t *testing.T) {
type user struct {
A string
B interface{}
C interface{}
}
type c struct {
A int
B interface{}
}
mask := fieldmask_utils.MaskFromString("A,B,C")
src := &user{
A: "nick",
B: []int{1, 2, 3, 4},
C: c{A: 42, B: map[string]interface{}{"hi": 34}},
}
dst := make(map[string]interface{})
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithTag(`json`))
assert.Nil(t, err)
expected := map[string]interface{}{
"A": "nick",
"B": []int{1, 2, 3, 4},
"C": map[string]interface{}{"A": 42, "B": map[string]interface{}{"hi": 34}},
}
assert.Equal(t, expected, dst)
}
func TestStructToMap_PtrToInt(t *testing.T) {
type example struct {
MyInt *int64
WhatEver string
}
mask := fieldmask_utils.MaskFromString("MyInt,WhatEver")
myInt := int64(42)
src := &example{
MyInt: &myInt,
WhatEver: "hello",
}
dst := make(map[string]interface{})
err := fieldmask_utils.StructToMap(mask, src, dst)
assert.Nil(t, err)
expected := map[string]interface{}{
"MyInt": int64(42),
"WhatEver": "hello",
}
assert.Equal(t, expected, dst)
}
func TestStructToMap_DifferentTypeWithSameDstKey(t *testing.T) {
t.Skip("this is a programming error which is expected to panic instead of returning an error")
type BB struct {
Field int
}
type A1 struct {
FieldA []int
FieldB []BB `json:"FieldA"`
}
var src1 = A1{FieldA: []int{1, 2}, FieldB: []BB{{1}, {2}}}
var dst1 = map[string]interface{}{}
mask := fieldmask_utils.MaskFromString("FieldA,FieldB")
err := fieldmask_utils.StructToMap(mask, src1, dst1, fieldmask_utils.WithTag("json"))
require.Error(t, err)
type A2 struct {
FieldA [2]int
FieldB [2]BB `json:"FieldA"`
}
var src2 = A2{FieldA: [2]int{1, 2}, FieldB: [2]BB{{1}, {2}}}
var dst2 = map[string]interface{}{}
mask = fieldmask_utils.MaskFromString("FieldA,FieldB")
err = fieldmask_utils.StructToMap(mask, src2, dst2, fieldmask_utils.WithTag("json"))
require.Error(t, err)
}
func TestStructToMap_EmptySrcSlice_JsonEncode(t *testing.T) {
type A struct{}
type B struct {
As []*A
}
src := &B{[]*A{}}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("As")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
jsonStr, _ := json.Marshal(dst)
assert.Equal(t, "{\"As\":[]}", string(jsonStr))
}
func TestStructToMap_NilSrcSlice_JsonEncode(t *testing.T) {
t.Skip("the behavior that this test verifies has changed")
type A struct{}
type B struct {
As []*A
}
src := &B{}
dst := make(map[string]interface{})
mask := fieldmask_utils.MaskFromString("As")
err := fieldmask_utils.StructToMap(mask, src, dst)
require.NoError(t, err)
jsonStr, _ := json.Marshal(dst)
assert.Equal(t, "{\"As\":null}", string(jsonStr))
}
func TestStructToStruct_CopySlice_WithDiffentAddr_WithDifferentFieldName(t *testing.T) {
type A struct {
Field1 []int
Field2 []int
}
var src = &A{
Field1: []int{1, 2, 3},
Field2: []int{1, 2, 3},
}
var field1 = reflect.ValueOf(src).Elem().FieldByName("Field1")
var dst = &A{}
var mask = fieldmask_utils.MaskFromString("Field1,Field2")
var err = fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(
func(src *reflect.Value) int {
if src.Pointer() == (&field1).Pointer() {
return 2
} else {
return src.Len()
}
},
))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: []int{1, 2},
Field2: []int{1, 2, 3},
}, dst)
}
func TestStructToStruct_CopySlice_WithSameAddr_WithDifferentFieldName(t *testing.T) {
t.Skip("Not Address this problem")
type A struct {
Field1 []int
Field2 []int
}
var arr = []int{1, 2, 3}
var src = &A{
Field1: arr,
Field2: arr,
}
var field1 = reflect.ValueOf(src).Elem().FieldByName("Field1")
var dst = &A{}
var mask = fieldmask_utils.MaskFromString("Field1,Field2")
var err = fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(
func(src *reflect.Value) int {
if src.Pointer() == (&field1).Pointer() {
return 2
} else {
return src.Len()
}
},
))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: []int{1, 2},
Field2: []int{1, 2, 3},
}, dst)
}
func TestStructToStruct_CopyArraySizeAccordingFieldName(t *testing.T) {
type A struct {
Field1 [3]int
Field2 [3]int
}
var src = &A{
Field1: [3]int{1, 2, 3},
Field2: [3]int{1, 2, 3},
}
var field1 = reflect.ValueOf(src).Elem().FieldByName("Field1")
var dst = &A{}
var mask = fieldmask_utils.MaskFromString("Field1,Field2")
var err = fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithCopyListSize(
func(src *reflect.Value) int {
if src.Addr() == (&field1).Addr() {
return 2
} else {
return src.Len()
}
},
))
require.NoError(t, err)
assert.Equal(t, &A{
Field1: [3]int{1, 2},
Field2: [3]int{1, 2, 3},
}, dst)
}
func TestStructToStruct_WithSrcTag(t *testing.T) {
type A struct {
Field1 string
Field2 int `db:"some_field"`
}
type B struct {
Field1 string `struct:"a_name"`
A A `db:"another_name,omitempty"`
}
src := &B{
Field1: "B Field1",
A: A{
Field1: "A Field 1",
Field2: 1,
},
}
dst := &B{}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
err := fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithSrcTag("db"))
require.NoError(t, err)
assert.Equal(t, &B{Field1: src.Field1}, dst)
mask, _ = fieldmask_utils.MaskFromPaths([]string{"Field1", "another_name.some_field"}, func(s string) string { return s })
dst = &B{}
err = fieldmask_utils.StructToStruct(mask, src, dst, fieldmask_utils.WithSrcTag("db"))
require.NoError(t, err)
assert.Equal(t, &B{Field1: src.Field1, A: A{Field2: src.A.Field2}}, dst)
}
func TestStructToMap_WithSrcTag(t *testing.T) {
type A struct {
Field1 string
Field2 int `db:"some_field1" json:"some_field1_json"`
Field3 bool `db:"some_field2" json:"some_field2_json"`
}
type B struct {
Field1 string `struct:"a_name"`
A A `db:"another_name,omitempty" json:"another_name_json"`
}
src := &B{
Field1: "B Field1",
A: A{
Field1: "A Field 1",
Field2: 1,
},
}
mask := fieldmask_utils.MaskFromString("Field1,A{Field2}")
dst := make(map[string]interface{})
err := fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithTag("json"), fieldmask_utils.WithSrcTag("db"))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
}, dst)
mask, _ = fieldmask_utils.MaskFromPaths([]string{"Field1", "another_name.some_field1", "another_name.some_field2"}, func(s string) string { return s })
dst = make(map[string]interface{})
err = fieldmask_utils.StructToMap(mask, src, dst, fieldmask_utils.WithTag("json"), fieldmask_utils.WithSrcTag("db"))
require.NoError(t, err)
assert.Equal(t, map[string]interface{}{
"Field1": src.Field1,
"another_name_json": map[string]interface{}{
"some_field1_json": src.A.Field2,
"some_field2_json": false,
},
}, dst)
}
func TestStructToStruct_WithConverterHook(t *testing.T) {
type E struct {
Field1 string
}
type F struct {
Field1 string
}
type A struct {
Field1 string
Field2 []*E
Field3 []*E
Field4 []string
}
src := &A{
Field1: " 42 ",
Field2: nil,
Field3: []*E{
{Field1: "foo"},
},
Field4: []string{" 3.141 ", "-273.15"},
}
type B struct {
Field1 int64
Field2 []*F
Field3 []*F
Field4 []float64
}
dst := &B{}
mask := fieldmask_utils.MaskFromString("Field1,Field2,Field3,Field4")
// test original error due to no conversion
err := fieldmask_utils.StructToStruct(mask, src, dst)
assert.Error(t, err)
// test conversion errors are critical
err = fieldmask_utils.StructToStruct(mask, src, dst,
fieldmask_utils.WithConverterHook(func(src, dst *reflect.Value) (interface{}, error) {
return nil, errors.New("dummy error")
}))
require.Error(t, err)
assert.ErrorContains(t, err, "dummy error")
// test incompatible conversion still returns original error
err = fieldmask_utils.StructToStruct(mask, src, dst,
fieldmask_utils.WithConverterHook(func(src, dst *reflect.Value) (interface{}, error) {
return 3.14, nil
}))
require.Error(t, err)
assert.ErrorContains(t, err, "differs from dst kind")
// test successful conversion
err = fieldmask_utils.StructToStruct(mask, src, dst,
// convert string to int64
fieldmask_utils.WithConverterHook(func(src, dst *reflect.Value) (interface{}, error) {
data := src.Interface()
if src.Kind() != reflect.String ||
dst.Kind() != reflect.Int64 {
return data, nil
}
raw, ok := data.(string)
if !ok {
return data, nil
}
return strconv.ParseInt(strings.TrimSpace(raw), 10, 64)
}),
// convert string to float64
fieldmask_utils.WithConverterHook(func(src, dst *reflect.Value) (interface{}, error) {
data := src.Interface()
if src.Kind() != reflect.String ||
dst.Kind() != reflect.Float64 {
return data, nil
}
raw, ok := data.(string)
if !ok {
return data, nil
}
return strconv.ParseFloat(strings.TrimSpace(raw), 64)
}))
require.NoError(t, err)
assert.Equal(t, &B{
Field1: 42,
Field2: nil,
Field3: []*F{
{Field1: "foo"},
},
Field4: []float64{3.141, -273.15},
}, dst)
}
================================================
FILE: go.mod
================================================
module github.com/mennanov/fieldmask-utils
go 1.16
require (
github.com/kr/text v0.2.0 // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.0
google.golang.org/genproto v0.0.0-20220531173845-685668d2de03
google.golang.org/protobuf v1.33.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220531173845-685668d2de03 h1:FG2YhwyltdDPC/0XuwzU0dijPcTzvfTtst0QdlDxoMU=
google.golang.org/genproto v0.0.0-20220531173845-685668d2de03/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
================================================
FILE: mask.go
================================================
package fieldmask_utils
import (
"fmt"
"strings"
"github.com/pkg/errors"
"google.golang.org/genproto/protobuf/field_mask"
)
// FieldFilter is an interface used by the copying function to filter fields that are needed to be copied.
type FieldFilter interface {
// Filter should return a corresponding FieldFilter for the given fieldName and a boolean result. If result is true
// then the field is copied, skipped otherwise.
Filter(fieldName string) (FieldFilter, bool)
// Returns true if the FieldFilter is empty. In this case all fields are copied.
IsEmpty() bool
}
// FieldFilterContainer is a FieldFilter with additional methods Get and Set.
type FieldFilterContainer interface {
FieldFilter
// Get gets the FieldFilter for the given field name. Result is false if the filter is not found.
Get(fieldName string) (filter FieldFilterContainer, result bool)
// Set sets the FieldFilter for the given field name.
Set(fieldName string, filter FieldFilterContainer)
}
// Mask is a tree-based implementation of a FieldFilter.
type Mask map[string]FieldFilterContainer
// Get gets the FieldFilter for the given field name. Result is false if the filter is not found.
func (m Mask) Get(fieldName string) (FieldFilterContainer, bool) {
f, ok := m[fieldName]
return f, ok
}
// Set sets the FieldFilter for the given field name.
func (m Mask) Set(fieldName string, filter FieldFilterContainer) {
m[fieldName] = filter
}
// Compile time interface check.
var _ FieldFilter = Mask{}
// Filter returns true for those fieldNames that exist in the underlying map.
// Field names that start with "XXX_" are ignored as unexported.
func (m Mask) Filter(fieldName string) (FieldFilter, bool) {
if len(m) == 0 {
// If the mask is empty choose all the exported fields.
return Mask{}, !strings.HasPrefix(fieldName, "XXX_")
}
subFilter, ok := m[fieldName]
if !ok {
subFilter = Mask{}
}
return subFilter, ok
}
// IsEmpty returns true of the mask is empty.
func (m Mask) IsEmpty() bool {
return len(m) == 0
}
func mapToString(m map[string]FieldFilterContainer) string {
if len(m) == 0 {
return ""
}
var result []string
for fieldName, maskNode := range m {
r := fieldName
var sub string
if stringer, ok := maskNode.(fmt.Stringer); ok {
sub = stringer.String()
} else {
sub = fmt.Sprint(maskNode)
}
if sub != "" {
r += "{" + sub + "}"
}
result = append(result, r)
}
return strings.Join(result, ",")
}
func (m Mask) String() string {
return mapToString(m)
}
// MaskInverse is an inversed version of a Mask (will copy all the fields except those mentioned in the mask).
type MaskInverse map[string]FieldFilterContainer
// Get gets the FieldFilter for the given field name. Result is false if the filter is not found.
func (m MaskInverse) Get(fieldName string) (FieldFilterContainer, bool) {
f, ok := m[fieldName]
return f, ok
}
// Set sets the FieldFilter for the given field name.
func (m MaskInverse) Set(fieldName string, filter FieldFilterContainer) {
m[fieldName] = filter
}
// Filter returns true for those fieldNames that do NOT exist in the underlying map.
// Field names that start with "XXX_" are ignored as unexported.
func (m MaskInverse) Filter(fieldName string) (FieldFilter, bool) {
subFilter, ok := m[fieldName]
if !ok {
return MaskInverse{}, !strings.HasPrefix(fieldName, "XXX_")
}
if subFilter == nil {
return nil, false
}
return subFilter, !subFilter.IsEmpty()
}
// IsEmpty returns true if the mask is empty.
func (m MaskInverse) IsEmpty() bool {
return len(m) == 0
}
func (m MaskInverse) String() string {
return mapToString(m)
}
// MaskFromProtoFieldMask creates a Mask from the given FieldMask.
func MaskFromProtoFieldMask(fm *field_mask.FieldMask, naming func(string) string) (Mask, error) {
return MaskFromPaths(fm.GetPaths(), naming)
}
// MaskInverseFromProtoFieldMask creates a MaskInverse from the given FieldMask.
func MaskInverseFromProtoFieldMask(fm *field_mask.FieldMask, naming func(string) string) (MaskInverse, error) {
return MaskInverseFromPaths(fm.GetPaths(), naming)
}
// MaskFromPaths creates a new Mask from the given paths.
func MaskFromPaths(paths []string, naming func(string) string) (Mask, error) {
mask, err := FieldFilterFromPaths(paths, naming, func() FieldFilterContainer {
return make(Mask)
})
if mask != nil {
return mask.(Mask), err
}
return nil, err
}
// MaskInverseFromPaths creates a new MaskInverse from the given paths.
func MaskInverseFromPaths(paths []string, naming func(string) string) (MaskInverse, error) {
mask, err := FieldFilterFromPaths(paths, naming, func() FieldFilterContainer {
return make(MaskInverse)
})
if mask != nil {
return mask.(MaskInverse), err
}
return nil, err
}
// FieldFilterFromPaths creates a new FieldFilter from the given paths.
func FieldFilterFromPaths(paths []string, naming func(string) string, filter func() FieldFilterContainer) (FieldFilterContainer, error) {
root := filter()
for _, path := range paths {
mask := root
for _, fieldName := range strings.Split(path, ".") {
if fieldName == "" {
return nil, errors.Errorf("invalid fieldName FieldFilter format: \"%s\"", path)
}
newFieldName := naming(fieldName)
subNode, ok := mask.Get(newFieldName)
if !ok {
mask.Set(newFieldName, filter())
subNode, _ = mask.Get(newFieldName)
}
mask = subNode
}
}
return root, nil
}
// MaskFromString creates a new Mask instance from a given string.
// Use in tests only. See FieldFilterFromString for details.
func MaskFromString(s string) Mask {
return FieldFilterFromString(s, func() FieldFilterContainer {
return make(Mask)
}).(Mask)
}
// MaskInverseFromString creates a new MaskInverse instance from a given string.
// Use in tests only. See FieldFilterFromString for details.
func MaskInverseFromString(s string) MaskInverse {
return FieldFilterFromString(s, func() FieldFilterContainer {
return make(MaskInverse)
}).(MaskInverse)
}
// FieldFilterFromString creates a new FieldFilterContainer from string.
// Input string is supposed to be a valid string representation of a FieldFilter like "a,b,c{d,e{f,g}},d".
// Use it in tests only as the input string is not validated and the underlying function panics in case of a
// parse error.
func FieldFilterFromString(input string, filter func() FieldFilterContainer) FieldFilterContainer {
var fieldName []string
mask := filter()
masks := []FieldFilterContainer{mask}
for pos, r := range input {
char := string(r)
switch char {
case " ", "\n", "\t":
// Skip white spaces.
case ",":
if len(fieldName) != 0 {
mask.Set(strings.Join(fieldName, ""), filter())
fieldName = nil
}
case "{":
if len(fieldName) == 0 {
panic(fmt.Sprintf("invalid mask format at position %d: got '{', expected a character", pos))
}
subMask := filter()
mask.Set(strings.Join(fieldName, ""), subMask)
fieldName = nil
masks = append(masks, mask)
mask = subMask
case "}":
if len(fieldName) != 0 {
mask.Set(strings.Join(fieldName, ""), filter())
fieldName = nil
}
mask = masks[len(masks)-1]
masks = masks[:len(masks)-1]
default:
fieldName = append(fieldName, char)
}
}
if len(fieldName) != 0 {
mask.Set(strings.Join(fieldName, ""), filter())
}
return mask
}
================================================
FILE: mask_test.go
================================================
package fieldmask_utils_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/genproto/protobuf/field_mask"
fieldmask_utils "github.com/mennanov/fieldmask-utils"
)
func TestMask_String(t *testing.T) {
mask := fieldmask_utils.MaskFromString("a{b{c}}")
assert.Equal(t, "a{b{c}}", mask.String())
}
func TestMaskInverse_String(t *testing.T) {
mask := fieldmask_utils.MaskInverseFromString("a{b{c}}")
assert.Equal(t, "a{b{c}}", mask.String())
}
func TestFieldFilterFromPaths_Success(t *testing.T) {
eye := func(s string) string { return s }
testCases := []struct {
mask *field_mask.FieldMask
expectedTree string
}{
{
&field_mask.FieldMask{Paths: []string{
"a", // overwritten by the paths below (a.*)
"a.b.c",
"a.b.d",
"a.c.d",
"b.c.d",
"a", // has no effect, since more strict rules are applied above
"c",
}},
"a{b{c,d},c{d}},b{c{d}},c",
},
{
&field_mask.FieldMask{Paths: []string{"a", "b", "b", "a"}},
"a,b",
},
{
&field_mask.FieldMask{Paths: []string{}},
"",
},
}
for _, testCase := range testCases {
maskFromProto, err := fieldmask_utils.MaskFromProtoFieldMask(testCase.mask, eye)
require.NoError(t, err)
maskFromPaths, err := fieldmask_utils.MaskFromPaths(testCase.mask.Paths, eye)
require.NoError(t, err)
assert.Equal(t, fieldmask_utils.MaskFromString(testCase.expectedTree), maskFromProto)
assert.Equal(t, maskFromProto, maskFromPaths)
// MaskInverse
maskInverseFromProto, err := fieldmask_utils.MaskInverseFromProtoFieldMask(testCase.mask, eye)
require.NoError(t, err)
maskInverseFromPaths, err := fieldmask_utils.MaskInverseFromPaths(testCase.mask.Paths, eye)
require.NoError(t, err)
fs := fieldmask_utils.MaskInverseFromString(testCase.expectedTree)
assert.Equal(t, fs, maskInverseFromProto)
assert.Equal(t, maskInverseFromProto, maskInverseFromPaths)
}
}
func TestMaskFromProtoFieldMask_Failure(t *testing.T) {
testCases := []*field_mask.FieldMask{
{Paths: []string{"a", ".a"}},
{Paths: []string{"."}},
{Paths: []string{"a.b.c.d.e", "a.b."}},
}
for _, fieldMask := range testCases {
_, err := fieldmask_utils.MaskFromProtoFieldMask(fieldMask, func(s string) string { return s })
assert.NotNil(t, err)
}
}
func TestMaskFromString(t *testing.T) {
testCases := []struct {
input string
expectedMask fieldmask_utils.Mask
length int
}{
{
"foo,bar{c{d,e{f,g,h}}}",
fieldmask_utils.Mask{
"foo": fieldmask_utils.Mask{},
"bar": fieldmask_utils.Mask{
"c": fieldmask_utils.Mask{
"d": fieldmask_utils.Mask{},
"e": fieldmask_utils.Mask{
"f": fieldmask_utils.Mask{},
"g": fieldmask_utils.Mask{},
"h": fieldmask_utils.Mask{},
},
},
},
}, 2,
},
{"foo, bar{c {d,e{f,\ng,h}}},\tt", fieldmask_utils.Mask{
"foo": fieldmask_utils.Mask{},
"bar": fieldmask_utils.Mask{
"c": fieldmask_utils.Mask{
"d": fieldmask_utils.Mask{},
"e": fieldmask_utils.Mask{
"f": fieldmask_utils.Mask{},
"g": fieldmask_utils.Mask{},
"h": fieldmask_utils.Mask{},
},
},
},
"t": fieldmask_utils.Mask{},
}, 3},
{"foo", fieldmask_utils.Mask{"foo": fieldmask_utils.Mask{}}, 1},
{"foo,bar", fieldmask_utils.Mask{
"foo": fieldmask_utils.Mask{},
"bar": fieldmask_utils.Mask{},
}, 2},
{"foo,bar{c},d,e", fieldmask_utils.Mask{
"foo": fieldmask_utils.Mask{},
"bar": fieldmask_utils.Mask{
"c": fieldmask_utils.Mask{},
},
"d": fieldmask_utils.Mask{},
"e": fieldmask_utils.Mask{},
}, 4},
{"", fieldmask_utils.Mask{}, 0},
}
for _, testCase := range testCases {
mask := fieldmask_utils.MaskFromString(testCase.input)
assert.Equal(t, testCase.expectedMask, mask)
assert.Equal(t, testCase.length, len(mask))
}
}
================================================
FILE: revive.toml
================================================
ignoreGeneratedHeader = false
severity = "warning"
confidence = 0.8
errorCode = 1
warningCode = 1
#
[rule.blank-imports]
[rule.context-as-argument]
[rule.context-keys-type]
[rule.dot-imports]
[rule.empty-block]
[rule.error-naming]
[rule.error-return]
[rule.error-strings]
[rule.errorf]
[rule.exported]
[rule.if-return]
[rule.increment-decrement]
[rule.indent-error-flow]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.redefines-builtin-id]
[rule.superfluous-else]
[rule.time-naming]
[rule.unexported-return]
[rule.unreachable-code]
[rule.unused-parameter]
[rule.var-declaration]
#
================================================
FILE: testproto/test.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.1
// source: test.proto
package testproto
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Role int32
const (
Role_UNKNOWN Role = 0
Role_REGULAR Role = 1
Role_ADMIN Role = 2
)
// Enum value maps for Role.
var (
Role_name = map[int32]string{
0: "UNKNOWN",
1: "REGULAR",
2: "ADMIN",
}
Role_value = map[string]int32{
"UNKNOWN": 0,
"REGULAR": 1,
"ADMIN": 2,
}
)
func (x Role) Enum() *Role {
p := new(Role)
*p = x
return p
}
func (x Role) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Role) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto_enumTypes[0].Descriptor()
}
func (Role) Type() protoreflect.EnumType {
return &file_test_proto_enumTypes[0]
}
func (x Role) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Role.Descriptor instead.
func (Role) EnumDescriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{0}
}
type Permission int32
const (
Permission_READ Permission = 0
Permission_WRITE Permission = 1
Permission_EXECUTE Permission = 2
)
// Enum value maps for Permission.
var (
Permission_name = map[int32]string{
0: "READ",
1: "WRITE",
2: "EXECUTE",
}
Permission_value = map[string]int32{
"READ": 0,
"WRITE": 1,
"EXECUTE": 2,
}
)
func (x Permission) Enum() *Permission {
p := new(Permission)
*p = x
return p
}
func (x Permission) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Permission) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto_enumTypes[1].Descriptor()
}
func (Permission) Type() protoreflect.EnumType {
return &file_test_proto_enumTypes[1]
}
func (x Permission) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Permission.Descriptor instead.
func (Permission) EnumDescriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{1}
}
type Image struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
OriginalUrl string `protobuf:"bytes,1,opt,name=original_url,json=originalUrl,proto3" json:"original_url,omitempty"`
ResizedUrl string `protobuf:"bytes,2,opt,name=resized_url,json=resizedUrl,proto3" json:"resized_url,omitempty"`
}
func (x *Image) Reset() {
*x = Image{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Image) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Image) ProtoMessage() {}
func (x *Image) ProtoReflect() protoreflect.Message {
mi := &file_test_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Image.ProtoReflect.Descriptor instead.
func (*Image) Descriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{0}
}
func (x *Image) GetOriginalUrl() string {
if x != nil {
return x.OriginalUrl
}
return ""
}
func (x *Image) GetResizedUrl() string {
if x != nil {
return x.ResizedUrl
}
return ""
}
type Metrics struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Height uint32 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
Weight uint32 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"`
}
func (x *Metrics) Reset() {
*x = Metrics{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Metrics) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Metrics) ProtoMessage() {}
func (x *Metrics) ProtoReflect() protoreflect.Message {
mi := &file_test_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Metrics.ProtoReflect.Descriptor instead.
func (*Metrics) Descriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{1}
}
func (x *Metrics) GetHeight() uint32 {
if x != nil {
return x.Height
}
return 0
}
func (x *Metrics) GetWeight() uint32 {
if x != nil {
return x.Weight
}
return 0
}
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
Role Role `protobuf:"varint,3,opt,name=role,proto3,enum=Role" json:"role,omitempty"`
Meta map[string]string `protobuf:"bytes,4,rep,name=meta,proto3" json:"meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Deactivated bool `protobuf:"varint,5,opt,name=deactivated,proto3" json:"deactivated,omitempty"`
Permissions []Permission `protobuf:"varint,6,rep,packed,name=permissions,proto3,enum=Permission" json:"permissions,omitempty"`
// Types that are assignable to Name:
// *User_MaleName
// *User_FemaleName
Name isUser_Name `protobuf_oneof:"name"`
Details []*anypb.Any `protobuf:"bytes,9,rep,name=details,proto3" json:"details,omitempty"`
Images []*Image `protobuf:"bytes,10,rep,name=images,proto3" json:"images,omitempty"`
Avatar *Image `protobuf:"bytes,11,opt,name=avatar,proto3" json:"avatar,omitempty"`
Tags []string `protobuf:"bytes,12,rep,name=tags,proto3" json:"tags,omitempty"`
Friends []*User `protobuf:"bytes,13,rep,name=friends,proto3" json:"friends,omitempty"`
ExtraUser *anypb.Any `protobuf:"bytes,14,opt,name=extra_user,json=extraUser,proto3" json:"extra_user,omitempty"`
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *User) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_test_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{2}
}
func (x *User) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
func (x *User) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *User) GetRole() Role {
if x != nil {
return x.Role
}
return Role_UNKNOWN
}
func (x *User) GetMeta() map[string]string {
if x != nil {
return x.Meta
}
return nil
}
func (x *User) GetDeactivated() bool {
if x != nil {
return x.Deactivated
}
return false
}
func (x *User) GetPermissions() []Permission {
if x != nil {
return x.Permissions
}
return nil
}
func (m *User) GetName() isUser_Name {
if m != nil {
return m.Name
}
return nil
}
func (x *User) GetMaleName() string {
if x, ok := x.GetName().(*User_MaleName); ok {
return x.MaleName
}
return ""
}
func (x *User) GetFemaleName() string {
if x, ok := x.GetName().(*User_FemaleName); ok {
return x.FemaleName
}
return ""
}
func (x *User) GetDetails() []*anypb.Any {
if x != nil {
return x.Details
}
return nil
}
func (x *User) GetImages() []*Image {
if x != nil {
return x.Images
}
return nil
}
func (x *User) GetAvatar() *Image {
if x != nil {
return x.Avatar
}
return nil
}
func (x *User) GetTags() []string {
if x != nil {
return x.Tags
}
return nil
}
func (x *User) GetFriends() []*User {
if x != nil {
return x.Friends
}
return nil
}
func (x *User) GetExtraUser() *anypb.Any {
if x != nil {
return x.ExtraUser
}
return nil
}
type isUser_Name interface {
isUser_Name()
}
type User_MaleName struct {
MaleName string `protobuf:"bytes,7,opt,name=male_name,json=maleName,proto3,oneof"`
}
type User_FemaleName struct {
FemaleName string `protobuf:"bytes,8,opt,name=female_name,json=femaleName,proto3,oneof"`
}
func (*User_MaleName) isUser_Name() {}
func (*User_FemaleName) isUser_Name() {}
type UpdateUserRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"`
}
func (x *UpdateUserRequest) Reset() {
*x = UpdateUserRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UpdateUserRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateUserRequest) ProtoMessage() {}
func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message {
mi := &file_test_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead.
func (*UpdateUserRequest) Descriptor() ([]byte, []int) {
return file_test_proto_rawDescGZIP(), []int{3}
}
func (x *UpdateUserRequest) GetUser() *User {
if x != nil {
return x.User
}
return nil
}
func (x *UpdateUserRequest) GetFieldMask() *fieldmaskpb.FieldMask {
if x != nil {
return x.FieldMask
}
return nil
}
var File_test_proto protoreflect.FileDescriptor
var file_test_proto_rawDesc = []byte{
0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d,
0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x05, 0x49, 0x6d, 0x61,
0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x75,
0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x64,
0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x69,
0x7a, 0x65, 0x64, 0x55, 0x72, 0x6c, 0x22, 0x39, 0x0a, 0x07, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69,
0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68,
0x74, 0x22, 0xa0, 0x04, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73,
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73,
0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x05, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x6f, 0x6c,
0x65, 0x12, 0x23, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x0f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x61, 0x63, 0x74, 0x69,
0x76, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x65, 0x61,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0b, 0x2e,
0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x09, 0x6d, 0x61, 0x6c, 0x65, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x61,
0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x66, 0x65, 0x6d, 0x61, 0x6c, 0x65,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x66,
0x65, 0x6d, 0x61, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74,
0x61, 0x69, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79,
0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1e, 0x0a, 0x06, 0x69, 0x6d, 0x61,
0x67, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x49, 0x6d, 0x61, 0x67,
0x65, 0x52, 0x06, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x76, 0x61,
0x74, 0x61, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x49, 0x6d, 0x61, 0x67,
0x65, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67,
0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1f, 0x0a,
0x07, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x05,
0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x33,
0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x55,
0x73, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x22, 0x69, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73,
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x04, 0x75, 0x73, 0x65,
0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04,
0x75, 0x73, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61,
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x2a,
0x2b, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x10,
0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x2a, 0x2e, 0x0a, 0x0a,
0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45,
0x41, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x01, 0x12,
0x0b, 0x0a, 0x07, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x10, 0x02, 0x42, 0x2f, 0x5a, 0x2d,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, 0x6e, 0x6e, 0x61,
0x6e, 0x6f, 0x76, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x6d, 0x61, 0x73, 0x6b, 0x2d, 0x75, 0x74,
0x69, 0x6c, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_test_proto_rawDescOnce sync.Once
file_test_proto_rawDescData = file_test_proto_rawDesc
)
func file_test_proto_rawDescGZIP() []byte {
file_test_proto_rawDescOnce.Do(func() {
file_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto_rawDescData)
})
return file_test_proto_rawDescData
}
var file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_test_proto_goTypes = []interface{}{
(Role)(0), // 0: Role
(Permission)(0), // 1: Permission
(*Image)(nil), // 2: Image
(*Metrics)(nil), // 3: Metrics
(*User)(nil), // 4: User
(*UpdateUserRequest)(nil), // 5: UpdateUserRequest
nil, // 6: User.MetaEntry
(*anypb.Any)(nil), // 7: google.protobuf.Any
(*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask
}
var file_test_proto_depIdxs = []int32{
0, // 0: User.role:type_name -> Role
6, // 1: User.meta:type_name -> User.MetaEntry
1, // 2: User.permissions:type_name -> Permission
7, // 3: User.details:type_name -> google.protobuf.Any
2, // 4: User.images:type_name -> Image
2, // 5: User.avatar:type_name -> Image
4, // 6: User.friends:type_name -> User
7, // 7: User.extra_user:type_name -> google.protobuf.Any
4, // 8: UpdateUserRequest.user:type_name -> User
8, // 9: UpdateUserRequest.field_mask:type_name -> google.protobuf.FieldMask
10, // [10:10] is the sub-list for method output_type
10, // [10:10] is the sub-list for method input_type
10, // [10:10] is the sub-list for extension type_name
10, // [10:10] is the sub-list for extension extendee
0, // [0:10] is the sub-list for field type_name
}
func init() { file_test_proto_init() }
func file_test_proto_init() {
if File_test_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Image); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Metrics); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UpdateUserRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_test_proto_msgTypes[2].OneofWrappers = []interface{}{
(*User_MaleName)(nil),
(*User_FemaleName)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_proto_rawDesc,
NumEnums: 2,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_proto_goTypes,
DependencyIndexes: file_test_proto_depIdxs,
EnumInfos: file_test_proto_enumTypes,
MessageInfos: file_test_proto_msgTypes,
}.Build()
File_test_proto = out.File
file_test_proto_rawDesc = nil
file_test_proto_goTypes = nil
file_test_proto_depIdxs = nil
}
================================================
FILE: testproto/test.proto
================================================
syntax = "proto3";
option go_package = "github.com/mennanov/fieldmask-utils/testproto";
import "google/protobuf/any.proto";
import "google/protobuf/field_mask.proto";
message Image {
string original_url = 1;
string resized_url = 2;
}
message Metrics {
uint32 height = 1;
uint32 weight = 2;
}
enum Role {
UNKNOWN = 0;
REGULAR = 1;
ADMIN = 2;
}
enum Permission {
READ = 0;
WRITE = 1;
EXECUTE = 2;
}
message User {
uint32 id = 1;
string username = 2;
Role role = 3;
map meta = 4;
bool deactivated = 5;
repeated Permission permissions = 6;
oneof name {
string male_name = 7;
string female_name = 8;
}
repeated google.protobuf.Any details = 9;
repeated Image images = 10;
Image avatar = 11;
repeated string tags = 12;
repeated User friends = 13;
google.protobuf.Any extra_user = 14;
}
message UpdateUserRequest {
User user = 1;
google.protobuf.FieldMask field_mask = 2;
}