Skip to content

Commit ea5bc31

Browse files
committed
Add cobra generator application
1 parent 230787e commit ea5bc31

File tree

6 files changed

+1137
-0
lines changed

6 files changed

+1137
-0
lines changed

‎cobra/cmd/helpers.go‎

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
// Copyright © 2015 Steve Francia <spf@spf13.com>.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package cmd
15+
16+
import (
17+
"bytes"
18+
"fmt"
19+
"io"
20+
"os"
21+
"path/filepath"
22+
"strings"
23+
"text/template"
24+
"time"
25+
26+
"github.com/spf13/viper"
27+
)
28+
29+
// var BaseDir = ""
30+
// var AppName = ""
31+
// var CommandDir = ""
32+
33+
var funcMap template.FuncMap
34+
var projectPath = ""
35+
var inputPath = ""
36+
var projectBase = ""
37+
38+
// for testing only
39+
var testWd = ""
40+
41+
var cmdDirs = []string{"cmd", "cmds", "command", "commands"}
42+
43+
func init() {
44+
funcMap = template.FuncMap{
45+
"comment": commentifyString,
46+
}
47+
}
48+
49+
func er(msg interface{}) {
50+
fmt.Println("Error:", msg)
51+
os.Exit(-1)
52+
}
53+
54+
// Check if a file or directory exists.
55+
func exists(path string) (bool, error) {
56+
_, err := os.Stat(path)
57+
if err == nil {
58+
return true, nil
59+
}
60+
if os.IsNotExist(err) {
61+
return false, nil
62+
}
63+
return false, err
64+
}
65+
66+
func ProjectPath() string {
67+
if projectPath == "" {
68+
guessProjectPath()
69+
}
70+
71+
return projectPath
72+
}
73+
74+
// wrapper of the os package so we can test better
75+
func getWd() (string, error) {
76+
if testWd == "" {
77+
return os.Getwd()
78+
}
79+
return testWd, nil
80+
}
81+
82+
func guessCmdDir() string {
83+
guessProjectPath()
84+
if b, _ := isEmpty(projectPath); b {
85+
return "cmd"
86+
}
87+
88+
files, _ := filepath.Glob(projectPath + string(os.PathSeparator) + "c*")
89+
for _, f := range files {
90+
for _, c := range cmdDirs {
91+
if f == c {
92+
return c
93+
}
94+
}
95+
}
96+
97+
return "cmd"
98+
}
99+
100+
func guessImportPath() string {
101+
guessProjectPath()
102+
103+
if !strings.HasPrefix(projectPath, getSrcPath()) {
104+
er("Cobra only supports project within $GOPATH")
105+
}
106+
107+
return strings.TrimPrefix(projectPath, getSrcPath())
108+
}
109+
110+
func getSrcPath() string {
111+
return os.Getenv("GOPATH") + string(os.PathSeparator) + "src" + string(os.PathSeparator)
112+
}
113+
114+
func projectName() string {
115+
pp := ProjectPath()
116+
return filepath.Dir(pp)
117+
}
118+
119+
func guessProjectPath() {
120+
// if no path is provided... assume CWD.
121+
if inputPath == "" {
122+
x, err := getWd()
123+
if err != nil {
124+
er(err)
125+
}
126+
127+
// inspect CWD
128+
base := filepath.Base(x)
129+
130+
// if we are in the cmd directory.. back up
131+
for _, c := range cmdDirs {
132+
fmt.Print(c)
133+
if base == c {
134+
projectPath = filepath.Dir(x)
135+
return
136+
}
137+
}
138+
139+
if projectPath == "" {
140+
projectPath = x
141+
return
142+
}
143+
}
144+
145+
srcPath := getSrcPath()
146+
// if provided inspect for logical locations
147+
if strings.ContainsRune(inputPath, os.PathSeparator) {
148+
if filepath.IsAbs(inputPath) {
149+
// if Absolute, use it
150+
projectPath = inputPath
151+
return
152+
}
153+
// If not absolute but contains slashes.. assuming it means create it from $GOPATH
154+
count := strings.Count(inputPath, string(os.PathSeparator))
155+
156+
switch count {
157+
// If only one directory deep assume "github.com"
158+
case 1:
159+
projectPath = srcPath + "github.com" + string(os.PathSeparator) + inputPath
160+
return
161+
case 2:
162+
projectPath = srcPath + inputPath
163+
return
164+
default:
165+
er("Unknown directory")
166+
}
167+
} else {
168+
// hardest case.. just a word.
169+
if projectBase == "" {
170+
x, err := getWd()
171+
if err == nil {
172+
projectPath = x + string(os.PathSeparator) + inputPath
173+
return
174+
}
175+
er(err)
176+
} else {
177+
projectPath = srcPath + projectBase + string(os.PathSeparator) + inputPath
178+
return
179+
}
180+
}
181+
}
182+
183+
// IsEmpty checks if a given path is empty.
184+
func isEmpty(path string) (bool, error) {
185+
if b, _ := exists(path); !b {
186+
return false, fmt.Errorf("%q path does not exist", path)
187+
}
188+
fi, err := os.Stat(path)
189+
if err != nil {
190+
return false, err
191+
}
192+
if fi.IsDir() {
193+
f, err := os.Open(path)
194+
// FIX: Resource leak - f.close() should be called here by defer or is missed
195+
// if the err != nil branch is taken.
196+
defer f.Close()
197+
if err != nil {
198+
return false, err
199+
}
200+
list, err := f.Readdir(-1)
201+
// f.Close() - see bug fix above
202+
return len(list) == 0, nil
203+
}
204+
return fi.Size() == 0, nil
205+
}
206+
207+
// IsDir checks if a given path is a directory.
208+
func isDir(path string) (bool, error) {
209+
fi, err := os.Stat(path)
210+
if err != nil {
211+
return false, err
212+
}
213+
return fi.IsDir(), nil
214+
}
215+
216+
// DirExists checks if a path exists and is a directory.
217+
func dirExists(path string) (bool, error) {
218+
fi, err := os.Stat(path)
219+
if err == nil && fi.IsDir() {
220+
return true, nil
221+
}
222+
if os.IsNotExist(err) {
223+
return false, nil
224+
}
225+
return false, err
226+
}
227+
228+
func writeTemplateToFile(path string, file string, template string, data interface{}) error {
229+
filename := filepath.Join(path, file)
230+
231+
fmt.Println(filename)
232+
r, err := templateToReader(template, data)
233+
234+
fmt.Println("err:", err)
235+
if err != nil {
236+
return err
237+
}
238+
239+
err = safeWriteToDisk(filename, r)
240+
fmt.Println("err:", err)
241+
242+
if err != nil {
243+
return err
244+
}
245+
return nil
246+
}
247+
248+
func writeStringToFile(path, file, text string) error {
249+
filename := filepath.Join(path, file)
250+
251+
r := strings.NewReader(text)
252+
err := safeWriteToDisk(filename, r)
253+
254+
if err != nil {
255+
return err
256+
}
257+
return nil
258+
}
259+
260+
func templateToReader(tpl string, data interface{}) (io.Reader, error) {
261+
tmpl := template.New("")
262+
tmpl.Funcs(funcMap)
263+
tmpl, err := tmpl.Parse(tpl)
264+
265+
if err != nil {
266+
return nil, err
267+
}
268+
buf := new(bytes.Buffer)
269+
err = tmpl.Execute(buf, data)
270+
271+
return buf, err
272+
}
273+
274+
// Same as WriteToDisk but checks to see if file/directory already exists.
275+
func safeWriteToDisk(inpath string, r io.Reader) (err error) {
276+
dir, _ := filepath.Split(inpath)
277+
ospath := filepath.FromSlash(dir)
278+
279+
if ospath != "" {
280+
err = os.MkdirAll(ospath, 0777) // rwx, rw, r
281+
if err != nil {
282+
return
283+
}
284+
}
285+
286+
ex, err := exists(inpath)
287+
if err != nil {
288+
return
289+
}
290+
if ex {
291+
return fmt.Errorf("%v already exists", inpath)
292+
}
293+
294+
file, err := os.Create(inpath)
295+
if err != nil {
296+
return
297+
}
298+
defer file.Close()
299+
300+
_, err = io.Copy(file, r)
301+
return
302+
}
303+
304+
func getLicense() License {
305+
l := whichLicense()
306+
if l != "" {
307+
if x, ok := Licenses[l]; ok {
308+
return x
309+
}
310+
}
311+
312+
return Licenses["apache"]
313+
}
314+
315+
func whichLicense() string {
316+
// if explicitly flagged use that
317+
if userLicense != "" {
318+
return matchLicense(userLicense)
319+
}
320+
321+
// if already present in the project, use that
322+
// TODO:Inpect project for existing license
323+
324+
// default to viper's setting
325+
326+
return matchLicense(viper.GetString("license"))
327+
}
328+
329+
func copyrightLine() string {
330+
author := viper.GetString("author")
331+
year := time.Now().Format("2006")
332+
333+
return "Copyright ©" + year + " " + author
334+
}
335+
336+
func commentifyString(in string) string {
337+
var newlines []string
338+
lines := strings.Split(in, "\n")
339+
for _, x := range lines {
340+
if !strings.HasPrefix(x, "//") {
341+
newlines = append(newlines, "// "+x)
342+
} else {
343+
newlines = append(newlines, x)
344+
}
345+
}
346+
return strings.Join(newlines, "\n")
347+
}

