-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from zoncoen/protobuf
feat(extractor/protobuf): add extractor for protobuf
- Loading branch information
Showing
11 changed files
with
682 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
export | ||
|
||
.DEFAULT_GOAL := generate | ||
|
||
UNAME_OS := $(shell uname -s) | ||
UNAME_ARCH := $(shell uname -m) | ||
|
||
BIN_DIR := $(CURDIR)/bin | ||
PROTO_DIR := $(CURDIR)/testdata/proto | ||
GEN_PB_DIR := $(CURDIR)/testdata/gen | ||
GOBIN := $(BIN_DIR) | ||
PATH := $(GOBIN):$(PATH) | ||
|
||
$(BIN_DIR): | ||
@mkdir -p $(BIN_DIR) | ||
|
||
PROTOC := $(BIN_DIR)/protoc | ||
PROTOC_VERSION := 25.1 | ||
PROTOC_OS := $(UNAME_OS) | ||
ifeq "$(UNAME_OS)" "Darwin" | ||
PROTOC_OS = osx | ||
endif | ||
PROTOC_ARCH := $(UNAME_ARCH) | ||
ifeq "$(UNAME_ARCH)" "arm64" | ||
PROTOC_ARCH = aarch_64 | ||
endif | ||
PROTOC_ZIP := protoc-$(PROTOC_VERSION)-$(PROTOC_OS)-$(PROTOC_ARCH).zip | ||
$(PROTOC): | $(BIN_DIR) | ||
@curl -sSOL \ | ||
"https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC_ZIP)" | ||
@unzip -j -o $(PROTOC_ZIP) -d $(BIN_DIR) bin/protoc | ||
@unzip -o $(PROTOC_ZIP) -d $(BIN_DIR) "include/*" | ||
@rm -f $(PROTOC_ZIP) | ||
|
||
PROTOC_GEN_GO := $(BIN_DIR)/protoc-gen-go | ||
$(PROTOC_GEN_GO): | $(BIN_DIR) | ||
@go install google.golang.org/protobuf/cmd/[email protected] | ||
|
||
PROTOC_GEN_GO_GRPC := $(BIN_DIR)/protoc-gen-go-grpc | ||
$(PROTOC_GEN_GO_GRPC): | $(BIN_DIR) | ||
@go install google.golang.org/grpc/cmd/[email protected] | ||
|
||
PROTOC_OPTION := -I$(PROTO_DIR) | ||
PROTOC_GO_OPTION := --plugin=${PROTOC_GEN_GO} --go_out=$(GEN_PB_DIR) --go_opt=paths=source_relative | ||
PROTOC_GO_GRPC_OPTION := --go-grpc_out=require_unimplemented_servers=false:$(GEN_PB_DIR) --go-grpc_opt=paths=source_relative | ||
.PHONY: generate | ||
generate: $(PROTOC) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) | ||
@mkdir -p $(GEN_PB_DIR) | ||
@find $(PROTO_DIR) -name '*.proto' | xargs -P8 protoc $(PROTOC_OPTION) $(PROTOC_GO_OPTION) $(PROTOC_GO_GRPC_OPTION) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package protobuf_test | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/zoncoen/query-go" | ||
|
||
protobufextractor "github.com/zoncoen/query-go/extractor/protobuf" | ||
testpb "github.com/zoncoen/query-go/extractor/protobuf/testdata/gen/testpb" | ||
) | ||
|
||
func ExampleExtractFunc() { | ||
v := testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
} | ||
q := query.New( | ||
query.CustomExtractFunc(protobufextractor.ExtractFunc()), | ||
query.CustomIsInlineStructFieldFunc(protobufextractor.OneofIsInlineStructFieldFunc()), | ||
).Key("B").Key("bar_value") | ||
got, err := q.Extract(v) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
fmt.Println(got) | ||
// Output: | ||
// yyy | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module github.com/zoncoen/query-go/extractor/protobuf | ||
|
||
go 1.21.6 | ||
|
||
require ( | ||
github.com/zoncoen/query-go v1.3.0 | ||
github.com/zoncoen/query-go/extractor/protobuf/testdata/gen v0.0.0-00010101000000-000000000000 | ||
) | ||
|
||
replace github.com/zoncoen/query-go/extractor/protobuf/testdata/gen => ./testdata/gen/ | ||
|
||
require ( | ||
github.com/pkg/errors v0.9.1 // indirect | ||
google.golang.org/protobuf v1.32.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/zoncoen/query-go v1.3.0 h1:wL5e3jxP1l3mMueyrsnj7KOtAvSe1JNzW1FaLhD5rsQ= | ||
github.com/zoncoen/query-go v1.3.0/go.mod h1:rbLi2EwarQtEJ5cNg9LFmAnleihBLsQSc1ow7vA8nms= | ||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= | ||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package protobuf | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
"strings" | ||
|
||
"github.com/zoncoen/query-go" | ||
) | ||
|
||
// ExtractFunc is a function for query.CustomExtractFunc option to extract values by protobuf struct tag. | ||
func ExtractFunc() func(query.ExtractFunc) query.ExtractFunc { | ||
return func(f query.ExtractFunc) query.ExtractFunc { | ||
return func(in reflect.Value) (reflect.Value, bool) { | ||
v := in | ||
for { | ||
if v.IsValid() { | ||
if k := v.Kind(); k == reflect.Interface || k == reflect.Pointer { | ||
v = v.Elem() | ||
continue | ||
} | ||
} | ||
break | ||
} | ||
switch v.Kind() { | ||
case reflect.Struct: | ||
for i := 0; i < v.Type().NumField(); i++ { | ||
field := v.Type().FieldByIndex([]int{i}) | ||
if s := field.Tag.Get("protobuf"); s != "" { | ||
if v, found := f(reflect.ValueOf(&keyExtractor{v})); found { | ||
return v, true | ||
} | ||
} | ||
} | ||
} | ||
return f(in) | ||
} | ||
} | ||
} | ||
|
||
type keyExtractor struct { | ||
v reflect.Value | ||
} | ||
|
||
// ExtractByKey implements KeyExtractorContext interface. | ||
func (e *keyExtractor) ExtractByKey(ctx context.Context, key string) (any, bool) { | ||
ci := query.IsCaseInsensitive(ctx) | ||
if ci { | ||
key = strings.ToLower(key) | ||
} | ||
switch e.v.Kind() { | ||
case reflect.Struct: | ||
for i := 0; i < e.v.Type().NumField(); i++ { | ||
if s := e.v.Type().FieldByIndex([]int{i}).Tag.Get("protobuf"); s != "" { | ||
for _, opt := range strings.Split(s, ",") { | ||
kv := strings.Split(opt, "=") | ||
if len(kv) == 2 { | ||
k, v := kv[0], kv[1] | ||
if k == "name" { | ||
if ci { | ||
v = strings.ToLower(v) | ||
} | ||
if v == key { | ||
var resp any | ||
if field := e.v.Field(i); field.CanInterface() { | ||
resp = field.Interface() | ||
} | ||
return resp, true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return nil, false | ||
} | ||
|
||
// OneofIsInlineStructFieldFunc is a function for query.CustomIsInlineStructFieldFunc option to enable extracting values even if the oneof field name is omitted. | ||
func OneofIsInlineStructFieldFunc() func(reflect.StructField) bool { | ||
return func(f reflect.StructField) bool { | ||
return f.Tag.Get("protobuf_oneof") != "" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package protobuf | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/zoncoen/query-go" | ||
testpb "github.com/zoncoen/query-go/extractor/protobuf/testdata/gen/testpb" | ||
) | ||
|
||
func TestExtractFunc(t *testing.T) { | ||
t.Run("success", func(t *testing.T) { | ||
tests := map[string]struct { | ||
query *query.Query | ||
v any | ||
expect any | ||
}{ | ||
"by field name": { | ||
query: query.New( | ||
query.CustomExtractFunc(ExtractFunc()), | ||
).Key("Value").Key("B").Key("BarValue"), | ||
v: testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
}, | ||
expect: "yyy", | ||
}, | ||
"by struct tag": { | ||
query: query.New( | ||
query.CustomExtractFunc(ExtractFunc()), | ||
).Key("Value").Key("B").Key("bar_value"), | ||
v: testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
}, | ||
expect: "yyy", | ||
}, | ||
"by struct tag (case insensitive)": { | ||
query: query.New( | ||
query.CaseInsensitive(), | ||
query.CustomExtractFunc(ExtractFunc()), | ||
).Key("Value").Key("B").Key("BAR_VALUE"), | ||
v: testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
}, | ||
expect: "yyy", | ||
}, | ||
} | ||
for name, test := range tests { | ||
test := test | ||
t.Run(name, func(t *testing.T) { | ||
got, err := test.query.Extract(test.v) | ||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
if got != test.expect { | ||
t.Errorf("expect %v but got %v", test.expect, got) | ||
} | ||
}) | ||
} | ||
}) | ||
t.Run("failure", func(t *testing.T) { | ||
tests := map[string]struct { | ||
query *query.Query | ||
v any | ||
expect string | ||
}{ | ||
"not found": { | ||
query: query.New( | ||
query.CustomExtractFunc(ExtractFunc()), | ||
).Key("Value").Key("B").Key("BAR_VALUE"), | ||
v: testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
}, | ||
expect: `".Value.B.BAR_VALUE" not found`, | ||
}, | ||
} | ||
for name, test := range tests { | ||
test := test | ||
t.Run(name, func(t *testing.T) { | ||
_, err := test.query.Extract(test.v) | ||
if err == nil { | ||
t.Fatal("no error") | ||
} | ||
if got := err.Error(); !strings.Contains(got, test.expect) { | ||
t.Errorf("expect %v but got %v", test.expect, got) | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
func TestOneofIsInlineStructFieldFunc(t *testing.T) { | ||
t.Run("success", func(t *testing.T) { | ||
tests := map[string]struct { | ||
query *query.Query | ||
v any | ||
expect any | ||
}{ | ||
"omit .Value": { | ||
query: query.New( | ||
query.CustomIsInlineStructFieldFunc(OneofIsInlineStructFieldFunc()), | ||
).Key("B").Key("BarValue"), | ||
v: testpb.OneofMessage{ | ||
Value: &testpb.OneofMessage_B_{ | ||
B: &testpb.OneofMessage_B{ | ||
BarValue: "yyy", | ||
}, | ||
}, | ||
}, | ||
expect: "yyy", | ||
}, | ||
} | ||
for name, test := range tests { | ||
test := test | ||
t.Run(name, func(t *testing.T) { | ||
got, err := test.query.Extract(test.v) | ||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
if got != test.expect { | ||
t.Errorf("expect %v but got %v", test.expect, got) | ||
} | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/zoncoen/query-go/extractor/protobuf/testdata/gen | ||
|
||
go 1.21.6 | ||
|
||
require google.golang.org/protobuf v1.32.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= | ||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= |
Oops, something went wrong.