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