github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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  }