Skip to content

Commit 9a97c10

Browse files
glerchundieparis
authored andcommitted
bytes: add support for base64 encoded flags (#177)
Signed-off-by: Gorka Lerchundi Osa <[email protected]>
1 parent 3ebe029 commit 9a97c10

File tree

2 files changed

+167
-1
lines changed

2 files changed

+167
-1
lines changed

bytes.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pflag
22

33
import (
4+
"encoding/base64"
45
"encoding/hex"
56
"fmt"
67
"strings"
@@ -9,10 +10,12 @@ import (
910
// BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded
1011
type bytesHexValue []byte
1112

13+
// String implements pflag.Value.String.
1214
func (bytesHex bytesHexValue) String() string {
1315
return fmt.Sprintf("%X", []byte(bytesHex))
1416
}
1517

18+
// Set implements pflag.Value.Set.
1619
func (bytesHex *bytesHexValue) Set(value string) error {
1720
bin, err := hex.DecodeString(strings.TrimSpace(value))
1821

@@ -25,6 +28,7 @@ func (bytesHex *bytesHexValue) Set(value string) error {
2528
return nil
2629
}
2730

31+
// Type implements pflag.Value.Type.
2832
func (*bytesHexValue) Type() string {
2933
return "bytesHex"
3034
}
@@ -103,3 +107,103 @@ func BytesHex(name string, value []byte, usage string) *[]byte {
103107
func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte {
104108
return CommandLine.BytesHexP(name, shorthand, value, usage)
105109
}
110+
111+
// BytesBase64 adapts []byte for use as a flag. Value of flag is Base64 encoded
112+
type bytesBase64Value []byte
113+
114+
// String implements pflag.Value.String.
115+
func (bytesBase64 bytesBase64Value) String() string {
116+
return base64.StdEncoding.EncodeToString([]byte(bytesBase64))
117+
}
118+
119+
// Set implements pflag.Value.Set.
120+
func (bytesBase64 *bytesBase64Value) Set(value string) error {
121+
bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))
122+
123+
if err != nil {
124+
return err
125+
}
126+
127+
*bytesBase64 = bin
128+
129+
return nil
130+
}
131+
132+
// Type implements pflag.Value.Type.
133+
func (*bytesBase64Value) Type() string {
134+
return "bytesBase64"
135+
}
136+
137+
func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value {
138+
*p = val
139+
return (*bytesBase64Value)(p)
140+
}
141+
142+
func bytesBase64ValueConv(sval string) (interface{}, error) {
143+
144+
bin, err := base64.StdEncoding.DecodeString(sval)
145+
if err == nil {
146+
return bin, nil
147+
}
148+
149+
return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err)
150+
}
151+
152+
// GetBytesBase64 return the []byte value of a flag with the given name
153+
func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) {
154+
val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv)
155+
156+
if err != nil {
157+
return []byte{}, err
158+
}
159+
160+
return val.([]byte), nil
161+
}
162+
163+
// BytesBase64Var defines an []byte flag with specified name, default value, and usage string.
164+
// The argument p points to an []byte variable in which to store the value of the flag.
165+
func (f *FlagSet) BytesBase64Var(p *[]byte, name string, value []byte, usage string) {
166+
f.VarP(newBytesBase64Value(value, p), name, "", usage)
167+
}
168+
169+
// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash.
170+
func (f *FlagSet) BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) {
171+
f.VarP(newBytesBase64Value(value, p), name, shorthand, usage)
172+
}
173+
174+
// BytesBase64Var defines an []byte flag with specified name, default value, and usage string.
175+
// The argument p points to an []byte variable in which to store the value of the flag.
176+
func BytesBase64Var(p *[]byte, name string, value []byte, usage string) {
177+
CommandLine.VarP(newBytesBase64Value(value, p), name, "", usage)
178+
}
179+
180+
// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash.
181+
func BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) {
182+
CommandLine.VarP(newBytesBase64Value(value, p), name, shorthand, usage)
183+
}
184+
185+
// BytesBase64 defines an []byte flag with specified name, default value, and usage string.
186+
// The return value is the address of an []byte variable that stores the value of the flag.
187+
func (f *FlagSet) BytesBase64(name string, value []byte, usage string) *[]byte {
188+
p := new([]byte)
189+
f.BytesBase64VarP(p, name, "", value, usage)
190+
return p
191+
}
192+
193+
// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash.
194+
func (f *FlagSet) BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte {
195+
p := new([]byte)
196+
f.BytesBase64VarP(p, name, shorthand, value, usage)
197+
return p
198+
}
199+
200+
// BytesBase64 defines an []byte flag with specified name, default value, and usage string.
201+
// The return value is the address of an []byte variable that stores the value of the flag.
202+
func BytesBase64(name string, value []byte, usage string) *[]byte {
203+
return CommandLine.BytesBase64P(name, "", value, usage)
204+
}
205+
206+
// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash.
207+
func BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte {
208+
return CommandLine.BytesBase64P(name, shorthand, value, usage)
209+
}

