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:
# <block keep-sorted="asc" keep-unique>
- '1.17'
- '1.18'
- '1.19'
- '1.20'
- '1.21'
- '1.22'
# </block>
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
# <block keep-sorted="asc">
[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]
# </block>
================================================
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<string, string> 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;
}
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
SYMBOL INDEX (228 symbols across 6 files)
FILE: copy.go
function StructToStruct (line 17) | func StructToStruct(filter FieldFilter, src, dst interface{}, userOpts ....
function ensureCompatible (line 38) | func ensureCompatible(src, dst *reflect.Value) error {
function structToStruct (line 53) | func structToStruct(filter FieldFilter, src, dst *reflect.Value, userOpt...
type options (line 265) | type options struct
type mapVisitor (line 299) | type mapVisitor
type MapVisitorResult (line 303) | type MapVisitorResult struct
type Option (line 309) | type Option
function WithTag (line 312) | func WithTag(s string) Option {
function WithSrcTag (line 319) | func WithSrcTag(s string) Option {
function WithCopyListSize (line 326) | func WithCopyListSize(f func(src *reflect.Value) int) Option {
function WithMapVisitor (line 333) | func WithMapVisitor(visitor mapVisitor) Option {
function WithUnmarshalAllAny (line 339) | func WithUnmarshalAllAny(unmarshal bool) Option {
function WithConverterHook (line 346) | func WithConverterHook(converter func(src, dst *reflect.Value) (interfac...
function newDefaultOptions (line 352) | func newDefaultOptions() *options {
function fieldName (line 361) | func fieldName(tag string, f reflect.StructField) string {
function StructToMap (line 379) | func StructToMap(filter FieldFilter, src interface{}, dst map[string]int...
function structToMap (line 388) | func structToMap(filter FieldFilter, src, dst reflect.Value, userOptions...
function indirect (line 518) | func indirect(v reflect.Value) reflect.Value {
function isPrimitive (line 526) | func isPrimitive(kind reflect.Kind) bool {
function newValue (line 536) | func newValue(t reflect.Type) reflect.Value {
function isExported (line 553) | func isExported(f reflect.StructField) bool {
FILE: copy_proto_test.go
function init (line 20) | func init() {
function TestStructToStruct_Proto (line 96) | func TestStructToStruct_Proto(t *testing.T) {
function TestStructToStruct_ExistingAnyPreserved (line 129) | func TestStructToStruct_ExistingAnyPreserved(t *testing.T) {
function TestStructToStruct_PartialProtoSuccess (line 153) | func TestStructToStruct_PartialProtoSuccess(t *testing.T) {
function TestStructToStruct_MaskInverse (line 164) | func TestStructToStruct_MaskInverse(t *testing.T) {
function TestStructToStruct_NonProtoSuccess (line 194) | func TestStructToStruct_NonProtoSuccess(t *testing.T) {
function TestStructToStruct_MaskInverseFromMask (line 229) | func TestStructToStruct_MaskInverseFromMask(t *testing.T) {
function TestStructToStruct_NonProtoFail (line 262) | func TestStructToStruct_NonProtoFail(t *testing.T) {
function TestStructToStruct_UnknownAnyInSrcNoSubfieldMask (line 280) | func TestStructToStruct_UnknownAnyInSrcNoSubfieldMask(t *testing.T) {
function TestStructToStruct_UnknownAnyInDstNoSubfieldMask (line 297) | func TestStructToStruct_UnknownAnyInDstNoSubfieldMask(t *testing.T) {
function TestStructToStruct_UnknownAnyDefault (line 314) | func TestStructToStruct_UnknownAnyDefault(t *testing.T) {
function TestStructToStruct_UnknownAnySubfieldMask (line 330) | func TestStructToStruct_UnknownAnySubfieldMask(t *testing.T) {
function TestStructToMap_Success (line 345) | func TestStructToMap_Success(t *testing.T) {
function TestStructToMap_PartialProtoSuccess (line 374) | func TestStructToMap_PartialProtoSuccess(t *testing.T) {
FILE: copy_test.go
function TestStructToStruct_SimpleStruct (line 19) | func TestStructToStruct_SimpleStruct(t *testing.T) {
function TestStructToStruct_PtrToInt (line 38) | func TestStructToStruct_PtrToInt(t *testing.T) {
function TestStructToStruct_StructToPointer (line 56) | func TestStructToStruct_StructToPointer(t *testing.T) {
function TestStructToStruct_IntToPointer (line 93) | func TestStructToStruct_IntToPointer(t *testing.T) {
function TestStructToStruct_PointerToInt (line 115) | func TestStructToStruct_PointerToInt(t *testing.T) {
function TestStructToStruct_Incompatible (line 137) | func TestStructToStruct_Incompatible(t *testing.T) {
function TestStructToStruct_PtrToStruct_EmptyDst (line 154) | func TestStructToStruct_PtrToStruct_EmptyDst(t *testing.T) {
function TestStructToStruct_PtrToStruct_NonEmptyDst (line 197) | func TestStructToStruct_PtrToStruct_NonEmptyDst(t *testing.T) {
function TestStructToStruct_NestedStruct_EmptyDst (line 250) | func TestStructToStruct_NestedStruct_EmptyDst(t *testing.T) {
function TestStructToStruct_NestedStruct_EmptyDst_OptionDst (line 293) | func TestStructToStruct_NestedStruct_EmptyDst_OptionDst(t *testing.T) {
function TestStructToStruct_NestedStruct_NonEmptyDst (line 332) | func TestStructToStruct_NestedStruct_NonEmptyDst(t *testing.T) {
function TestStructToStruct_SliceOfStructs_EmptyDst (line 385) | func TestStructToStruct_SliceOfStructs_EmptyDst(t *testing.T) {
function TestStructToStruct_SliceOfStructs_NonEmptyDst (line 427) | func TestStructToStruct_SliceOfStructs_NonEmptyDst(t *testing.T) {
function TestStructToStruct_EntireSlice_NonEmptyDst (line 489) | func TestStructToStruct_EntireSlice_NonEmptyDst(t *testing.T) {
function TestStructToStruct_NilSrcSlice_NonEmptyDst (line 538) | func TestStructToStruct_NilSrcSlice_NonEmptyDst(t *testing.T) {
function TestStructToStruct_SliceOfPtrsToStruct_EmptyDst (line 578) | func TestStructToStruct_SliceOfPtrsToStruct_EmptyDst(t *testing.T) {
function TestStructToStruct_ArrayOfStructs_EmptyDst (line 620) | func TestStructToStruct_ArrayOfStructs_EmptyDst(t *testing.T) {
function TestStructToStruct_Array_DstLenLessThanSrc (line 670) | func TestStructToStruct_Array_DstLenLessThanSrc(t *testing.T) {
function TestStructToStruct_DifferentStructTypes (line 703) | func TestStructToStruct_DifferentStructTypes(t *testing.T) {
function TestStructToStruct_DifferentStructTypesNested (line 720) | func TestStructToStruct_DifferentStructTypesNested(t *testing.T) {
function TestStructToStruct_DifferentStructTypesPtrNested (line 753) | func TestStructToStruct_DifferentStructTypesPtrNested(t *testing.T) {
type StringerA (line 786) | type StringerA struct
method String (line 790) | func (a *StringerA) String() string {
type StringerB (line 794) | type StringerB struct
method String (line 798) | func (b *StringerB) String() string {
function TestStructToStruct_Interface_EmptyDst (line 802) | func TestStructToStruct_Interface_EmptyDst(t *testing.T) {
function TestStructToStruct_SameInterfaces_NonEmptyDst (line 823) | func TestStructToStruct_SameInterfaces_NonEmptyDst(t *testing.T) {
function TestStructToStruct_DifferentCompatibleInterfaces_NonEmptyDst (line 849) | func TestStructToStruct_DifferentCompatibleInterfaces_NonEmptyDst(t *tes...
type Logger (line 870) | type Logger interface
type LoggerImpl (line 874) | type LoggerImpl struct
method Log (line 878) | func (d *LoggerImpl) Log() string {
function TestStructToStruct_DifferentIncompatibleInterfaces (line 882) | func TestStructToStruct_DifferentIncompatibleInterfaces(t *testing.T) {
function TestStructToStruct_EmptyMask (line 907) | func TestStructToStruct_EmptyMask(t *testing.T) {
type StringerImpl (line 923) | type StringerImpl struct
method someMethod (line 927) | func (*StringerImpl) someMethod() {}
method String (line 928) | func (f *StringerImpl) String() string {
type StringerNonPtrImpl (line 932) | type StringerNonPtrImpl struct
method String (line 936) | func (s StringerNonPtrImpl) String() string {
function TestStructToStruct_SameInterfacesPtr_EmptyDst (line 940) | func TestStructToStruct_SameInterfacesPtr_EmptyDst(t *testing.T) {
function TestStructToStruct_SameInterfacesPtr_NonEmptyDst (line 961) | func TestStructToStruct_SameInterfacesPtr_NonEmptyDst(t *testing.T) {
function TestStructToStruct_SameInterfacesNonPtr_EmptyDst (line 987) | func TestStructToStruct_SameInterfacesNonPtr_EmptyDst(t *testing.T) {
function TestStructToStruct_SameInterfacesNonPtr_NonEmptyDst (line 1002) | func TestStructToStruct_SameInterfacesNonPtr_NonEmptyDst(t *testing.T) {
function TestStructToStruct_NonPtrDst (line 1019) | func TestStructToStruct_NonPtrDst(t *testing.T) {
function TestStructToStruct_DifferentDstKind (line 1030) | func TestStructToStruct_DifferentDstKind(t *testing.T) {
function TestStructToStruct_UnexportedFieldsPtr (line 1041) | func TestStructToStruct_UnexportedFieldsPtr(t *testing.T) {
function TestStructToStruct_UnexportedFields (line 1067) | func TestStructToStruct_UnexportedFields(t *testing.T) {
function TestStructToStruct_MaskWithInverseMask (line 1093) | func TestStructToStruct_MaskWithInverseMask(t *testing.T) {
function TestStructToStruct_InverseMaskWithMask (line 1129) | func TestStructToStruct_InverseMaskWithMask(t *testing.T) {
function TestStructToMap_NestedStruct_EmptyDst (line 1165) | func TestStructToMap_NestedStruct_EmptyDst(t *testing.T) {
function TestStructToMap_NestedStruct_EmptyDst_OptionDst (line 1193) | func TestStructToMap_NestedStruct_EmptyDst_OptionDst(t *testing.T) {
function TestStructToMap_NestedStruct_NonEmptyDst (line 1222) | func TestStructToMap_NestedStruct_NonEmptyDst(t *testing.T) {
function TestStructToMap_PtrToStruct_EmptyDst (line 1256) | func TestStructToMap_PtrToStruct_EmptyDst(t *testing.T) {
function TestStructToMap_PtrToStruct_NonEmptyDst (line 1284) | func TestStructToMap_PtrToStruct_NonEmptyDst(t *testing.T) {
function TestStructToMap_ArrayOfStructs_EmptyDst (line 1318) | func TestStructToMap_ArrayOfStructs_EmptyDst(t *testing.T) {
function TestStructToMap_SliceOfStructs_NonEmptyDst (line 1347) | func TestStructToMap_SliceOfStructs_NonEmptyDst(t *testing.T) {
function TestStructToMap_EntireSlicePrimitive_NonEmptyDst (line 1383) | func TestStructToMap_EntireSlicePrimitive_NonEmptyDst(t *testing.T) {
function TestStructToMap_EntireSlice_NonEmptyDst (line 1402) | func TestStructToMap_EntireSlice_NonEmptyDst(t *testing.T) {
function TestStructToMap_NilSrcSlice_NonEmptyDst (line 1452) | func TestStructToMap_NilSrcSlice_NonEmptyDst(t *testing.T) {
function TestStructToMap_EntireSlice_DstSliceLenIsLessThanSource (line 1482) | func TestStructToMap_EntireSlice_DstSliceLenIsLessThanSource(t *testing....
function TestStructToMap_Array_NonEmptyDst (line 1526) | func TestStructToMap_Array_NonEmptyDst(t *testing.T) {
function TestStructToMap_ArrayPrimitive_NonEmptyDst (line 1580) | func TestStructToMap_ArrayPrimitive_NonEmptyDst(t *testing.T) {
function TestStructToMap_EmptySliceSrc_NonEmptyArrayDst (line 1599) | func TestStructToMap_EmptySliceSrc_NonEmptyArrayDst(t *testing.T) {
function TestStructToStruct_CopyStructSlice_WithMaxCopyListSize (line 1618) | func TestStructToStruct_CopyStructSlice_WithMaxCopyListSize(t *testing.T) {
function TestStructToStruct_CopyIntSlice_WithMaxCopyListSize (line 1642) | func TestStructToStruct_CopyIntSlice_WithMaxCopyListSize(t *testing.T) {
function TestStructToStruct_CopyIntArray_WithMaxCopyListSize (line 1663) | func TestStructToStruct_CopyIntArray_WithMaxCopyListSize(t *testing.T) {
function TestStructToStruct_CopyStructArray_WithMaxCopyListSize (line 1683) | func TestStructToStruct_CopyStructArray_WithMaxCopyListSize(t *testing.T) {
function TestStructToMap_CopyStructSlice_WithMaxCopyListSize (line 1708) | func TestStructToMap_CopyStructSlice_WithMaxCopyListSize(t *testing.T) {
function TestStructToMap_CopyIntSlice_WithMaxCopyListSize (line 1732) | func TestStructToMap_CopyIntSlice_WithMaxCopyListSize(t *testing.T) {
function TestStructToMap_CopyStructArray_WithMaxCopyListSize (line 1753) | func TestStructToMap_CopyStructArray_WithMaxCopyListSize(t *testing.T) {
function TestStructToMap_CopyIntArray_WithMaxCopyListSize (line 1778) | func TestStructToMap_CopyIntArray_WithMaxCopyListSize(t *testing.T) {
function TestStructToMap_CopyStructWithPrivateFields_WithMapVisitor (line 1798) | func TestStructToMap_CopyStructWithPrivateFields_WithMapVisitor(t *testi...
function TestStructToMap_MapVisitorVisitsOnlyFilteredFields (line 1824) | func TestStructToMap_MapVisitorVisitsOnlyFilteredFields(t *testing.T) {
function TestStructToMap_WithMapVisitor_SkipsToNextField (line 1844) | func TestStructToMap_WithMapVisitor_SkipsToNextField(t *testing.T) {
function TestStructToStruct_CopySlice_WithDiffentItemKind (line 1869) | func TestStructToStruct_CopySlice_WithDiffentItemKind(t *testing.T) {
function TestStructToMap_CopySlice_WithDiffentItemKind (line 1895) | func TestStructToMap_CopySlice_WithDiffentItemKind(t *testing.T) {
function TestStructToStruct_CopySlice_WithDiffentItemType (line 1921) | func TestStructToStruct_CopySlice_WithDiffentItemType(t *testing.T) {
function TestStructToMap_CopySlice_WithDiffentItemType (line 1950) | func TestStructToMap_CopySlice_WithDiffentItemType(t *testing.T) {
function TestStructToStruct_WithNonStructSrcError (line 1979) | func TestStructToStruct_WithNonStructSrcError(t *testing.T) {
function TestStructToStruct_WithMultiTagComma (line 1988) | func TestStructToStruct_WithMultiTagComma(t *testing.T) {
function TestStructToMap_WithInterface (line 2002) | func TestStructToMap_WithInterface(t *testing.T) {
function TestStructToMap_PtrToInt (line 2031) | func TestStructToMap_PtrToInt(t *testing.T) {
function TestStructToMap_DifferentTypeWithSameDstKey (line 2054) | func TestStructToMap_DifferentTypeWithSameDstKey(t *testing.T) {
function TestStructToMap_EmptySrcSlice_JsonEncode (line 2080) | func TestStructToMap_EmptySrcSlice_JsonEncode(t *testing.T) {
function TestStructToMap_NilSrcSlice_JsonEncode (line 2097) | func TestStructToMap_NilSrcSlice_JsonEncode(t *testing.T) {
function TestStructToStruct_CopySlice_WithDiffentAddr_WithDifferentFieldName (line 2115) | func TestStructToStruct_CopySlice_WithDiffentAddr_WithDifferentFieldName...
function TestStructToStruct_CopySlice_WithSameAddr_WithDifferentFieldName (line 2145) | func TestStructToStruct_CopySlice_WithSameAddr_WithDifferentFieldName(t ...
function TestStructToStruct_CopyArraySizeAccordingFieldName (line 2177) | func TestStructToStruct_CopyArraySizeAccordingFieldName(t *testing.T) {
function TestStructToStruct_WithSrcTag (line 2206) | func TestStructToStruct_WithSrcTag(t *testing.T) {
function TestStructToMap_WithSrcTag (line 2235) | func TestStructToMap_WithSrcTag(t *testing.T) {
function TestStructToStruct_WithConverterHook (line 2273) | func TestStructToStruct_WithConverterHook(t *testing.T) {
FILE: mask.go
type FieldFilter (line 12) | type FieldFilter interface
type FieldFilterContainer (line 21) | type FieldFilterContainer interface
type Mask (line 30) | type Mask
method Get (line 33) | func (m Mask) Get(fieldName string) (FieldFilterContainer, bool) {
method Set (line 39) | func (m Mask) Set(fieldName string, filter FieldFilterContainer) {
method Filter (line 48) | func (m Mask) Filter(fieldName string) (FieldFilter, bool) {
method IsEmpty (line 61) | func (m Mask) IsEmpty() bool {
method String (line 86) | func (m Mask) String() string {
function mapToString (line 65) | func mapToString(m map[string]FieldFilterContainer) string {
type MaskInverse (line 91) | type MaskInverse
method Get (line 94) | func (m MaskInverse) Get(fieldName string) (FieldFilterContainer, bool) {
method Set (line 100) | func (m MaskInverse) Set(fieldName string, filter FieldFilterContainer) {
method Filter (line 106) | func (m MaskInverse) Filter(fieldName string) (FieldFilter, bool) {
method IsEmpty (line 118) | func (m MaskInverse) IsEmpty() bool {
method String (line 122) | func (m MaskInverse) String() string {
function MaskFromProtoFieldMask (line 127) | func MaskFromProtoFieldMask(fm *field_mask.FieldMask, naming func(string...
function MaskInverseFromProtoFieldMask (line 132) | func MaskInverseFromProtoFieldMask(fm *field_mask.FieldMask, naming func...
function MaskFromPaths (line 137) | func MaskFromPaths(paths []string, naming func(string) string) (Mask, er...
function MaskInverseFromPaths (line 148) | func MaskInverseFromPaths(paths []string, naming func(string) string) (M...
function FieldFilterFromPaths (line 159) | func FieldFilterFromPaths(paths []string, naming func(string) string, fi...
function MaskFromString (line 181) | func MaskFromString(s string) Mask {
function MaskInverseFromString (line 189) | func MaskInverseFromString(s string) MaskInverse {
function FieldFilterFromString (line 199) | func FieldFilterFromString(input string, filter func() FieldFilterContai...
FILE: mask_test.go
function TestMask_String (line 13) | func TestMask_String(t *testing.T) {
function TestMaskInverse_String (line 18) | func TestMaskInverse_String(t *testing.T) {
function TestFieldFilterFromPaths_Success (line 23) | func TestFieldFilterFromPaths_Success(t *testing.T) {
function TestMaskFromProtoFieldMask_Failure (line 68) | func TestMaskFromProtoFieldMask_Failure(t *testing.T) {
function TestMaskFromString (line 81) | func TestMaskFromString(t *testing.T) {
FILE: testproto/test.pb.go
constant _ (line 21) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 23) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type Role (line 26) | type Role
method Enum (line 48) | func (x Role) Enum() *Role {
method String (line 54) | func (x Role) String() string {
method Descriptor (line 58) | func (Role) Descriptor() protoreflect.EnumDescriptor {
method Type (line 62) | func (Role) Type() protoreflect.EnumType {
method Number (line 66) | func (x Role) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 71) | func (Role) EnumDescriptor() ([]byte, []int) {
constant Role_UNKNOWN (line 29) | Role_UNKNOWN Role = 0
constant Role_REGULAR (line 30) | Role_REGULAR Role = 1
constant Role_ADMIN (line 31) | Role_ADMIN Role = 2
type Permission (line 75) | type Permission
method Enum (line 97) | func (x Permission) Enum() *Permission {
method String (line 103) | func (x Permission) String() string {
method Descriptor (line 107) | func (Permission) Descriptor() protoreflect.EnumDescriptor {
method Type (line 111) | func (Permission) Type() protoreflect.EnumType {
method Number (line 115) | func (x Permission) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 120) | func (Permission) EnumDescriptor() ([]byte, []int) {
constant Permission_READ (line 78) | Permission_READ Permission = 0
constant Permission_WRITE (line 79) | Permission_WRITE Permission = 1
constant Permission_EXECUTE (line 80) | Permission_EXECUTE Permission = 2
type Image (line 124) | type Image struct
method Reset (line 133) | func (x *Image) Reset() {
method String (line 142) | func (x *Image) String() string {
method ProtoMessage (line 146) | func (*Image) ProtoMessage() {}
method ProtoReflect (line 148) | func (x *Image) ProtoReflect() protoreflect.Message {
method Descriptor (line 161) | func (*Image) Descriptor() ([]byte, []int) {
method GetOriginalUrl (line 165) | func (x *Image) GetOriginalUrl() string {
method GetResizedUrl (line 172) | func (x *Image) GetResizedUrl() string {
type Metrics (line 179) | type Metrics struct
method Reset (line 188) | func (x *Metrics) Reset() {
method String (line 197) | func (x *Metrics) String() string {
method ProtoMessage (line 201) | func (*Metrics) ProtoMessage() {}
method ProtoReflect (line 203) | func (x *Metrics) ProtoReflect() protoreflect.Message {
method Descriptor (line 216) | func (*Metrics) Descriptor() ([]byte, []int) {
method GetHeight (line 220) | func (x *Metrics) GetHeight() uint32 {
method GetWeight (line 227) | func (x *Metrics) GetWeight() uint32 {
type User (line 234) | type User struct
method Reset (line 257) | func (x *User) Reset() {
method String (line 266) | func (x *User) String() string {
method ProtoMessage (line 270) | func (*User) ProtoMessage() {}
method ProtoReflect (line 272) | func (x *User) ProtoReflect() protoreflect.Message {
method Descriptor (line 285) | func (*User) Descriptor() ([]byte, []int) {
method GetId (line 289) | func (x *User) GetId() uint32 {
method GetUsername (line 296) | func (x *User) GetUsername() string {
method GetRole (line 303) | func (x *User) GetRole() Role {
method GetMeta (line 310) | func (x *User) GetMeta() map[string]string {
method GetDeactivated (line 317) | func (x *User) GetDeactivated() bool {
method GetPermissions (line 324) | func (x *User) GetPermissions() []Permission {
method GetName (line 331) | func (m *User) GetName() isUser_Name {
method GetMaleName (line 338) | func (x *User) GetMaleName() string {
method GetFemaleName (line 345) | func (x *User) GetFemaleName() string {
method GetDetails (line 352) | func (x *User) GetDetails() []*anypb.Any {
method GetImages (line 359) | func (x *User) GetImages() []*Image {
method GetAvatar (line 366) | func (x *User) GetAvatar() *Image {
method GetTags (line 373) | func (x *User) GetTags() []string {
method GetFriends (line 380) | func (x *User) GetFriends() []*User {
method GetExtraUser (line 387) | func (x *User) GetExtraUser() *anypb.Any {
type isUser_Name (line 394) | type isUser_Name interface
type User_MaleName (line 398) | type User_MaleName struct
method isUser_Name (line 406) | func (*User_MaleName) isUser_Name() {}
type User_FemaleName (line 402) | type User_FemaleName struct
method isUser_Name (line 408) | func (*User_FemaleName) isUser_Name() {}
type UpdateUserRequest (line 410) | type UpdateUserRequest struct
method Reset (line 419) | func (x *UpdateUserRequest) Reset() {
method String (line 428) | func (x *UpdateUserRequest) String() string {
method ProtoMessage (line 432) | func (*UpdateUserRequest) ProtoMessage() {}
method ProtoReflect (line 434) | func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 447) | func (*UpdateUserRequest) Descriptor() ([]byte, []int) {
method GetUser (line 451) | func (x *UpdateUserRequest) GetUser() *User {
method GetFieldMask (line 458) | func (x *UpdateUserRequest) GetFieldMask() *fieldmaskpb.FieldMask {
function file_test_proto_rawDescGZIP (line 539) | func file_test_proto_rawDescGZIP() []byte {
function init (line 577) | func init() { file_test_proto_init() }
function file_test_proto_init (line 578) | func file_test_proto_init() {
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (148K chars).
[
{
"path": ".github/dependabot.yaml",
"chars": 173,
"preview": "version: 2\nupdates:\n - package-ecosystem: gomod\n directory: /\n schedule:\n interval: monthly\n groups:\n "
},
{
"path": ".github/workflows/blockwatch.yml",
"chars": 235,
"preview": "name: blockwatch\n\non:\n push:\n branches: [ \"master\" ]\n pull_request:\n branches: [ \"master\" ]\n\npermissions:\n cont"
},
{
"path": ".github/workflows/coverage.yml",
"chars": 503,
"preview": "name: Tests coverage\n\non: [ \"push\", \"pull_request\" ]\n\njobs:\n\n build:\n name: Coverage\n runs-on: ubuntu-latest\n "
},
{
"path": ".github/workflows/linter.yml",
"chars": 303,
"preview": "name: Linter\n\non: [ \"push\", \"pull_request\" ]\n\njobs:\n\n build:\n name: Linter\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": ".github/workflows/tests.yml",
"chars": 587,
"preview": "name: Go tests\n\non: [ push, pull_request ]\n\njobs:\n go-test:\n\n runs-on: ubuntu-latest\n strategy:\n matrix:\n "
},
{
"path": ".gitignore",
"chars": 41,
"preview": ".idea/\nvendor/\ncoverage.out\ncoverage.txt\n"
},
{
"path": ".pre-commit-config.yaml",
"chars": 244,
"preview": "repos:\n - repo: local\n hooks:\n - id: blockwatch\n name: blockwatch\n entry: bash -c 'git diff --pat"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2019 Renat Mennanov\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.md",
"chars": 6507,
"preview": "## Protobuf Field Mask utils for Go\n\n[. The extraction includes 19 files (131.3 KB), approximately 47.2k tokens, and a symbol index with 228 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.