// Copyright 2013 bee authors // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path" "path/filepath" "regexp" "runtime" "strings" "text/template" "time" ) // Go is a basic promise implementation: it wraps calls a function in a goroutine // and returns a channel which will later return the function's return value. func Go(f func() error) chan error { ch := make(chan error) go func() { ch <- f() }() return ch } // Now returns the current local time in the specified layout func Now(layout string) string { return time.Now().Format(layout) } // EndLine returns the a newline escape character func EndLine() string { return "\n" } // IsExist returns whether a file or directory exists. func isExist(path string) bool { _, err := os.Stat(path) return err == nil || os.IsExist(err) } // GetGOPATHs returns all paths in GOPATH variable. func GetGOPATHs() []string { gopath := os.Getenv("GOPATH") var paths []string if runtime.GOOS == "windows" { gopath = strings.Replace(gopath, "\\", "/", -1) paths = strings.Split(gopath, ";") } else { paths = strings.Split(gopath, ":") } return paths } // IsInGOPATH checks the path is in the fisrt GOPATH(/src) or not func IsInGOPATH(thePath string) bool { if runtime.GOOS == "windows" { thePath = filepath.ToSlash(thePath) } return strings.Contains(thePath, GetGOPATHs()[0]+"/src") } // IsBeegoProject checks whether the current path is a Beego application or not func IsBeegoProject(thePath string) bool { mainFiles := []string{} hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`) c := make(chan error) // Walk the application path tree to look for main files. // Main files must satisfy the 'hasBeegoRegex' regular expression. go func() { filepath.Walk(thePath, func(fpath string, f os.FileInfo, err error) error { if err != nil { return nil } // Skip sub-directories if !f.IsDir() { var data []byte data, err = ioutil.ReadFile(fpath) if err != nil { c <- err return nil } if len(hasBeegoRegex.Find(data)) > 0 { mainFiles = append(mainFiles, fpath) } } return nil }) close(c) }() if err := <-c; err != nil { logger.Fatalf("Unable to walk '%s' tree: %s", thePath, err) } if len(mainFiles) > 0 { return true } return false } // SearchGOPATHs searchs the user GOPATH(s) for the specified application name. // It returns a boolean, the application's GOPATH and its full path. func SearchGOPATHs(app string) (bool, string, string) { gps := GetGOPATHs() if len(gps) == 0 { logger.Fatal("GOPATH environment variable is not set or empty") } // Lookup the application inside the user workspace(s) for _, gopath := range gps { var currentPath string if !strings.Contains(app, "src") { gopathsrc := path.Join(gopath, "src") currentPath = path.Join(gopathsrc, app) } else { currentPath = app } if isExist(currentPath) { return true, gopath, currentPath } } return false, "", "" } // askForConfirmation uses Scanln to parse user input. A user must type in "yes" or "no" and // then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", and "Yes" all count as // confirmations. If the input is not recognized, it will ask again. The function does not return // until it gets a valid response from the user. Typically, you should use fmt to print out a question // before calling askForConfirmation. E.g. fmt.Println("WARNING: Are you sure? (yes/no)") func askForConfirmation() bool { var response string _, err := fmt.Scanln(&response) if err != nil { logger.Fatalf("%s", err) } okayResponses := []string{"y", "Y", "yes", "Yes", "YES"} nokayResponses := []string{"n", "N", "no", "No", "NO"} if containsString(okayResponses, response) { return true } else if containsString(nokayResponses, response) { return false } else { fmt.Println("Please type yes or no and then press enter:") return askForConfirmation() } } func containsString(slice []string, element string) bool { for _, elem := range slice { if elem == element { return true } } return false } // snake string, XxYy to xx_yy func snakeString(s string) string { data := make([]byte, 0, len(s)*2) j := false num := len(s) for i := 0; i < num; i++ { d := s[i] if i > 0 && d >= 'A' && d <= 'Z' && j { data = append(data, '_') } if d != '_' { j = true } data = append(data, d) } return strings.ToLower(string(data[:])) } func camelString(s string) string { data := make([]byte, 0, len(s)) j := false k := false num := len(s) - 1 for i := 0; i <= num; i++ { d := s[i] if k == false && d >= 'A' && d <= 'Z' { k = true } if d >= 'a' && d <= 'z' && (j || k == false) { d = d - 32 j = false k = true } if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' { j = true continue } data = append(data, d) } return string(data[:]) } // camelCase converts a _ delimited string to camel case // e.g. very_important_person => VeryImportantPerson func camelCase(in string) string { tokens := strings.Split(in, "_") for i := range tokens { tokens[i] = strings.Title(strings.Trim(tokens[i], " ")) } return strings.Join(tokens, "") } // formatSourceCode formats source files func formatSourceCode(filename string) { cmd := exec.Command("gofmt", "-w", filename) if err := cmd.Run(); err != nil { logger.Warnf("Error while running gofmt: %s", err) } } // The string flag list, implemented flag.Value interface type strFlags []string func (s *strFlags) String() string { return fmt.Sprintf("%s", *s) } func (s *strFlags) Set(value string) error { *s = append(*s, value) return nil } // CloseFile attempts to close the passed file // or panics with the actual error func CloseFile(f *os.File) { err := f.Close() MustCheck(err) } // MustCheck panics when the error is not nil func MustCheck(err error) { if err != nil { panic(err) } } func exitPrint(con string) { fmt.Fprintln(os.Stderr, con) os.Exit(2) } // WriteToFile creates a file and writes content to it func WriteToFile(filename, content string) { f, err := os.Create(filename) MustCheck(err) defer CloseFile(f) _, err = f.WriteString(content) MustCheck(err) } // IsDebugEnabled checks if DEBUG_ENABLED is set or not func IsDebugEnabled() bool { debugMode := os.Getenv("DEBUG_ENABLED") return map[string]bool{"1": true, "0": false}[debugMode] } // __FILE__ returns the file name in which the function was invoked func __FILE__() string { _, file, _, _ := runtime.Caller(1) return file } // __LINE__ returns the line number at which the function was invoked func __LINE__() int { _, _, line, _ := runtime.Caller(1) return line } // BeeFuncMap returns a FuncMap of functions used in different templates. func BeeFuncMap() template.FuncMap { return template.FuncMap{ "trim": strings.TrimSpace, "bold": bold, "headline": MagentaBold, "foldername": RedBold, "endline": EndLine, "tmpltostr": TmplToString, } } // TmplToString parses a text template and return the result as a string. func TmplToString(tmpl string, data interface{}) string { t := template.New("tmpl").Funcs(BeeFuncMap()) template.Must(t.Parse(tmpl)) var doc bytes.Buffer err := t.Execute(&doc, data) MustCheck(err) return doc.String() }