backend, options: Prefer strings.Cut to SplitN

Also realigned the various "split into host🪣prefix"
implementations.
This commit is contained in:
greatroar 2022-11-27 18:09:59 +01:00 committed by Michael Eischer
parent 60aa87bbab
commit 65612d797c
9 changed files with 56 additions and 66 deletions

View File

@ -43,14 +43,13 @@ func ParseConfig(s string) (interface{}, error) {
// use the first entry of the path as the bucket name and the // use the first entry of the path as the bucket name and the
// remainder as prefix // remainder as prefix
data := strings.SplitN(s, ":", 2) container, prefix, colon := strings.Cut(s, ":")
if len(data) < 2 { if !colon {
return nil, errors.New("azure: invalid format: bucket name or path not found") return nil, errors.New("azure: invalid format: bucket name or path not found")
} }
container, path := data[0], path.Clean(data[1]) prefix = strings.TrimPrefix(path.Clean(prefix), "/")
path = strings.TrimPrefix(path, "/")
cfg := NewConfig() cfg := NewConfig()
cfg.Container = container cfg.Container = container
cfg.Prefix = path cfg.Prefix = prefix
return cfg, nil return cfg, nil
} }

View File

@ -37,7 +37,7 @@ var bucketName = regexp.MustCompile("^[a-zA-Z0-9-]+$")
// https://help.backblaze.com/hc/en-us/articles/217666908-What-you-need-to-know-about-B2-Bucket-names // https://help.backblaze.com/hc/en-us/articles/217666908-What-you-need-to-know-about-B2-Bucket-names
func checkBucketName(name string) error { func checkBucketName(name string) error {
if name == "" { if name == "" {
return errors.New("bucket name is empty") return errors.New("bucket name not found")
} }
if len(name) < 6 { if len(name) < 6 {
@ -64,30 +64,18 @@ func ParseConfig(s string) (interface{}, error) {
} }
s = s[3:] s = s[3:]
data := strings.SplitN(s, ":", 2) bucket, prefix, _ := strings.Cut(s, ":")
if len(data) == 0 || len(data[0]) == 0 { if err := checkBucketName(bucket); err != nil {
return nil, errors.New("bucket name not found")
}
cfg := NewConfig()
cfg.Bucket = data[0]
if err := checkBucketName(cfg.Bucket); err != nil {
return nil, err return nil, err
} }
if len(data) == 2 { if len(prefix) > 0 {
p := data[1] prefix = strings.TrimPrefix(path.Clean(prefix), "/")
if len(p) > 0 {
p = path.Clean(p)
}
if len(p) > 0 && path.IsAbs(p) {
p = p[1:]
}
cfg.Prefix = p
} }
cfg := NewConfig()
cfg.Bucket = bucket
cfg.Prefix = prefix
return cfg, nil return cfg, nil
} }

View File

@ -42,17 +42,15 @@ func ParseConfig(s string) (interface{}, error) {
// use the first entry of the path as the bucket name and the // use the first entry of the path as the bucket name and the
// remainder as prefix // remainder as prefix
data := strings.SplitN(s, ":", 2) bucket, prefix, colon := strings.Cut(s, ":")
if len(data) < 2 { if !colon {
return nil, errors.New("gs: invalid format: bucket name or path not found") return nil, errors.New("gs: invalid format: bucket name or path not found")
} }
bucket, path := data[0], path.Clean(data[1]) prefix = strings.TrimPrefix(path.Clean(prefix), "/")
path = strings.TrimPrefix(path, "/")
cfg := NewConfig() cfg := NewConfig()
cfg.Bucket = bucket cfg.Bucket = bucket
cfg.Prefix = path cfg.Prefix = prefix
return cfg, nil return cfg, nil
} }

View File

@ -127,6 +127,6 @@ func StripPassword(s string) string {
} }
func extractScheme(s string) string { func extractScheme(s string) string {
data := strings.SplitN(s, ":", 2) scheme, _, _ := strings.Cut(s, ":")
return data[0] return scheme
} }

View File

