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