Enhance the map flag to support parsing input value contains entry delimiters
Enhance the map flag to support parsing input value contains entry delimiters Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>pull/4920/head
parent
15851ac9aa
commit
44199db79d
|
@ -0,0 +1 @@
|
|||
Enhance the map flag to support parsing input value contains entry delimiters
|
|
@ -98,7 +98,7 @@ func NewCreateOptions() *CreateOptions {
|
|||
return &CreateOptions{
|
||||
Labels: flag.NewMap(),
|
||||
IncludeNamespaces: flag.NewStringArray("*"),
|
||||
NamespaceMappings: flag.NewMap().WithEntryDelimiter(",").WithKeyValueDelimiter(":"),
|
||||
NamespaceMappings: flag.NewMap().WithEntryDelimiter(',').WithKeyValueDelimiter(':'),
|
||||
RestoreVolumes: flag.NewOptionalBool(nil),
|
||||
PreserveNodePorts: flag.NewOptionalBool(nil),
|
||||
IncludeClusterResources: flag.NewOptionalBool(nil),
|
||||
|
|
|
@ -132,7 +132,7 @@ type controllerRunInfo struct {
|
|||
|
||||
func NewCommand(f client.Factory) *cobra.Command {
|
||||
var (
|
||||
volumeSnapshotLocations = flag.NewMap().WithKeyValueDelimiter(":")
|
||||
volumeSnapshotLocations = flag.NewMap().WithKeyValueDelimiter(':')
|
||||
logLevelFlag = logging.LogLevelFlag(logrus.InfoLevel)
|
||||
config = serverConfig{
|
||||
pluginDir: "/plugins",
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package flag
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -27,25 +28,25 @@ import (
|
|||
// map data (i.e. a collection of key-value pairs).
|
||||
type Map struct {
|
||||
data map[string]string
|
||||
entryDelimiter string
|
||||
keyValueDelimiter string
|
||||
entryDelimiter rune
|
||||
keyValueDelimiter rune
|
||||
}
|
||||
|
||||
// NewMap returns a Map using the default delimiters ("=" between keys and
|
||||
// values, and "," between map entries, e.g. k1=v1,k2=v2)
|
||||
// NewMap returns a Map using the default delimiters ('=' between keys and
|
||||
// values, and ',' between map entries, e.g. k1=v1,k2=v2)
|
||||
func NewMap() Map {
|
||||
m := Map{
|
||||
data: make(map[string]string),
|
||||
}
|
||||
|
||||
return m.WithEntryDelimiter(",").WithKeyValueDelimiter("=")
|
||||
return m.WithEntryDelimiter(',').WithKeyValueDelimiter('=')
|
||||
}
|
||||
|
||||
// WithEntryDelimiter sets the delimiter to be used between map
|
||||
// entries.
|
||||
//
|
||||
// For example, in "k1=v1&k2=v2", the entry delimiter is "&"
|
||||
func (m Map) WithEntryDelimiter(delimiter string) Map {
|
||||
// For example, in "k1=v1&k2=v2", the entry delimiter is '&'
|
||||
func (m Map) WithEntryDelimiter(delimiter rune) Map {
|
||||
m.entryDelimiter = delimiter
|
||||
return m
|
||||
}
|
||||
|
@ -53,8 +54,8 @@ func (m Map) WithEntryDelimiter(delimiter string) Map {
|
|||
// WithKeyValueDelimiter sets the delimiter to be used between
|
||||
// keys and values.
|
||||
//
|
||||
// For example, in "k1=v1&k2=v2", the key-value delimiter is "="
|
||||
func (m Map) WithKeyValueDelimiter(delimiter string) Map {
|
||||
// For example, in "k1=v1&k2=v2", the key-value delimiter is '='
|
||||
func (m Map) WithKeyValueDelimiter(delimiter rune) Map {
|
||||
m.keyValueDelimiter = delimiter
|
||||
return m
|
||||
}
|
||||
|
@ -63,17 +64,26 @@ func (m Map) WithKeyValueDelimiter(delimiter string) Map {
|
|||
func (m *Map) String() string {
|
||||
var a []string
|
||||
for k, v := range m.data {
|
||||
a = append(a, fmt.Sprintf("%s%s%s", k, m.keyValueDelimiter, v))
|
||||
a = append(a, fmt.Sprintf("%s%s%s", k, string(m.keyValueDelimiter), v))
|
||||
}
|
||||
return strings.Join(a, m.entryDelimiter)
|
||||
return strings.Join(a, string(m.entryDelimiter))
|
||||
}
|
||||
|
||||
// Set parses the provided string according to the delimiters and
|
||||
// assigns the result to the Map receiver. It returns an error if
|
||||
// the string is not parseable.
|
||||
func (m *Map) Set(s string) error {
|
||||
for _, part := range strings.Split(s, m.entryDelimiter) {
|
||||
kvs := strings.SplitN(part, m.keyValueDelimiter, 2)
|
||||
// use csv.Reader to support parsing input string contains entry delimiters.
|
||||
// e.g. `"k1=a=b,c=d",k2=v2` will be parsed into two parts: `k1=a=b,c=d` and `k2=v2`
|
||||
r := csv.NewReader(strings.NewReader(s))
|
||||
r.Comma = m.entryDelimiter
|
||||
parts, err := r.Read()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing %q", s)
|
||||
}
|
||||
|
||||
for _, part := range parts {
|
||||
kvs := strings.SplitN(part, string(m.keyValueDelimiter), 2)
|
||||
if len(kvs) != 2 {
|
||||
return errors.Errorf("error parsing %q", part)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright The Velero Contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flag
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetOfMap(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
error bool
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "invalid_input_missing_quote",
|
||||
input: `"k=v`,
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "invalid_input_contains_no_key_value_delimiter",
|
||||
input: `k`,
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
name: "valid input",
|
||||
input: `k1=v1,k2=v2`,
|
||||
error: false,
|
||||
expected: map[string]string{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid input whose value contains entry delimiter",
|
||||
input: `k1=v1,"k2=a=b,c=d"`,
|
||||
error: false,
|
||||
expected: map[string]string{
|
||||
"k1": "v1",
|
||||
"k2": "a=b,c=d",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
m := NewMap()
|
||||
err := m.Set(c.input)
|
||||
if c.error {
|
||||
require.NotNil(t, err)
|
||||
return
|
||||
}
|
||||
assert.EqualValues(t, c.expected, m.data)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue