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  }