1package cmd 2 3import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 "strings" 8) 9 10// Project contains name, license and paths to projects. 11type Project struct { 12 absPath string 13 cmdPath string 14 srcPath string 15 license License 16 name string 17} 18 19// NewProject returns Project with specified project name. 20// If projectName is blank string, it returns nil. 21func NewProject(projectName string) *Project { 22 if projectName == "" { 23 return nil 24 } 25 26 p := new(Project) 27 p.name = projectName 28 29 // 1. Find already created protect. 30 p.absPath = findPackage(projectName) 31 32 // 2. If there are no created project with this path, and user is in GOPATH, 33 // then use GOPATH/src/projectName. 34 if p.absPath == "" { 35 wd, err := os.Getwd() 36 if err != nil { 37 er(err) 38 } 39 for _, srcPath := range srcPaths { 40 goPath := filepath.Dir(srcPath) 41 if filepathHasPrefix(wd, goPath) { 42 p.absPath = filepath.Join(srcPath, projectName) 43 break 44 } 45 } 46 } 47 48 // 3. If user is not in GOPATH, then use (first GOPATH)/src/projectName. 49 if p.absPath == "" { 50 p.absPath = filepath.Join(srcPaths[0], projectName) 51 } 52 53 return p 54} 55 56// findPackage returns full path to existing go package in GOPATHs. 57// findPackage returns "", if it can't find path. 58// If packageName is "", findPackage returns "". 59func findPackage(packageName string) string { 60 if packageName == "" { 61 return "" 62 } 63 64 for _, srcPath := range srcPaths { 65 packagePath := filepath.Join(srcPath, packageName) 66 if exists(packagePath) { 67 return packagePath 68 } 69 } 70 71 return "" 72} 73 74// NewProjectFromPath returns Project with specified absolute path to 75// package. 76// If absPath is blank string or if absPath is not actually absolute, 77// it returns nil. 78func NewProjectFromPath(absPath string) *Project { 79 if absPath == "" || !filepath.IsAbs(absPath) { 80 return nil 81 } 82 83 p := new(Project) 84 p.absPath = absPath 85 p.absPath = strings.TrimSuffix(p.absPath, findCmdDir(p.absPath)) 86 p.name = filepath.ToSlash(trimSrcPath(p.absPath, p.SrcPath())) 87 return p 88} 89 90// trimSrcPath trims at the beginning of absPath the srcPath. 91func trimSrcPath(absPath, srcPath string) string { 92 relPath, err := filepath.Rel(srcPath, absPath) 93 if err != nil { 94 er("Cobra supports project only within $GOPATH: " + err.Error()) 95 } 96 return relPath 97} 98 99// License returns the License object of project. 100func (p *Project) License() License { 101 if p.license.Text == "" && p.license.Name != "None" { 102 p.license = getLicense() 103 } 104 105 return p.license 106} 107 108// Name returns the name of project, e.g. "github.com/spf13/cobra" 109func (p Project) Name() string { 110 return p.name 111} 112 113// CmdPath returns absolute path to directory, where all commands are located. 114// 115// CmdPath returns blank string, only if p.AbsPath() is a blank string. 116func (p *Project) CmdPath() string { 117 if p.absPath == "" { 118 return "" 119 } 120 if p.cmdPath == "" { 121 p.cmdPath = filepath.Join(p.absPath, findCmdDir(p.absPath)) 122 } 123 return p.cmdPath 124} 125 126// findCmdDir checks if base of absPath is cmd dir and returns it or 127// looks for existing cmd dir in absPath. 128// If the cmd dir doesn't exist, empty, or cannot be found, 129// it returns "cmd". 130func findCmdDir(absPath string) string { 131 if !exists(absPath) || isEmpty(absPath) { 132 return "cmd" 133 } 134 135 if isCmdDir(absPath) { 136 return filepath.Base(absPath) 137 } 138 139 files, _ := filepath.Glob(filepath.Join(absPath, "c*")) 140 for _, file := range files { 141 if isCmdDir(file) { 142 return filepath.Base(file) 143 } 144 } 145 146 return "cmd" 147} 148 149// isCmdDir checks if base of name is one of cmdDir. 150func isCmdDir(name string) bool { 151 name = filepath.Base(name) 152 for _, cmdDir := range cmdDirs { 153 if name == cmdDir { 154 return true 155 } 156 } 157 return false 158} 159 160// AbsPath returns absolute path of project. 161func (p Project) AbsPath() string { 162 return p.absPath 163} 164 165// SrcPath returns absolute path to $GOPATH/src where project is located. 166func (p *Project) SrcPath() string { 167 if p.srcPath != "" { 168 return p.srcPath 169 } 170 if p.absPath == "" { 171 p.srcPath = srcPaths[0] 172 return p.srcPath 173 } 174 175 for _, srcPath := range srcPaths { 176 if filepathHasPrefix(p.absPath, srcPath) { 177 p.srcPath = srcPath 178 break 179 } 180 } 181 182 return p.srcPath 183} 184 185func filepathHasPrefix(path string, prefix string) bool { 186 if len(path) <= len(prefix) { 187 return false 188 } 189 if runtime.GOOS == "windows" { 190 // Paths in windows are case-insensitive. 191 return strings.EqualFold(path[0:len(prefix)], prefix) 192 } 193 return path[0:len(prefix)] == prefix 194 195} 196