bytes_test.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pflag
22

33
import (
4+
"encoding/base64"
45
"fmt"
56
"os"
67
"testing"
@@ -61,7 +62,7 @@ func TestBytesHex(t *testing.T) {
6162
} else if tc.success {
6263
bytesHex, err := f.GetBytesHex("bytes")
6364
if err != nil {
64-
t.Errorf("Got error trying to fetch the IP flag: %v", err)
65+
t.Errorf("Got error trying to fetch the 'bytes' flag: %v", err)
6566
}
6667
if fmt.Sprintf("%X", bytesHex) != tc.expected {
6768
t.Errorf("expected %q, got '%X'", tc.expected, bytesHex)
@@ -70,3 +71,64 @@ func TestBytesHex(t *testing.T) {
7071
}
7172
}
7273
}
74+
75+
func setUpBytesBase64(bytesBase64 *[]byte) *FlagSet {
76+
f := NewFlagSet("test", ContinueOnError)
77+
f.BytesBase64Var(bytesBase64, "bytes", []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, "Some bytes in Base64")
78+
f.BytesBase64VarP(bytesBase64, "bytes2", "B", []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, "Some bytes in Base64")
79+
return f
80+
}
81+
82+
func TestBytesBase64(t *testing.T) {
83+
testCases := []struct {
84+
input string
85+
success bool
86+
expected string
87+
}{
88+
/// Positive cases
89+
{"", true, ""}, // Is empty string OK ?
90+
{"AQ==", true, "AQ=="},
91+
92+
// Negative cases
93+
{"AQ", false, ""}, // Padding removed
94+
{"ï", false, ""}, // non-base64 characters
95+
}
96+
97+
devnull, _ := os.Open(os.DevNull)
98+
os.Stderr = devnull
99+
100+
for i := range testCases {
101+
var bytesBase64 []byte
102+
f := setUpBytesBase64(&bytesBase64)
103+
104+
tc := &testCases[i]
105+
106+
// --bytes
107+
args := []string{
108+
fmt.Sprintf("--bytes=%s", tc.input),
109+
fmt.Sprintf("-B %s", tc.input),
110+
fmt.Sprintf("--bytes2=%s", tc.input),
111+
}
112+
113+
for _, arg := range args {
114+
err := f.Parse([]string{arg})
115+
116+
if err != nil && tc.success == true {
117+
t.Errorf("expected success, got %q", err)
118+
continue
119+
} else if err == nil && tc.success == false {
120+
// bytesBase64, err := f.GetBytesBase64("bytes")
121+
t.Errorf("expected failure while processing %q", tc.input)
122+
continue
123+
} else if tc.success {
124+
bytesBase64, err := f.GetBytesBase64("bytes")
125+
if err != nil {
126+
t.Errorf("Got error trying to fetch the 'bytes' flag: %v", err)
127+
}
128+
if base64.StdEncoding.EncodeToString(bytesBase64) != tc.expected {
129+
t.Errorf("expected %q, got '%X'", tc.expected, bytesBase64)
130+
}
131+
}
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)