Use strings.Cut
instead of strings.Split*
where possible #76
11 changed files with 69 additions and 72 deletions
|
@ -141,13 +141,13 @@ func setConfigCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
func parseConfigPair(kvStr string, force bool) (key string, val any, err error) {
|
||||
kv := strings.SplitN(kvStr, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, v, found := strings.Cut(kvStr, "=")
|
||||
if !found {
|
||||
return "", nil, fmt.Errorf("invalid parameter format: must be 'key=val', got: %s", kvStr)
|
||||
}
|
||||
|
||||
key = kv[0]
|
||||
valRaw := kv[1]
|
||||
key = k
|
||||
valRaw := v
|
||||
|
||||
switch key {
|
||||
case netmapAuditFeeKey, netmapBasicIncomeRateKey,
|
||||
|
@ -162,7 +162,7 @@ func parseConfigPair(kvStr string, force bool) (key string, val any, err error)
|
|||
case netmapEigenTrustAlphaKey:
|
||||
// just check that it could
|
||||
// be parsed correctly
|
||||
_, err = strconv.ParseFloat(kv[1], 64)
|
||||
_, err = strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not parse %s's value '%s' as float: %w", key, valRaw, err)
|
||||
}
|
||||
|
|
|
@ -27,23 +27,23 @@ func setPolicyCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
bw := io.NewBufBinWriter()
|
||||
for i := range args {
|
||||
kv := strings.SplitN(args[i], "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, v, found := strings.Cut(args[i], "=")
|
||||
if !found {
|
||||
return fmt.Errorf("invalid parameter format, must be Parameter=Value")
|
||||
}
|
||||
|
||||
switch kv[0] {
|
||||
switch k {
|
||||
case execFeeParam, storagePriceParam, setFeeParam:
|
||||
default:
|
||||
return fmt.Errorf("parameter must be one of %s, %s and %s", execFeeParam, storagePriceParam, setFeeParam)
|
||||
}
|
||||
|
||||
value, err := strconv.ParseUint(kv[1], 10, 32)
|
||||
value, err := strconv.ParseUint(v, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't parse parameter value '%s': %w", args[1], err)
|
||||
}
|
||||
|
||||
emit.AppCall(bw.BinWriter, policy.Hash, "set"+kv[0], callflag.All, int64(value))
|
||||
emit.AppCall(bw.BinWriter, policy.Hash, "set"+k, callflag.All, int64(value))
|
||||
}
|
||||
|
||||
if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil {
|
||||
|
|
|
@ -197,12 +197,12 @@ func parseContainerPolicy(cmd *cobra.Command, policyString string) (*netmap.Plac
|
|||
|
||||
func parseAttributes(dst *container.Container, attributes []string) error {
|
||||
for i := range attributes {
|
||||
kvPair := strings.Split(attributes[i], attributeDelimiter)
|
||||
if len(kvPair) != 2 {
|
||||
k, v, found := strings.Cut(attributes[i], attributeDelimiter)
|
||||
if !found {
|
||||
return errors.New("invalid container attribute")
|
||||
}
|
||||
|
||||
dst.SetAttribute(kvPair[0], kvPair[1])
|
||||
dst.SetAttribute(k, v)
|
||||
}
|
||||
|
||||
if !containerNoTimestamp {
|
||||
Agree, but there are no restriction in the sdk for the content of the key or value. it should be non empty only. The idea of the cli is to be as simple as it can, @fyrchik correct me if I'm wrong. Agree, but there are no restriction in the sdk for the content of the key or value. it should be non empty only. The idea of the cli is to be as simple as it can, @fyrchik correct me if I'm wrong.
Most likely, it was a bug. API gives no restrictions on container value https://github.com/TrueCloudLab/frostfs-api/blob/master/container/types.proto#L30 Most likely, it was a bug. API gives no restrictions on container value https://github.com/TrueCloudLab/frostfs-api/blob/master/container/types.proto#L30
|
||||
|
|
|
@ -179,12 +179,12 @@ func parseObjectAttrs(cmd *cobra.Command) ([]object.Attribute, error) {
|
|||
|
||||
attrs := make([]object.Attribute, len(rawAttrs), len(rawAttrs)+2) // name + timestamp attributes
|
||||
for i := range rawAttrs {
|
||||
kv := strings.SplitN(rawAttrs[i], "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, v, found := strings.Cut(rawAttrs[i], "=")
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid attribute format: %s", rawAttrs[i])
|
||||
}
|
||||
attrs[i].SetKey(kv[0])
|
||||
attrs[i].SetValue(kv[1])
|
||||
attrs[i].SetKey(k)
|
||||
attrs[i].SetValue(v)
|
||||
}
|
||||
|
||||
disableFilename, _ := cmd.Flags().GetBool("disable-filename")
|
||||
|
@ -218,26 +218,26 @@ func parseObjectNotifications(cmd *cobra.Command) (*object.NotificationInfo, err
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
rawSlice := strings.SplitN(raw, separator, 2)
|
||||
if len(rawSlice) != 2 {
|
||||
before, after, found := strings.Cut(raw, separator)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("notification must be in the form of: *epoch*%s*topic*, got %s", separator, raw)
|
||||
}
|
||||
|
||||
ni := new(object.NotificationInfo)
|
||||
|
||||
epoch, err := strconv.ParseUint(rawSlice[0], 10, 64)
|
||||
epoch, err := strconv.ParseUint(before, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse notification epoch %s: %w", rawSlice[0], err)
|
||||
return nil, fmt.Errorf("could not parse notification epoch %s: %w", before, err)
|
||||
}
|
||||
|
||||
ni.SetEpoch(epoch)
|
||||
|
||||
if rawSlice[1] == "" {
|
||||
if after == "" {
|
||||
return nil, fmt.Errorf("incorrect empty topic: use %s to force using default topic", useDefaultTopic)
|
||||
}
|
||||
|
||||
if rawSlice[1] != useDefaultTopic {
|
||||
ni.SetTopic(rawSlice[1])
|
||||
if after != useDefaultTopic {
|
||||
ni.SetTopic(after)
|
||||
}
|
||||
|
||||
return ni, nil
|
||||
|
|
|
@ -154,16 +154,16 @@ func getRangeList(cmd *cobra.Command) ([]*object.Range, error) {
|
|||
vs := strings.Split(v, ",")
|
||||
rs := make([]*object.Range, len(vs))
|
||||
for i := range vs {
|
||||
r := strings.Split(vs[i], rangeSep)
|
||||
if len(r) != 2 {
|
||||
before, after, found := strings.Cut(vs[i], rangeSep)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid range specifier: %s", vs[i])
|
||||
}
|
||||
|
||||
offset, err := strconv.ParseUint(r[0], 10, 64)
|
||||
offset, err := strconv.ParseUint(before, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid '%s' range offset specifier: %w", vs[i], err)
|
||||
}
|
||||
length, err := strconv.ParseUint(r[1], 10, 64)
|
||||
length, err := strconv.ParseUint(after, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid '%s' range length specifier: %w", vs[i], err)
|
||||
}
|
||||
|
|
|
@ -63,12 +63,12 @@ func parseXHeaders(cmd *cobra.Command) []string {
|
|||
xs := make([]string, 0, 2*len(xHeaders))
|
||||
|
||||
for i := range xHeaders {
|
||||
kv := strings.SplitN(xHeaders[i], "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, v, found := strings.Cut(xHeaders[i], "=")
|
||||
if !found {
|
||||
panic(fmt.Errorf("invalid X-Header format: %s", xHeaders[i]))
|
||||
}
|
||||
|
||||
xs = append(xs, kv[0], kv[1])
|
||||
xs = append(xs, k, v)
|
||||
}
|
||||
|
||||
return xs
|
||||
|
|
|
@ -79,14 +79,14 @@ func parseMeta(cmd *cobra.Command) ([]*tree.KeyValue, error) {
|
|||
|
||||
pairs := make([]*tree.KeyValue, 0, len(raws))
|
||||
for i := range raws {
|
||||
kv := strings.SplitN(raws[i], "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, v, found := strings.Cut(raws[i], "=")
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid meta pair format: %s", raws[i])
|
||||
}
|
||||
|
||||
var pair tree.KeyValue
|
||||
pair.Key = kv[0]
|
||||
pair.Value = []byte(kv[1])
|
||||
pair.Key = k
|
||||
pair.Value = []byte(v)
|
||||
|
||||
pairs = append(pairs, &pair)
|
||||
}
|
||||
|
|
|
@ -226,35 +226,32 @@ func parseEACLTable(tb *eacl.Table, args []string) error {
|
|||
|
||||
Can use Can use `Cut` here too.
Done. Done.
|
||||
func parseEACLRecord(args []string) (*eacl.Record, error) {
|
||||
r := new(eacl.Record)
|
||||
for i := range args {
|
||||
ss := strings.SplitN(args[i], ":", 2)
|
||||
for _, arg := range args {
|
||||
before, after, found := strings.Cut(arg, ":")
|
||||
|
||||
switch prefix := strings.ToLower(ss[0]); prefix {
|
||||
switch prefix := strings.ToLower(before); prefix {
|
||||
case "req", "obj": // filters
|
||||
if len(ss) != 2 {
|
||||
return nil, fmt.Errorf("invalid filter or target: %s", args[i])
|
||||
}
|
||||
|
||||
i := strings.Index(ss[1], "=")
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("invalid filter key-value pair: %s", ss[1])
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid filter or target: %s", arg)
|
||||
}
|
||||
|
||||
var key, value string
|
||||
var op eacl.Match
|
||||
var f bool
|
||||
|
||||
if 0 < i && ss[1][i-1] == '!' {
|
||||
key = ss[1][:i-1]
|
||||
key, value, f = strings.Cut(after, "!=")
|
||||
if f {
|
||||
op = eacl.MatchStringNotEqual
|
||||
} else {
|
||||
key = ss[1][:i]
|
||||
key, value, f = strings.Cut(after, "=")
|
||||
if !f {
|
||||
return nil, fmt.Errorf("invalid filter key-value pair: %s", after)
|
||||
}
|
||||
op = eacl.MatchStringEqual
|
||||
}
|
||||
|
||||
value = ss[1][i+1:]
|
||||
|
||||
typ := eacl.HeaderFromRequest
|
||||
if ss[0] == "obj" {
|
||||
if before == "obj" {
|
||||
typ = eacl.HeaderFromObject
|
||||
}
|
||||
|
||||
|
@ -263,8 +260,8 @@ func parseEACLRecord(args []string) (*eacl.Record, error) {
|
|||
var err error
|
||||
|
||||
var pubs []ecdsa.PublicKey
|
||||
if len(ss) == 2 {
|
||||
pubs, err = parseKeyList(ss[1])
|
||||
if found {
|
||||
pubs, err = parseKeyList(after)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -281,7 +278,7 @@ func parseEACLRecord(args []string) (*eacl.Record, error) {
|
|||
eacl.AddFormedTarget(r, role, pubs...)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid prefix: %s", ss[0])
|
||||
return nil, fmt.Errorf("invalid prefix: %s", before)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,14 +65,14 @@ func loadEnv(path string) {
|
|||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
pair := strings.SplitN(scanner.Text(), "=", 2)
|
||||
if len(pair) != 2 {
|
||||
k, v, found := strings.Cut(scanner.Text(), "=")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
pair[1] = strings.Trim(pair[1], `"`)
|
||||
v = strings.Trim(v, `"`)
|
||||
|
||||
err = os.Setenv(pair[0], pair[1])
|
||||
err = os.Setenv(k, v)
|
||||
if err != nil {
|
||||
panic("can't set environment variable")
|
||||
}
|
||||
|
|
|
@ -73,18 +73,18 @@ func stringifyAddress(addr oid.Address) string {
|
|||
}
|
||||
|
||||
func addressFromString(s string) (oid.Address, error) {
|
||||
i := strings.IndexByte(s, '.')
|
||||
if i == -1 {
|
||||
before, after, found := strings.Cut(s, ".")
|
||||
if !found {
|
||||
return oid.Address{}, errors.New("invalid address")
|
||||
}
|
||||
|
||||
var obj oid.ID
|
||||
if err := obj.DecodeString(s[:i]); err != nil {
|
||||
if err := obj.DecodeString(before); err != nil {
|
||||
return oid.Address{}, err
|
||||
}
|
||||
|
||||
var cnr cid.ID
|
||||
if err := cnr.DecodeString(s[i+1:]); err != nil {
|
||||
if err := cnr.DecodeString(after); err != nil {
|
||||
return oid.Address{}, err
|
||||
}
|
||||
|
||||
|
|
|
@ -19,29 +19,29 @@ func ReadNodeAttributes(dst *netmap.NodeInfo, attrs []string) error {
|
|||
for i := range attrs {
|
||||
line := replaceEscaping(attrs[i], false) // replaced escaped symbols with non-printable symbols
|
||||
|
||||
words := strings.Split(line, keyValueSeparator)
|
||||
if len(words) != 2 {
|
||||
k, v, found := strings.Cut(line, keyValueSeparator)
|
||||
if !found {
|
||||
return errors.New("missing attribute key and/or value")
|
||||
}
|
||||
Maybe Maybe `if !found || len(k) == 0 || len(v) == 0` will be better?
We already have this check in sdk. We already have this check in sdk.
|
||||
|
||||
_, ok := cache[words[0]]
|
||||
_, ok := cache[k]
|
||||
if ok {
|
||||
return fmt.Errorf("duplicated keys %s", words[0])
|
||||
return fmt.Errorf("duplicated keys %s", k)
|
||||
}
|
||||
|
||||
cache[words[0]] = struct{}{}
|
||||
cache[k] = struct{}{}
|
||||
|
||||
// replace non-printable symbols with escaped symbols without escape character
|
||||
words[0] = replaceEscaping(words[0], true)
|
||||
words[1] = replaceEscaping(words[1], true)
|
||||
k = replaceEscaping(k, true)
|
||||
v = replaceEscaping(v, true)
|
||||
|
||||
if words[0] == "" {
|
||||
if k == "" {
|
||||
return errors.New("empty key")
|
||||
} else if words[1] == "" {
|
||||
} else if v == "" {
|
||||
return errors.New("empty value")
|
||||
}
|
||||
|
||||
dst.SetAttribute(words[0], words[1])
|
||||
dst.SetAttribute(k, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue
I suppose attribute
key=value=value
is not valid, but after this it will pass check.