github.com/sleungcy/cli@v7.1.0+incompatible/command/flag/path.go (about) 1 package flag 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "code.cloudfoundry.org/cli/util/v6manifestparser" 12 "github.com/jessevdk/go-flags" 13 ) 14 15 type Path string 16 17 func (p Path) String() string { 18 return string(p) 19 } 20 21 func (Path) Complete(prefix string) []flags.Completion { 22 return completeWithTilde(prefix) 23 } 24 25 type PathWithExistenceCheck string 26 27 func (PathWithExistenceCheck) Complete(prefix string) []flags.Completion { 28 return completeWithTilde(prefix) 29 } 30 31 func (p *PathWithExistenceCheck) UnmarshalFlag(path string) error { 32 _, err := checkIfFileExists(path) 33 if err != nil { 34 return err 35 } 36 37 path, err = filepath.Abs(path) 38 if err != nil { 39 return err 40 } 41 42 *p = PathWithExistenceCheck(path) 43 return nil 44 } 45 46 type ManifestPathWithExistenceCheck string 47 48 func (ManifestPathWithExistenceCheck) Complete(prefix string) []flags.Completion { 49 return completeWithTilde(prefix) 50 } 51 52 func (p *ManifestPathWithExistenceCheck) UnmarshalFlag(path string) error { 53 fileInfo, err := checkIfFileExists(path) 54 if err != nil { 55 return err 56 } 57 58 if fileInfo.IsDir() { 59 locator := v6manifestparser.NewLocator() 60 pathToFile, existsInDirectory, err := locator.Path(path) 61 if err != nil { 62 return err 63 } 64 65 if !existsInDirectory { 66 return &flags.Error{ 67 Type: flags.ErrRequired, 68 Message: fmt.Sprintf("The specified directory '%s' does not contain a file named 'manifest.yml'.", path), 69 } 70 } 71 72 *p = ManifestPathWithExistenceCheck(pathToFile) 73 } else { 74 *p = ManifestPathWithExistenceCheck(path) 75 } 76 77 return nil 78 } 79 80 type JSONOrFileWithValidation map[string]interface{} 81 82 func (JSONOrFileWithValidation) Complete(prefix string) []flags.Completion { 83 return completeWithTilde(prefix) 84 } 85 86 func (p *JSONOrFileWithValidation) UnmarshalFlag(pathOrJSON string) error { 87 var jsonBytes []byte 88 89 errorToReturn := &flags.Error{ 90 Type: flags.ErrRequired, 91 Message: "Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.", 92 } 93 94 _, err := os.Stat(pathOrJSON) 95 if err == nil { 96 jsonBytes, err = ioutil.ReadFile(pathOrJSON) 97 if err != nil { 98 return errorToReturn 99 } 100 } else { 101 jsonBytes = []byte(pathOrJSON) 102 } 103 104 var jsonMap map[string]interface{} 105 106 if jsonIsInvalid := json.Unmarshal(jsonBytes, &jsonMap); jsonIsInvalid != nil { 107 return errorToReturn 108 } 109 110 *p = JSONOrFileWithValidation(jsonMap) 111 return nil 112 } 113 114 type PathWithExistenceCheckOrURL string 115 116 func (PathWithExistenceCheckOrURL) Complete(prefix string) []flags.Completion { 117 return completeWithTilde(prefix) 118 } 119 120 func (p *PathWithExistenceCheckOrURL) UnmarshalFlag(path string) error { 121 if !strings.HasPrefix(path, "http://") && !strings.HasPrefix(path, "https://") { 122 _, err := os.Stat(path) 123 if err != nil { 124 if os.IsNotExist(err) { 125 return &flags.Error{ 126 Type: flags.ErrRequired, 127 Message: fmt.Sprintf("The specified path '%s' does not exist.", path), 128 } 129 } 130 return err 131 } 132 } 133 134 *p = PathWithExistenceCheckOrURL(path) 135 return nil 136 } 137 138 type PathWithAt string 139 140 func (PathWithAt) Complete(prefix string) []flags.Completion { 141 if prefix == "" || prefix[0] != '@' { 142 return nil 143 } 144 145 prefix = prefix[1:] 146 147 var homeDir string 148 if strings.HasPrefix(prefix, fmt.Sprintf("~%c", os.PathSeparator)) { 149 // when $HOME is empty this will complete on /, however this is not tested 150 homeDir = os.Getenv("HOME") 151 prefix = fmt.Sprintf("%s%s", homeDir, prefix[1:]) 152 } 153 154 return findMatches( 155 fmt.Sprintf("%s*", prefix), 156 func(path string) string { 157 if homeDir != "" { 158 newPath, err := filepath.Rel(homeDir, path) 159 if err == nil { 160 path = filepath.Join("~", newPath) 161 } 162 } 163 return fmt.Sprintf("@%s", path) 164 }) 165 } 166 167 type PathWithBool string 168 169 func (PathWithBool) Complete(prefix string) []flags.Completion { 170 return append( 171 completions([]string{"true", "false"}, prefix, false), 172 completeWithTilde(prefix)..., 173 ) 174 } 175 176 func findMatches(pattern string, formatMatch func(string) string) []flags.Completion { 177 paths, err := filepath.Glob(pattern) 178 if paths == nil || err != nil { 179 return nil 180 } 181 182 matches := []flags.Completion{} 183 for _, path := range paths { 184 info, err := os.Stat(path) 185 if err != nil { 186 continue 187 } 188 189 formattedMatch := formatMatch(path) 190 if info.IsDir() { 191 formattedMatch = fmt.Sprintf("%s%c", formattedMatch, os.PathSeparator) 192 } 193 matches = append(matches, flags.Completion{Item: formattedMatch}) 194 } 195 196 return matches 197 } 198 199 func completeWithTilde(prefix string) []flags.Completion { 200 var homeDir string 201 if strings.HasPrefix(prefix, fmt.Sprintf("~%c", os.PathSeparator)) { 202 // when $HOME is empty this will complete on /, however this is not tested 203 homeDir = os.Getenv("HOME") 204 prefix = fmt.Sprintf("%s%s", homeDir, prefix[1:]) 205 } 206 207 return findMatches( 208 fmt.Sprintf("%s*", prefix), 209 func(path string) string { 210 if homeDir != "" { 211 newPath, err := filepath.Rel(homeDir, path) 212 if err == nil { 213 path = filepath.Join("~", newPath) 214 } 215 } 216 return path 217 }) 218 } 219 220 func checkIfFileExists(path string) (os.FileInfo, error) { 221 fileInfo, err := os.Stat(path) 222 if err != nil { 223 if os.IsNotExist(err) { 224 return nil, &flags.Error{ 225 Type: flags.ErrRequired, 226 Message: fmt.Sprintf("The specified path '%s' does not exist.", path), 227 } 228 } 229 return nil, err 230 } 231 return fileInfo, nil 232 }