package main import ( "bytes" "flag" "fmt" "io" "io/ioutil" "local/pt-todo-server/pttodo" "log" "os" "os/exec" "path" "syscall" "gopkg.in/yaml.v2" ) func main() { if err := _main(); err != nil { panic(err) } } func _main() error { defaultFilepath, ok := os.LookupEnv("PTTODO_FILE") if !ok { defaultFilepath = "-" } filepath := flag.String("f", defaultFilepath, "($PTTODO_FILE) path to yaml file") e := flag.Bool("e", false, "edit file") dry := flag.Bool("dry", false, "dry run") flag.Parse() if *e { if err := edit(*dry, *filepath); err != nil { return err } } return dump(*dry, os.Stdout, *filepath) } func edit(dry bool, filepath string) error { var tempFile string cp := func() error { f, err := ioutil.TempFile(os.TempDir(), path.Base(filepath)) if err != nil { return err } if _, err := os.Stat(filepath); err == nil { g, err := os.Open(filepath) if err != nil { return err } if _, err := io.Copy(f, g); err != nil { return err } g.Close() } f.Close() tempFile = f.Name() return nil } vi := func() error { bin := "vim" if editor := os.Getenv("EDITOR"); editor != "" { bin = editor } editorbin, err := exec.LookPath(bin) if err != nil { editorbin, err = exec.LookPath("vi") } if err != nil { return err } args := []string{editorbin, tempFile} vimrc := path.Join(os.Getenv("HOME"), ".vimrc") if _, err := os.Stat(vimrc); err == nil { args = append(args, "-u", vimrc) } cpid, err := syscall.ForkExec( editorbin, args, &syscall.ProcAttr{ Dir: "", Env: os.Environ(), Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, Sys: nil, }, ) if err != nil { return err } proc, err := os.FindProcess(cpid) if err != nil { return err } state, err := proc.Wait() if err != nil { return err } if exitCode := state.ExitCode(); exitCode != 0 { return fmt.Errorf("bad exit code on vim: %d, state: %+v", exitCode, state) } return nil } verify := func() error { if err := dump(true, io.Discard, tempFile); err != nil { return fmt.Errorf("failed to verify %s: %v", tempFile, err) } return nil } save := func() error { if dry { log.Printf("would've saved %s as %s", tempFile, filepath) return nil } return os.Rename(tempFile, filepath) } for _, foo := range []func() error{cp, vi, verify, save} { if err := foo(); err != nil { return err } } if !dry { os.Remove(tempFile) } return nil } func dump(dry bool, writer io.Writer, filepath string) error { var reader io.Reader if filepath == "-" { reader = os.Stdin } else { b, err := ioutil.ReadFile(filepath) if err != nil { return err } reader = bytes.NewReader(b) } b, err := ioutil.ReadAll(reader) if err != nil { return err } var root pttodo.Root if err := yaml.Unmarshal(b, &root); err != nil { return err } root.MoveScheduledToTodo() var v interface{} = root switch flag.Arg(0) { case "": case "todo": v = root.Todo case "scheduled": v = root.Scheduled case "done": v = root.Done } b2, err := yaml.Marshal(v) if err != nil { return err } fmt.Fprintf(writer, "%s\n", b2) if dry { return nil } return os.WriteFile(filepath, b2, os.ModePerm) }