diff --git a/README.md b/README.md
index c02bf00b..301d8f2b 100644
--- a/README.md
+++ b/README.md
@@ -263,7 +263,18 @@ When CTRL-D is entered, inputting will be aborted.
```
### Bytes type fields
-You can use byte literal and Unicode literal.
+You can pass bytes as a base64-encoded string.
+
+```
+> call UnaryBytes
+data (TYPE_BYTES) => SGVsbG8gV29ybGQh
+{
+ "message": "received: (bytes) 48 65 6c 6c 6f 20 57 6f 72 6c 64 21, (string) Hello World!"
+}
+```
+
+Or add the flag `--bytes-as-quoted-literals` to pass bytes as a (quoted) byte
+literal or Unicode literal string:
```
> call UnaryBytes
@@ -279,13 +290,7 @@ data (TYPE_BYTES) => \u65e5\u672c\u8a9e
}
```
-Or add the flag `--bytes-as-base64` to pass bytes as a base64-encoded string
-```
-> call UnaryBytes --bytes-as-base64
-data (TYPE_BYTES) => SGVsbG8gV29ybGQh
-```
-
-Or add the flag `--bytes-from-file` to read bytes from the provided relative path
+Or add the flag `--bytes-from-file` to read bytes from the provided relative path:
```
> call UnaryBytes --bytes-from-file
data (TYPE_BYTES) => ../relative/path/to/file
diff --git a/e2e/old_repl_test.go b/e2e/old_repl_test.go
index 5d4066d7..bca55b95 100644
--- a/e2e/old_repl_test.go
+++ b/e2e/old_repl_test.go
@@ -122,7 +122,11 @@ func TestE2E_OldREPL(t *testing.T) {
},
"call UnaryBytes": {
args: "testdata/test.proto",
- input: []interface{}{"call UnaryBytes", "\\u3084\\u306f\\u308a\\u4ffa\\u306e\\u9752\\u6625\\u30e9\\u30d6\\u30b3\\u30e1\\u306f\\u307e\\u3061\\u304c\\u3063\\u3066\\u3044\\u308b\\u3002"},
+ input: []interface{}{"call UnaryBytes", "44KE44Gv44KK5L+644Gu6Z2S5pil44Op44OW44Kz44Oh44Gv44G+44Gh44GM44Gj44Gm44GE44KL44CC"},
+ },
+ "call UnaryBytes with quoted literals": {
+ args: "testdata/test.proto",
+ input: []interface{}{"call UnaryBytes --bytes-as-quoted-literals", "\\u3084\\u306f\\u308a\\u4ffa\\u306e\\u9752\\u6625\\u30e9\\u30d6\\u30b3\\u30e1\\u306f\\u307e\\u3061\\u304c\\u3063\\u3066\\u3044\\u308b\\u3002"},
},
"call UnaryRepeatedEnum": {
args: "testdata/test.proto",
diff --git a/e2e/repl_test.go b/e2e/repl_test.go
index 79e6e135..7274b4c6 100644
--- a/e2e/repl_test.go
+++ b/e2e/repl_test.go
@@ -129,7 +129,11 @@ func TestE2E_REPL(t *testing.T) {
},
"call UnaryBytes": {
commonFlags: "--proto testdata/test.proto",
- input: []interface{}{"call UnaryBytes", "\\u3084\\u306f\\u308a\\u4ffa\\u306e\\u9752\\u6625\\u30e9\\u30d6\\u30b3\\u30e1\\u306f\\u307e\\u3061\\u304c\\u3063\\u3066\\u3044\\u308b\\u3002"},
+ input: []interface{}{"call UnaryBytes", "44KE44Gv44KK5L+644Gu6Z2S5pil44Op44OW44Kz44Oh44Gv44G+44Gh44GM44Gj44Gm44GE44KL44CC"},
+ },
+ "call UnaryBytes with quoted literals": {
+ commonFlags: "--proto testdata/test.proto",
+ input: []interface{}{"call UnaryBytes --bytes-as-quoted-literals", "\\u3084\\u306f\\u308a\\u4ffa\\u306e\\u9752\\u6625\\u30e9\\u30d6\\u30b3\\u30e1\\u306f\\u307e\\u3061\\u304c\\u3063\\u3066\\u3044\\u308b\\u3002"},
},
"call UnaryRepeatedEnum": {
commonFlags: "--proto testdata/test.proto",
diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unarybytes_with_quoted_literals.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unarybytes_with_quoted_literals.golden
new file mode 100644
index 00000000..62e67d9e
--- /dev/null
+++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unarybytes_with_quoted_literals.golden
@@ -0,0 +1,4 @@
+{
+ "message": "received: (bytes) e3 82 84 e3 81 af e3 82 8a e4 bf ba e3 81 ae e9 9d 92 e6 98 a5 e3 83 a9 e3 83 96 e3 82 b3 e3 83 a1 e3 81 af e3 81 be e3 81 a1 e3 81 8c e3 81 a3 e3 81 a6 e3 81 84 e3 82 8b e3 80 82, (string) やはり俺の青春ラブコメはまちがっている。"
+}
+
diff --git a/e2e/testdata/fixtures/teste2e_repl-call_--help.golden b/e2e/testdata/fixtures/teste2e_repl-call_--help.golden
index e9b83e1b..1a403911 100644
--- a/e2e/testdata/fixtures/teste2e_repl-call_--help.golden
+++ b/e2e/testdata/fixtures/teste2e_repl-call_--help.golden
@@ -1,11 +1,11 @@
usage: call
Options:
- --add-repeated-manually prompt asks whether to add a value if it encountered to a repeated field
- --bytes-as-base64 interpret TYPE_BYTES input as base64-encoded string (mutually exclusive with --bytes-from-file)
- --bytes-from-file interpret TYPE_BYTES input as a relative path to a file (mutually exclusive with --bytes-as-base64)
- --dig-manually prompt asks whether to dig down if it encountered to a message field
- --emit-defaults render fields with default values
- --enrich enrich response output includes header, message, trailer and status
- -r, --repeat repeat previous unary or server streaming request (if exists)
+ --add-repeated-manually prompt asks whether to add a value if it encountered to a repeated field
+ --bytes-as-quoted-literals interpret TYPE_BYTES input as a string of (quoted) byte literal or Unicode, instead of a base64-encoded string (mutually exclusive with --bytes-from-file)
+ --bytes-from-file interpret TYPE_BYTES input as a relative path to a file (mutually exclusive with --bytes-as-quoted-literals)
+ --dig-manually prompt asks whether to dig down if it encountered to a message field
+ --emit-defaults render fields with default values
+ --enrich enrich response output includes header, message, trailer and status
+ -r, --repeat repeat previous unary or server streaming request (if exists)
diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unarybytes_with_quoted_literals.golden b/e2e/testdata/fixtures/teste2e_repl-call_unarybytes_with_quoted_literals.golden
new file mode 100644
index 00000000..62e67d9e
--- /dev/null
+++ b/e2e/testdata/fixtures/teste2e_repl-call_unarybytes_with_quoted_literals.golden
@@ -0,0 +1,4 @@
+{
+ "message": "received: (bytes) e3 82 84 e3 81 af e3 82 8a e4 bf ba e3 81 ae e9 9d 92 e6 98 a5 e3 83 a9 e3 83 96 e3 82 b3 e3 83 a1 e3 81 af e3 81 be e3 81 a1 e3 81 8c e3 81 a3 e3 81 a6 e3 81 84 e3 82 8b e3 80 82, (string) やはり俺の青春ラブコメはまちがっている。"
+}
+
diff --git a/fill/filler.go b/fill/filler.go
index e0a96b91..9238cf28 100644
--- a/fill/filler.go
+++ b/fill/filler.go
@@ -22,8 +22,8 @@ type Filler interface {
type InteractiveFillerOpts struct {
// DigManually is true, Fill asks whether to dig down if it encountered to a message field.
DigManually,
- // BytesAsBase64 is true, Fill will interpret input as base64-encoded string
- BytesAsBase64,
+ // BytesAsQuotedLiterals is true, Fill will interpret input as a quoted byte or unicode literal string.
+ BytesAsQuotedLiterals,
// BytesFromFile is true, Fill will read the contents of the file from the provided relative path.
BytesFromFile,
// AddRepeatedManually is true, Fill asks whether to add a repeated field value
diff --git a/fill/proto/interactive_filler.go b/fill/proto/interactive_filler.go
index 126bf4b4..e8908571 100644
--- a/fill/proto/interactive_filler.go
+++ b/fill/proto/interactive_filler.go
@@ -202,20 +202,15 @@ func (r *resolver) resolveField(f *desc.FieldDescriptor) error {
// So, we need to call strconv.Unquote to interpret backslashes as an escape sequence.
case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
converter = func(v string) (interface{}, error) {
- if r.opts.BytesAsBase64 {
- b, err := base64.StdEncoding.DecodeString(v)
- if err == nil {
- return b, nil
- }
+ if r.opts.BytesAsQuotedLiterals {
+ v, err := strconv.Unquote(`"` + v + `"`)
+ return []byte(v), err
} else if r.opts.BytesFromFile {
b, err := os.ReadFile(v)
- if err == nil {
- return b, nil
- }
+ return b, err
}
-
- v, err := strconv.Unquote(`"` + v + `"`)
- return []byte(v), err
+ b, err := base64.StdEncoding.DecodeString(v)
+ return b, err
}
default:
diff --git a/fill/proto/interactive_filler_test.go b/fill/proto/interactive_filler_test.go
index c5072c77..f3367d82 100644
--- a/fill/proto/interactive_filler_test.go
+++ b/fill/proto/interactive_filler_test.go
@@ -76,8 +76,6 @@ func TestInteractiveFiller(t *testing.T) {
b.AddField(builder.NewField("o", builder.FieldTypeBool()))
b.AddField(builder.NewField("p", builder.FieldTypeString()))
b.AddField(builder.NewField("q", builder.FieldTypeBytes()))
- b.AddField(builder.NewField("r", builder.FieldTypeBytes()))
- b.AddField(builder.NewField("s", builder.FieldTypeBytes()))
m, err := b.Build()
if err != nil {
t.Fatalf("Build should not return an error, but got '%s'", err)
@@ -87,23 +85,21 @@ func TestInteractiveFiller(t *testing.T) {
p := &stubPrompt{
t: t,
input: []string{
- "1.1", // c
- "1.2", // d
- "1", // e
- "2", // f
- "3", // g
- "4", // h
- "5", // i
- "6", // j
- "7", // k
- "8", // l
- "9", // m
- "10", // n
- "true", // o
- "foo", // p
- "bar", // q
- "\x62\x61\x7a", // r
- "./proto.go", // s
+ "1.1", // c
+ "1.2", // d
+ "1", // e
+ "2", // f
+ "3", // g
+ "4", // h
+ "5", // i
+ "6", // j
+ "7", // k
+ "8", // l
+ "9", // m
+ "10", // n
+ "true", // o
+ "foo", // p
+ "./proto.go", // q
},
selection: []int{
0, // a - yes
@@ -121,7 +117,7 @@ func TestInteractiveFiller(t *testing.T) {
return
}
- const want = `{"a":[{}],"b":"enum2","c":1.1,"d":1.2,"e":"1","f":"2","g":"3","h":"4","i":"5","j":6,"k":7,"l":8,"m":9,"n":10,"o":true,"p":"foo","q":"YmFy","r":"YmF6","s":"Ly8gUGFja2FnZSBwcm90byBwcm92aWRlcyBhIGZpbGxlciBpbXBsZW1lbnRhdGlvbiBmb3IgUHJvdG9jb2wgQnVmZmVycy4KcGFja2FnZSBwcm90bwo="}`
+ const want = `{"a":[{}],"b":"enum2","c":1.1,"d":1.2,"e":"1","f":"2","g":"3","h":"4","i":"5","j":6,"k":7,"l":8,"m":9,"n":10,"o":true,"p":"foo","q":"Ly8gUGFja2FnZSBwcm90byBwcm92aWRlcyBhIGZpbGxlciBpbXBsZW1lbnRhdGlvbiBmb3IgUHJvdG9jb2wgQnVmZmVycy4KcGFja2FnZSBwcm90bwo="}`
marshaler := jsonpb.Marshaler{EmitDefaults: true}
got, err := marshaler.MarshalToString(msg)
diff --git a/repl/commands.go b/repl/commands.go
index a203ab96..630fa64b 100644
--- a/repl/commands.go
+++ b/repl/commands.go
@@ -155,7 +155,7 @@ func (c *showCommand) Run(w io.Writer, args []string) error {
}
type callCommand struct {
- enrich, digManually, bytesAsBase64, bytesFromFile, emitDefaults, repeatCall, addRepeatedManually bool
+ enrich, digManually, bytesAsQuotedLiterals, bytesFromFile, emitDefaults, repeatCall, addRepeatedManually bool
}
func (c *callCommand) FlagSet() (*pflag.FlagSet, bool) {
@@ -163,8 +163,8 @@ func (c *callCommand) FlagSet() (*pflag.FlagSet, bool) {
fs.Usage = func() {} // Disable help output when an error occurred.
fs.BoolVar(&c.enrich, "enrich", false, "enrich response output includes header, message, trailer and status")
fs.BoolVar(&c.digManually, "dig-manually", false, "prompt asks whether to dig down if it encountered to a message field")
- fs.BoolVar(&c.bytesAsBase64, "bytes-as-base64", false, "interpret TYPE_BYTES input as base64-encoded string (mutually exclusive with --bytes-from-file)")
- fs.BoolVar(&c.bytesFromFile, "bytes-from-file", false, "interpret TYPE_BYTES input as a relative path to a file (mutually exclusive with --bytes-as-base64)")
+ fs.BoolVar(&c.bytesAsQuotedLiterals, "bytes-as-quoted-literals", false, "interpret TYPE_BYTES input as a string of (quoted) byte literal or Unicode, instead of a base64-encoded string (mutually exclusive with --bytes-from-file)")
+ fs.BoolVar(&c.bytesFromFile, "bytes-from-file", false, "interpret TYPE_BYTES input as a relative path to a file (mutually exclusive with --bytes-as-quoted-literals)")
fs.BoolVar(&c.emitDefaults, "emit-defaults", false, "render fields with default values")
fs.BoolVarP(&c.repeatCall, "repeat", "r", false, "repeat previous unary or server streaming request (if exists)")
fs.BoolVar(&c.addRepeatedManually, "add-repeated-manually", false, "prompt asks whether to add a value if it encountered to a repeated field")
@@ -200,15 +200,15 @@ func (c *callCommand) Run(w io.Writer, args []string) error {
},
)
- // Ensure bytesAsBase64 and bytesFromFile are not both set
+ // Ensure bytesAsLiteral and bytesFromFile are not both set
// pflag doesn't suppport mutually exclusive flags (https://github.com/spf13/pflag/issues/270)
- if c.bytesAsBase64 && c.bytesFromFile {
- return errors.New("only one of --bytes-as-base64 or --bytes-from-file can be specified")
+ if c.bytesAsQuotedLiterals && c.bytesFromFile {
+ return errors.New("only one of --bytes-as-quoted-literals or --bytes-from-file can be specified")
}
// here we create the request context
// we also add the call command flags here
- err := usecase.CallRPCInteractively(context.Background(), w, args[0], c.digManually, c.bytesAsBase64, c.bytesFromFile, c.repeatCall, c.addRepeatedManually)
+ err := usecase.CallRPCInteractively(context.Background(), w, args[0], c.digManually, c.bytesAsQuotedLiterals, c.bytesFromFile, c.repeatCall, c.addRepeatedManually)
if errors.Is(err, io.EOF) {
return errors.New("inputting canceled")
}
diff --git a/usecase/call_rpc.go b/usecase/call_rpc.go
index 48e795e0..73b8f246 100644
--- a/usecase/call_rpc.go
+++ b/usecase/call_rpc.go
@@ -478,18 +478,18 @@ func (f *interactiveFiller) Fill(v interface{}) error {
return f.fillFunc(v)
}
-func CallRPCInteractively(ctx context.Context, w io.Writer, rpcName string, digManually, bytesAsBase64, bytesFromFile, rerunPrevious, addRepeatedManually bool) error {
- return dm.CallRPCInteractively(ctx, w, rpcName, digManually, bytesAsBase64, bytesFromFile, rerunPrevious, addRepeatedManually)
+func CallRPCInteractively(ctx context.Context, w io.Writer, rpcName string, digManually, bytesAsQuotedLiterals, bytesFromFile, rerunPrevious, addRepeatedManually bool) error {
+ return dm.CallRPCInteractively(ctx, w, rpcName, digManually, bytesAsQuotedLiterals, bytesFromFile, rerunPrevious, addRepeatedManually)
}
-func (m *dependencyManager) CallRPCInteractively(ctx context.Context, w io.Writer, rpcName string, digManually, bytesAsBase64, bytesFromFile, rerunPrevious, addRepeatedManually bool) error {
+func (m *dependencyManager) CallRPCInteractively(ctx context.Context, w io.Writer, rpcName string, digManually, bytesAsQuotedLiterals, bytesFromFile, rerunPrevious, addRepeatedManually bool) error {
return m.CallRPC(ctx, w, rpcName, rerunPrevious, &interactiveFiller{
fillFunc: func(v interface{}) error {
return m.interactiveFiller.Fill(v, fill.InteractiveFillerOpts{
- DigManually: digManually,
- BytesAsBase64: bytesAsBase64,
- BytesFromFile: bytesFromFile,
- AddRepeatedManually: addRepeatedManually,
+ DigManually: digManually,
+ BytesAsQuotedLiterals: bytesAsQuotedLiterals,
+ BytesFromFile: bytesFromFile,
+ AddRepeatedManually: addRepeatedManually,
})
},
})