‎cobra/cmd/helpers_test.go‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
)
8+
9+
var _ = fmt.Println
10+
var _ = os.Stderr
11+
12+
func checkGuess(t *testing.T, wd, input, expected string) {
13+
testWd = wd
14+
inputPath = input
15+
guessProjectPath()
16+
17+
if projectPath != expected {
18+
t.Errorf("Unexpected Project Path. \n Got: %q\nExpected: %q\n", projectPath, expected)
19+
}
20+
21+
reset()
22+
}
23+
24+
func reset() {
25+
testWd = ""
26+
inputPath = ""
27+
projectPath = ""
28+
}
29+
30+
func TestProjectPath(t *testing.T) {
31+
checkGuess(t, "", "github.com/spf13/hugo", getSrcPath()+"github.com/spf13/hugo")
32+
checkGuess(t, "", "spf13/hugo", getSrcPath()+"github.com/spf13/hugo")
33+
checkGuess(t, "", "/bar/foo", "/bar/foo")
34+
checkGuess(t, "/bar/foo", "baz", "/bar/foo/baz")
35+
checkGuess(t, "/bar/foo/cmd", "", "/bar/foo")
36+
checkGuess(t, "/bar/foo/command", "", "/bar/foo")
37+
checkGuess(t, "/bar/foo/commands", "", "/bar/foo")
38+
}

0 commit comments

Comments
 (0)