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) {
|
func parseConfigPair(kvStr string, force bool) (key string, val any, err error) {
|
||||||
kv := strings.SplitN(kvStr, "=", 2)
|
k, v, found := strings.Cut(kvStr, "=")
|
||||||
if len(kv) != 2 {
|
if !found {
|
||||||
return "", nil, fmt.Errorf("invalid parameter format: must be 'key=val', got: %s", kvStr)
|
return "", nil, fmt.Errorf("invalid parameter format: must be 'key=val', got: %s", kvStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
key = kv[0]
|
key = k
|
||||||
valRaw := kv[1]
|
valRaw := v
|
||||||
|
|
||||||
switch key {
|
switch key {
|
||||||
case netmapAuditFeeKey, netmapBasicIncomeRateKey,
|
case netmapAuditFeeKey, netmapBasicIncomeRateKey,
|
||||||
|
@ -162,7 +162,7 @@ func parseConfigPair(kvStr string, force bool) (key string, val any, err error)
|
||||||
case netmapEigenTrustAlphaKey:
|
case netmapEigenTrustAlphaKey:
|
||||||
// just check that it could
|
// just check that it could
|
||||||
// be parsed correctly
|
// be parsed correctly
|
||||||
_, err = strconv.ParseFloat(kv[1], 64)
|
_, err = strconv.ParseFloat(v, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("could not parse %s's value '%s' as float: %w", key, valRaw, err)
|
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()
|
bw := io.NewBufBinWriter()
|
||||||
for i := range args {
|
for i := range args {
|
||||||
kv := strings.SplitN(args[i], "=", 2)
|
k, v, found := strings.Cut(args[i], "=")
|
||||||
if len(kv) != 2 {
|
if !found {
|
||||||
return fmt.Errorf("invalid parameter format, must be Parameter=Value")
|
return fmt.Errorf("invalid parameter format, must be Parameter=Value")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch kv[0] {
|
switch k {
|
||||||
case execFeeParam, storagePriceParam, setFeeParam:
|
case execFeeParam, storagePriceParam, setFeeParam:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("parameter must be one of %s, %s and %s", execFeeParam, storagePriceParam, setFeeParam)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("can't parse parameter value '%s': %w", args[1], err)
|
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 {
|
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 {
|
func parseAttributes(dst *container.Container, attributes []string) error {
|
||||||
for i := range attributes {
|
for i := range attributes {
|
||||||
kvPair := strings.Split(attributes[i], attributeDelimiter)
|
k, v, found := strings.Cut(attributes[i], attributeDelimiter)
|
||||||
if len(kvPair) != 2 {
|
if !found {
|
||||||
return errors.New("invalid container attribute")
|
return errors.New("invalid container attribute")
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.SetAttribute(kvPair[0], kvPair[1])
|
dst.SetAttribute(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !containerNoTimestamp {
|
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
|
attrs := make([]object.Attribute, len(rawAttrs), len(rawAttrs)+2) // name + timestamp attributes
|
||||||
for i := range rawAttrs {
|
for i := range rawAttrs {
|
||||||
kv := strings.SplitN(rawAttrs[i], "=", 2)
|
k, v, found := strings.Cut(rawAttrs[i], "=")
|
||||||
if len(kv) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid attribute format: %s", rawAttrs[i])
|
return nil, fmt.Errorf("invalid attribute format: %s", rawAttrs[i])
|
||||||
}
|
}
|
||||||
attrs[i].SetKey(kv[0])
|
attrs[i].SetKey(k)
|
||||||
attrs[i].SetValue(kv[1])
|
attrs[i].SetValue(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
disableFilename, _ := cmd.Flags().GetBool("disable-filename")
|
disableFilename, _ := cmd.Flags().GetBool("disable-filename")
|
||||||
|
@ -218,26 +218,26 @@ func parseObjectNotifications(cmd *cobra.Command) (*object.NotificationInfo, err
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSlice := strings.SplitN(raw, separator, 2)
|
before, after, found := strings.Cut(raw, separator)
|
||||||
if len(rawSlice) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("notification must be in the form of: *epoch*%s*topic*, got %s", separator, raw)
|
return nil, fmt.Errorf("notification must be in the form of: *epoch*%s*topic*, got %s", separator, raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
ni := new(object.NotificationInfo)
|
ni := new(object.NotificationInfo)
|
||||||
|
|
||||||
epoch, err := strconv.ParseUint(rawSlice[0], 10, 64)
|
epoch, err := strconv.ParseUint(before, 10, 64)
|
||||||
if err != nil {
|
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)
|
ni.SetEpoch(epoch)
|
||||||
|
|
||||||
if rawSlice[1] == "" {
|
if after == "" {
|
||||||
return nil, fmt.Errorf("incorrect empty topic: use %s to force using default topic", useDefaultTopic)
|
return nil, fmt.Errorf("incorrect empty topic: use %s to force using default topic", useDefaultTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rawSlice[1] != useDefaultTopic {
|
if after != useDefaultTopic {
|
||||||
ni.SetTopic(rawSlice[1])
|
ni.SetTopic(after)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ni, nil
|
return ni, nil
|
||||||
|
|
|
@ -154,16 +154,16 @@ func getRangeList(cmd *cobra.Command) ([]*object.Range, error) {
|
||||||
vs := strings.Split(v, ",")
|
vs := strings.Split(v, ",")
|
||||||
rs := make([]*object.Range, len(vs))
|
rs := make([]*object.Range, len(vs))
|
||||||
for i := range vs {
|
for i := range vs {
|
||||||
r := strings.Split(vs[i], rangeSep)
|
before, after, found := strings.Cut(vs[i], rangeSep)
|
||||||
if len(r) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid range specifier: %s", vs[i])
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid '%s' range offset specifier: %w", vs[i], err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid '%s' range length specifier: %w", vs[i], err)
|
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))
|
xs := make([]string, 0, 2*len(xHeaders))
|
||||||
|
|
||||||
for i := range xHeaders {
|
for i := range xHeaders {
|
||||||
kv := strings.SplitN(xHeaders[i], "=", 2)
|
k, v, found := strings.Cut(xHeaders[i], "=")
|
||||||
if len(kv) != 2 {
|
if !found {
|
||||||
panic(fmt.Errorf("invalid X-Header format: %s", xHeaders[i]))
|
panic(fmt.Errorf("invalid X-Header format: %s", xHeaders[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
xs = append(xs, kv[0], kv[1])
|
xs = append(xs, k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return xs
|
return xs
|
||||||
|
|
|
@ -79,14 +79,14 @@ func parseMeta(cmd *cobra.Command) ([]*tree.KeyValue, error) {
|
||||||
|
|
||||||
pairs := make([]*tree.KeyValue, 0, len(raws))
|
pairs := make([]*tree.KeyValue, 0, len(raws))
|
||||||
for i := range raws {
|
for i := range raws {
|
||||||
kv := strings.SplitN(raws[i], "=", 2)
|
k, v, found := strings.Cut(raws[i], "=")
|
||||||
if len(kv) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid meta pair format: %s", raws[i])
|
return nil, fmt.Errorf("invalid meta pair format: %s", raws[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
var pair tree.KeyValue
|
var pair tree.KeyValue
|
||||||
pair.Key = kv[0]
|
pair.Key = k
|
||||||
pair.Value = []byte(kv[1])
|
pair.Value = []byte(v)
|
||||||
|
|
||||||
pairs = append(pairs, &pair)
|
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) {
|
func parseEACLRecord(args []string) (*eacl.Record, error) {
|
||||||
r := new(eacl.Record)
|
r := new(eacl.Record)
|
||||||
for i := range args {
|
for _, arg := range args {
|
||||||
ss := strings.SplitN(args[i], ":", 2)
|
before, after, found := strings.Cut(arg, ":")
|
||||||
|
|
||||||
switch prefix := strings.ToLower(ss[0]); prefix {
|
switch prefix := strings.ToLower(before); prefix {
|
||||||
case "req", "obj": // filters
|
case "req", "obj": // filters
|
||||||
if len(ss) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid filter or target: %s", args[i])
|
return nil, fmt.Errorf("invalid filter or target: %s", arg)
|
||||||
}
|
|
||||||
|
|
||||||
i := strings.Index(ss[1], "=")
|
|
||||||
if i < 0 {
|
|
||||||
return nil, fmt.Errorf("invalid filter key-value pair: %s", ss[1])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var key, value string
|
var key, value string
|
||||||
var op eacl.Match
|
var op eacl.Match
|
||||||
|
var f bool
|
||||||
|
|
||||||
if 0 < i && ss[1][i-1] == '!' {
|
key, value, f = strings.Cut(after, "!=")
|
||||||
key = ss[1][:i-1]
|
if f {
|
||||||
op = eacl.MatchStringNotEqual
|
op = eacl.MatchStringNotEqual
|
||||||
} else {
|
} 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
|
op = eacl.MatchStringEqual
|
||||||
}
|
}
|
||||||
|
|
||||||
value = ss[1][i+1:]
|
|
||||||
|
|
||||||
typ := eacl.HeaderFromRequest
|
typ := eacl.HeaderFromRequest
|
||||||
if ss[0] == "obj" {
|
if before == "obj" {
|
||||||
typ = eacl.HeaderFromObject
|
typ = eacl.HeaderFromObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,8 +260,8 @@ func parseEACLRecord(args []string) (*eacl.Record, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var pubs []ecdsa.PublicKey
|
var pubs []ecdsa.PublicKey
|
||||||
if len(ss) == 2 {
|
if found {
|
||||||
pubs, err = parseKeyList(ss[1])
|
pubs, err = parseKeyList(after)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -281,7 +278,7 @@ func parseEACLRecord(args []string) (*eacl.Record, error) {
|
||||||
eacl.AddFormedTarget(r, role, pubs...)
|
eacl.AddFormedTarget(r, role, pubs...)
|
||||||
|
|
||||||
default:
|
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)
|
scanner := bufio.NewScanner(f)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
pair := strings.SplitN(scanner.Text(), "=", 2)
|
k, v, found := strings.Cut(scanner.Text(), "=")
|
||||||
if len(pair) != 2 {
|
if !found {
|
||||||
continue
|
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 {
|
if err != nil {
|
||||||
panic("can't set environment variable")
|
panic("can't set environment variable")
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,18 +73,18 @@ func stringifyAddress(addr oid.Address) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addressFromString(s string) (oid.Address, error) {
|
func addressFromString(s string) (oid.Address, error) {
|
||||||
i := strings.IndexByte(s, '.')
|
before, after, found := strings.Cut(s, ".")
|
||||||
if i == -1 {
|
if !found {
|
||||||
return oid.Address{}, errors.New("invalid address")
|
return oid.Address{}, errors.New("invalid address")
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj oid.ID
|
var obj oid.ID
|
||||||
if err := obj.DecodeString(s[:i]); err != nil {
|
if err := obj.DecodeString(before); err != nil {
|
||||||
return oid.Address{}, err
|
return oid.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var cnr cid.ID
|
var cnr cid.ID
|
||||||
if err := cnr.DecodeString(s[i+1:]); err != nil {
|
if err := cnr.DecodeString(after); err != nil {
|
||||||
return oid.Address{}, err
|
return oid.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,29 +19,29 @@ func ReadNodeAttributes(dst *netmap.NodeInfo, attrs []string) error {
|
||||||
for i := range attrs {
|
for i := range attrs {
|
||||||
line := replaceEscaping(attrs[i], false) // replaced escaped symbols with non-printable symbols
|
line := replaceEscaping(attrs[i], false) // replaced escaped symbols with non-printable symbols
|
||||||
|
|
||||||
words := strings.Split(line, keyValueSeparator)
|
k, v, found := strings.Cut(line, keyValueSeparator)
|
||||||
if len(words) != 2 {
|
if !found {
|
||||||
return errors.New("missing attribute key and/or value")
|
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 {
|
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
|
// replace non-printable symbols with escaped symbols without escape character
|
||||||
words[0] = replaceEscaping(words[0], true)
|
k = replaceEscaping(k, true)
|
||||||
words[1] = replaceEscaping(words[1], true)
|
v = replaceEscaping(v, true)
|
||||||
|
|
||||||
if words[0] == "" {
|
if k == "" {
|
||||||
return errors.New("empty key")
|
return errors.New("empty key")
|
||||||
} else if words[1] == "" {
|
} else if v == "" {
|
||||||
return errors.New("empty value")
|
return errors.New("empty value")
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.SetAttribute(words[0], words[1])
|
dst.SetAttribute(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue
I suppose attribute
key=value=value
is not valid, but after this it will pass check.