github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+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  	flags "github.com/jessevdk/go-flags"
    12  )
    13  
    14  type Path string
    15  
    16  func (p Path) String() string {
    17  	return string(p)
    18  }
    19  
    20  func (_ Path) Complete(prefix string) []flags.Completion {
    21  	return completeWithTilde(prefix)
    22  }
    23  
    24  type PathWithExistenceCheck string
    25  
    26  func (_ PathWithExistenceCheck) Complete(prefix string) []flags.Completion {
    27  	return completeWithTilde(prefix)
    28  }
    29  
    30  func (p *PathWithExistenceCheck) UnmarshalFlag(path string) error {
    31  	_, err := os.Stat(path)
    32  	if err != nil {
    33  		if os.IsNotExist(err) {
    34  			return &flags.Error{
    35  				Type:    flags.ErrRequired,
    36  				Message: fmt.Sprintf("The specified path '%s' does not exist.", path),
    37  			}
    38  		}
    39  		return err
    40  	}
    41  
    42  	*p = PathWithExistenceCheck(path)
    43  	return nil
    44  }
    45  
    46  type JSONOrFileWithValidation map[string]interface{}
    47  
    48  func (_ JSONOrFileWithValidation) Complete(prefix string) []flags.Completion {
    49  	return completeWithTilde(prefix)
    50  }
    51  
    52  func (p *JSONOrFileWithValidation) UnmarshalFlag(pathOrJSON string) error {
    53  	var jsonBytes []byte
    54  
    55  	errorToReturn := &flags.Error{
    56  		Type:    flags.ErrRequired,
    57  		Message: "Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.",
    58  	}
    59  
    60  	_, err := os.Stat(pathOrJSON)
    61  	if err == nil {
    62  		jsonBytes, err = ioutil.ReadFile(pathOrJSON)
    63  		if err != nil {
    64  			return errorToReturn
    65  		}
    66  	} else {
    67  		jsonBytes = []byte(pathOrJSON)
    68  	}
    69  
    70  	var jsonMap map[string]interface{}
    71  
    72  	if jsonIsInvalid := json.Unmarshal(jsonBytes, &jsonMap); jsonIsInvalid != nil {
    73  		return errorToReturn
    74  	}
    75  
    76  	*p = JSONOrFileWithValidation(jsonMap)
    77  	return nil
    78  }
    79  
    80  type PathWithExistenceCheckOrURL string
    81  
    82  func (_ PathWithExistenceCheckOrURL) Complete(prefix string) []flags.Completion {
    83  	return completeWithTilde(prefix)
    84  }
    85  
    86  func (p *PathWithExistenceCheckOrURL) UnmarshalFlag(path string) error {
    87  	if !strings.HasPrefix(path, "http://") && !strings.HasPrefix(path, "https://") {
    88  		_, err := os.Stat(path)
    89  		if err != nil {
    90  			if os.IsNotExist(err) {
    91  				return &flags.Error{
    92  					Type:    flags.ErrRequired,
    93  					Message: fmt.Sprintf("The specified path '%s' does not exist.", path),
    94  				}
    95  			}
    96  			return err
    97  		}
    98  	}
    99  
   100  	*p = PathWithExistenceCheckOrURL(path)
   101  	return nil
   102  }
   103  
   104  type PathWithAt string
   105  
   106  func (_ PathWithAt) Complete(prefix string) []flags.Completion {
   107  	if prefix == "" || prefix[0] != '@' {
   108  		return nil
   109  	}
   110  
   111  	prefix = prefix[1:]
   112  
   113  	var homeDir string
   114  	if strings.HasPrefix(prefix, fmt.Sprintf("~%c", os.PathSeparator)) {
   115  		// when $HOME is empty this will complete on /, however this is not tested
   116  		homeDir = os.Getenv("HOME")
   117  		prefix = fmt.Sprintf("%s%s", homeDir, prefix[1:])
   118  	}
   119  
   120  	return findMatches(
   121  		fmt.Sprintf("%s*", prefix),
   122  		func(path string) string {
   123  			if homeDir != "" {
   124  				newPath, err := filepath.Rel(homeDir, path)
   125  				if err == nil {
   126  					path = filepath.Join("~", newPath)
   127  				}
   128  			}
   129  			return fmt.Sprintf("@%s", path)
   130  		})
   131  }
   132  
   133  type PathWithBool string
   134  
   135  func (_ PathWithBool) Complete(prefix string) []flags.Completion {
   136  	return append(
   137  		completions([]string{"true", "false"}, prefix, false),
   138  		completeWithTilde(prefix)...,
   139  	)
   140  }
   141  
   142  func findMatches(pattern string, formatMatch func(string) string) []flags.Completion {
   143  	paths, _ := filepath.Glob(pattern)
   144  	if paths == nil {
   145  		return nil
   146  	}
   147  
   148  	matches := []flags.Completion{}
   149  	for _, path := range paths {
   150  		info, err := os.Stat(path)
   151  		if err != nil {
   152  			continue
   153  		}
   154  
   155  		formattedMatch := formatMatch(path)
   156  		if info.IsDir() {
   157  			formattedMatch = fmt.Sprintf("%s%c", formattedMatch, os.PathSeparator)
   158  		}
   159  		matches = append(matches, flags.Completion{Item: formattedMatch})
   160  	}
   161  
   162  	return matches
   163  }
   164  
   165  func completeWithTilde(prefix string) []flags.Completion {
   166  	var homeDir string
   167  	if strings.HasPrefix(prefix, fmt.Sprintf("~%c", os.PathSeparator)) {
   168  		// when $HOME is empty this will complete on /, however this is not tested
   169  		homeDir = os.Getenv("HOME")
   170  		prefix = fmt.Sprintf("%s%s", homeDir, prefix[1:])
   171  	}
   172  
   173  	return findMatches(
   174  		fmt.Sprintf("%s*", prefix),
   175  		func(path string) string {
   176  			if homeDir != "" {
   177  				newPath, err := filepath.Rel(homeDir, path)
   178  				if err == nil {
   179  					path = filepath.Join("~", newPath)
   180  				}
   181  			}
   182  			return path
   183  		})
   184  }