@ -59,8 +59,8 @@ func ParseConfig(s string) (interface{}, error) {
return nil, errors.New("s3: bucket name not found") return nil, errors.New("s3: bucket name not found")
} }
path := strings.SplitN(url.Path[1:], "/", 2) bucket, path, _ := strings.Cut(url.Path[1:], "/")
return createConfig(url.Host, path, url.Scheme == "http") return createConfig(url.Host, bucket, path, url.Scheme == "http")
case strings.HasPrefix(s, "s3://"): case strings.HasPrefix(s, "s3://"):
s = s[5:] s = s[5:]
case strings.HasPrefix(s, "s3:"): case strings.HasPrefix(s, "s3:"):
@ -70,24 +70,24 @@ func ParseConfig(s string) (interface{}, error) {
} }
// use the first entry of the path as the endpoint and the // use the first entry of the path as the endpoint and the
// remainder as bucket name and prefix // remainder as bucket name and prefix
path := strings.SplitN(s, "/", 3) endpoint, rest, _ := strings.Cut(s, "/")
return createConfig(path[0], path[1:], false) bucket, prefix, _ := strings.Cut(rest, "/")
return createConfig(endpoint, bucket, prefix, false)
} }
func createConfig(endpoint string, p []string, useHTTP bool) (interface{}, error) { func createConfig(endpoint, bucket, prefix string, useHTTP bool) (interface{}, error) {
if len(p) < 1 { if endpoint == "" {
return nil, errors.New("s3: invalid format, host/region or bucket name not found") return nil, errors.New("s3: invalid format, host/region or bucket name not found")
} }
var prefix string if prefix != "" {
if len(p) > 1 && p[1] != "" { prefix = path.Clean(prefix)
prefix = path.Clean(p[1])
} }
cfg := NewConfig() cfg := NewConfig()
cfg.Endpoint = endpoint cfg.Endpoint = endpoint
cfg.UseHTTP = useHTTP cfg.UseHTTP = useHTTP
cfg.Bucket = p[0] cfg.Bucket = bucket
cfg.Prefix = prefix cfg.Prefix = prefix
return cfg, nil return cfg, nil
} }

View File

@ -1,6 +1,9 @@
package s3 package s3
import "testing" import (
"strings"
"testing"
)
var configTests = []struct { var configTests = []struct {
s string s string
@ -111,3 +114,14 @@ func TestParseConfig(t *testing.T) {
} }
} }
} }
func TestParseError(t *testing.T) {
const prefix = "s3: invalid format,"
for _, s := range []string{"", "/", "//", "/bucket/prefix"} {
_, err := ParseConfig("s3://" + s)
if err == nil || !strings.HasPrefix(err.Error(), prefix) {
t.Errorf("expected %q, got %q", prefix, err)
}
}
}

View File

@ -60,14 +60,13 @@ func ParseConfig(s string) (interface{}, error) {
// "user@host:path" in s // "user@host:path" in s
s = s[5:] s = s[5:]
// split user@host and path at the colon // split user@host and path at the colon
data := strings.SplitN(s, ":", 2) var colon bool
if len(data) < 2 { host, dir, colon = strings.Cut(s, ":")
if !colon {
return nil, errors.New("sftp: invalid format, hostname or path not found") return nil, errors.New("sftp: invalid format, hostname or path not found")
} }
host = data[0]
dir = data[1]
// split user and host at the "@" // split user and host at the "@"
data = strings.SplitN(host, "@", 3) data := strings.SplitN(host, "@", 3)
if len(data) == 3 { if len(data) == 3 {
user = data[0] + "@" + data[1] user = data[0] + "@" + data[1]
host = data[2] host = data[2]

View File

@ -51,17 +51,13 @@ func NewConfig() Config {
// ParseConfig parses the string s and extract swift's container name and prefix. // ParseConfig parses the string s and extract swift's container name and prefix.
func ParseConfig(s string) (interface{}, error) { func ParseConfig(s string) (interface{}, error) {
data := strings.SplitN(s, ":", 3) if !strings.HasPrefix(s, "swift:") {
if len(data) != 3 {
return nil, errors.New("invalid URL, expected: swift:container-name:/[prefix]") return nil, errors.New("invalid URL, expected: swift:container-name:/[prefix]")
} }
s = strings.TrimPrefix(s, "swift:")
scheme, container, prefix := data[0], data[1], data[2] container, prefix, _ := strings.Cut(s, ":")
if scheme != "swift" { if prefix == "" {
return nil, errors.Errorf("unexpected prefix: %s", data[0])
}
if len(prefix) == 0 {
return nil, errors.Errorf("prefix is empty") return nil, errors.Errorf("prefix is empty")
} }

View File

@ -92,14 +92,10 @@ func (h helpList) Swap(i, j int) {
// splitKeyValue splits at the first equals (=) sign. // splitKeyValue splits at the first equals (=) sign.
func splitKeyValue(s string) (key string, value string) { func splitKeyValue(s string) (key string, value string) {
data := strings.SplitN(s, "=", 2) key, value, _ = strings.Cut(s, "=")
key = strings.ToLower(strings.TrimSpace(data[0])) key = strings.ToLower(strings.TrimSpace(key))
if len(data) == 1 { value = strings.TrimSpace(value)
// no equals sign is treated as the empty value return key, value
return key, ""
}
return key, strings.TrimSpace(data[1])
} }
// Parse takes a slice of key=value pairs and returns an Options type. // Parse takes a slice of key=value pairs and returns an Options type.