package misc import ( "bufio" "bytes" "errors" "flag" "fmt" "io" "io/ioutil" "net/http" "os" "os/exec" "os/user" "path/filepath" "runtime" "strings" ) func DecorateError(s string, err error) error { if err != nil { return errors.New(s + ": " + err.Error()) } else { return nil } } func WriteFile(filename string, data string) error { return ioutil.WriteFile(filename, []byte(data), 0644) } func AppendToFile(filename string, data string) error { f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return errors.New("AppendToFile " + filename + ": " + err.Error()) } defer f.Close() _, err = f.WriteString(data) return err } func ReadFile(filename string) (string, error) { data, err := ioutil.ReadFile(filename) return string(data), err } func TrimExt(path string) string { extension := filepath.Ext(path) return path[0 : len(path)-len(extension)] } func ListFilesExt(dir string, ext string) []string { list := make([]string, 0) ext = strings.ToUpper(ext) files, err := ioutil.ReadDir(dir) if err == nil { for _, file := range files { if !file.IsDir() { if strings.ToUpper(filepath.Ext(file.Name())) == ext { list = append(list, filepath.Join(dir, file.Name())) } } } } return list } func RecListFilesExt(dir string, ext string) []string { list := make([]string, 0) traverse := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { list = append(list, ListFilesExt(path, ext)...) } return nil } filepath.Walk(dir, traverse) return list } func PathExists(path string) bool { if _, err := os.Stat(path); os.IsNotExist(err) { return false } else { return true } } func DirsWithPrefix(path string, prefix string) ([]string, error) { dirs := []string{} files, err := ioutil.ReadDir(path) if err != nil { return dirs, err } for _, file := range files { if file.IsDir() { if name := file.Name(); strings.HasPrefix(name, prefix) { dirs = append(dirs, name) } } } return dirs, nil } func HomeDir() (string, error) { d := "" currentUser, err := user.Current() if err == nil { d = currentUser.HomeDir } return d, err } func AskFor(question string) (string, error) { fmt.Print(question + ": ") reader := bufio.NewReader(os.Stdin) s, err := reader.ReadString('\n') if err == nil { return strings.TrimSpace(s), nil } else { return "", err } } func OptDo(err error, f func() error) error { if err == nil { return f() } else { return err } } func OptPanic(err error) { if err != nil { panic(err) } } func IntMin(x, y int) int { if x < y { return x } else { return y } } func IntMax(x, y int) int { if x > y { return x } else { return y } } func StrSliceAt(sl []string, i int) string { if i < len(sl) { return sl[i] } else { return "" } } func StrSlice(sl []string, start int, end int) []string { bound := func(i int) int { return IntMin(IntMax(0, i), len(sl)) } start = bound(start) end = bound(end) return sl[start:end] } func OpenInDefaultApp(filename string, wait bool) error { var cmd *exec.Cmd goos := runtime.GOOS switch goos { case "windows": cmd = exec.Command(filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe"), "url.dll,FileProtocolHandler", filename) case "darwin": cmd = exec.Command("open", filename) default: cmd = exec.Command("xdg-open", filename) } if wait { err := cmd.Run() if goos == "windows" { AskFor("Press Enter when you're done") } return err } else { return cmd.Start() } } func OpenInEditor(filename string, wait bool) error { var err error goos := runtime.GOOS if ed := os.Getenv("EDITOR"); ed != "" { cmd := exec.Command(ed, filename) cmd.Env = os.Environ() if wait { err = cmd.Run() if goos == "windows" { AskFor("Press Enter when you're done") } } else { err = cmd.Start() } } else { err = errors.New("EDITOR not set in environment") } return err } type CommandFunc func(args []string) error type CommandFlagsInit func(s *flag.FlagSet) type CommandInitExecFunc func() (CommandFlagsInit, CommandFunc) type Command struct { Cmd string Exec CommandFunc Desc string Flags *flag.FlagSet } func NewCommandWithFlags(cmd string, f CommandInitExecFunc, desc string) Command { flagsInit, exec := f() var flags *flag.FlagSet if flagsInit != nil { flags = flag.NewFlagSet(cmd, flag.ExitOnError) flagsInit(flags) } c := Command{cmd, exec, desc, flags} return c } func NewCommand(cmd string, f CommandFunc, desc string) Command { return NewCommandWithFlags(cmd, func() (CommandFlagsInit, CommandFunc) { return nil, f }, desc) } type commandCollection map[string]Command func usageAndExit(cmds []Command) { app := os.Args[0] fmt.Println(app) fmt.Println() fmt.Println("Possible commands:") for _, cmd := range cmds { fmt.Println(" " + cmd.Cmd) fmt.Println(" \t" + cmd.Desc) } fmt.Println() fmt.Println("for detailed information type '" + app + " help COMMAND'") os.Exit(-1) } func Execute(possibleCommands []Command) error { commands := commandCollection{} for _, c := range possibleCommands { commands[c.Cmd] = c } arglen := len(os.Args) if arglen <= 1 { usageAndExit(possibleCommands) } cmdStr := os.Args[1] if cmdStr == "help" { if arglen == 2 { usageAndExit(possibleCommands) } else { cmd, ok := commands[os.Args[2]] if !ok { usageAndExit(possibleCommands) } else { fmt.Println("Description of " + cmd.Cmd + ":") fmt.Println(" " + cmd.Desc) if cmd.Flags != nil { fmt.Println("Possible flags:") cmd.Flags.PrintDefaults() } else { fmt.Println("No flags") } return nil } } } cmd, ok := commands[cmdStr] if !ok { usageAndExit(possibleCommands) } if cmd.Flags != nil { cmd.Flags.Parse(os.Args[2:]) return cmd.Exec(cmd.Flags.Args()) } else { return cmd.Exec(os.Args[2:]) } } func Notify(head string, body string) error { var cmd *exec.Cmd goos := runtime.GOOS switch goos { case "windows": cmd = nil case "darwin": cmd = exec.Command("osascript", "-e", `display notification "`+body+`" with title "`+head+`"`) default: cmd = exec.Command("notify-send", head, body) } if cmd != nil { return cmd.Start() } return nil } func DownloadAll(url string, limit int64) ([]byte, error) { var buf bytes.Buffer err := download(url, &buf, limit) return buf.Bytes(), err } func DownloadToFile(url string, dest string, limit int64) error { file, err := os.Create(dest) if err != nil { return errors.New("DownloadToFile: " + err.Error()) } defer file.Close() return download(url, file, limit) } func download(url string, dest io.Writer, limit int64) error { resp, err := http.Get(url) if err != nil { return errors.New("download: " + err.Error()) } defer resp.Body.Close() var reader io.Reader if limit > 0 { reader = io.LimitReader(resp.Body, limit) } else { reader = resp.Body } _, err = io.Copy(dest, reader) return err }