github.com/mckael/restic@v0.8.3/internal/backend/location/location.go (about) 1 // Package location implements parsing the restic repository location from a string. 2 package location 3 4 import ( 5 "strings" 6 7 "github.com/restic/restic/internal/backend/azure" 8 "github.com/restic/restic/internal/backend/b2" 9 "github.com/restic/restic/internal/backend/gs" 10 "github.com/restic/restic/internal/backend/local" 11 "github.com/restic/restic/internal/backend/rest" 12 "github.com/restic/restic/internal/backend/s3" 13 "github.com/restic/restic/internal/backend/sftp" 14 "github.com/restic/restic/internal/backend/swift" 15 "github.com/restic/restic/internal/errors" 16 ) 17 18 // Location specifies the location of a repository, including the method of 19 // access and (possibly) credentials needed for access. 20 type Location struct { 21 Scheme string 22 Config interface{} 23 } 24 25 type parser struct { 26 scheme string 27 parse func(string) (interface{}, error) 28 } 29 30 // parsers is a list of valid config parsers for the backends. The first parser 31 // is the fallback and should always be set to the local backend. 32 var parsers = []parser{ 33 {"b2", b2.ParseConfig}, 34 {"local", local.ParseConfig}, 35 {"sftp", sftp.ParseConfig}, 36 {"s3", s3.ParseConfig}, 37 {"gs", gs.ParseConfig}, 38 {"azure", azure.ParseConfig}, 39 {"swift", swift.ParseConfig}, 40 {"rest", rest.ParseConfig}, 41 } 42 43 func isPath(s string) bool { 44 if strings.HasPrefix(s, "../") || strings.HasPrefix(s, `..\`) { 45 return true 46 } 47 48 if strings.HasPrefix(s, "/") || strings.HasPrefix(s, `\`) { 49 return true 50 } 51 52 if len(s) < 3 { 53 return false 54 } 55 56 // check for drive paths 57 drive := s[0] 58 if !(drive >= 'a' && drive <= 'z') && !(drive >= 'A' && drive <= 'Z') { 59 return false 60 } 61 62 if s[1] != ':' { 63 return false 64 } 65 66 if s[2] != '\\' && s[2] != '/' { 67 return false 68 } 69 70 return true 71 } 72 73 // Parse extracts repository location information from the string s. If s 74 // starts with a backend name followed by a colon, that backend's Parse() 75 // function is called. Otherwise, the local backend is used which interprets s 76 // as the name of a directory. 77 func Parse(s string) (u Location, err error) { 78 scheme := extractScheme(s) 79 u.Scheme = scheme 80 81 for _, parser := range parsers { 82 if parser.scheme != scheme { 83 continue 84 } 85 86 u.Config, err = parser.parse(s) 87 if err != nil { 88 return Location{}, err 89 } 90 91 return u, nil 92 } 93 94 // if s is not a path or contains ":", it's ambiguous 95 if !isPath(s) && strings.ContainsRune(s, ':') { 96 return Location{}, errors.New("invalid backend\nIf the repo is in a local directory, you need to add a `local:` prefix") 97 } 98 99 u.Scheme = "local" 100 u.Config, err = local.ParseConfig("local:" + s) 101 if err != nil { 102 return Location{}, err 103 } 104 105 return u, nil 106 } 107 108 func extractScheme(s string) string { 109 data := strings.SplitN(s, ":", 2) 110 return data[0] 111 }