github.com/matrixorigin/matrixone@v0.7.0/pkg/fileservice/path.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fileservice 16 17 import ( 18 "encoding/csv" 19 "os" 20 "strings" 21 "unicode" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 ) 25 26 const ServiceNameSeparator = ":" 27 28 type Path struct { 29 Service string 30 ServiceArguments []string 31 File string 32 } 33 34 func ParsePath(s string) (path Path, err error) { 35 // split 36 i := strings.LastIndex(s, ServiceNameSeparator) 37 if i == -1 { 38 // no service part 39 path.File = s 40 } else { 41 // with service part 42 path.Service, path.ServiceArguments, err = parseService(s[:i]) 43 if err != nil { 44 return 45 } 46 path.File = s[i+1:] 47 } 48 49 // validate 50 for _, r := range path.File { 51 // most common patterns first 52 if r >= '0' && r <= '9' || 53 r >= 'a' && r <= 'z' || 54 r >= 'A' && r <= 'Z' { 55 continue 56 } 57 switch r { 58 case '!', '-', '_', '.', '*', '\'', '(', ')', '@', '/': 59 continue 60 } 61 // printable non-ASCII characters 62 if r > unicode.MaxASCII && unicode.IsPrint(r) { 63 continue 64 } 65 err = moerr.NewInvalidPathNoCtx(path.File) 66 return 67 } 68 69 path.File = strings.TrimLeft(path.File, "/") // trim leading / 70 71 return 72 } 73 74 func parseService(str string) (service string, arguments []string, err error) { 75 r := csv.NewReader(strings.NewReader(str)) 76 records, err := r.ReadAll() 77 if err != nil { 78 return 79 } 80 if len(records) != 1 { 81 err = moerr.NewInvalidInputNoCtx("bad service: %v", str) 82 return 83 } 84 service = records[0][0] 85 arguments = records[0][1:] 86 return 87 } 88 89 func ParsePathAtService(s string, serviceName string) (path Path, err error) { 90 path, err = ParsePath(s) 91 if err != nil { 92 return 93 } 94 if serviceName != "" && 95 path.Service != "" && 96 !strings.EqualFold(path.Service, serviceName) { 97 err = moerr.NewWrongServiceNoCtx(serviceName, path.Service) 98 return 99 } 100 return 101 } 102 103 func JoinPath(serviceName string, path string) string { 104 buf := new(strings.Builder) 105 buf.WriteString(serviceName) 106 buf.WriteString(ServiceNameSeparator) 107 buf.WriteString(path) 108 return buf.String() 109 } 110 111 var osPathSeparatorStr = string([]rune{os.PathSeparator}) 112 113 func toOSPath(filePath string) string { 114 if os.PathSeparator == '/' { 115 return filePath 116 } 117 return strings.ReplaceAll(filePath, "/", osPathSeparatorStr) 118 }