M entries.go => entries.go +13 -0
@@ 18,6 18,19 @@ type Entry struct {
End time.Time
}
+//Compare returns -1 if f is before e
+//0 if f overlaps e
+//1 if f is after e
+func (e Entry) Compare(f Entry) int {
+ if f.Start.Before(e.Start) && f.End.Before(e.Start) {
+ return -1
+ }
+ if f.Start.After(e.Start) && f.End.After(e.Start) {
+ return 1
+ }
+ return 0
+}
+
func (e Entry) String() string {
return fmt.Sprintf(
"{Project:%s, Description:'%s', Start: %s, End: %s}",
M go.mod => go.mod +1 -1
@@ 19,5 19,5 @@ require (
github.com/posener/script v1.1.5 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
- golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect
+ golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect
)
M go.sum => go.sum +2 -0
@@ 53,6 53,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8=
+golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.5-0.20201125200606-c27b9fd57aec/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
M main.go => main.go +23 -30
@@ 38,10 38,13 @@ var CLI struct {
To []string `required help:"specify the primary recipients of the emails generated"`
From string `required help:"specify the sender of the emails" env:"SENDMAIL_FROM"`
} `cmd help:"sends the report by email"`
- DataFile string `optional short:"d" help:"path to the wt data file (default:$HOME/.local/wt.data)" predictor:"file"`
+ Merge struct {
+ Path string `arg required type:"path" help:"path to a data file to merge with the default wt data file"`
+ } `cmd help:"merge an external wt data file"`
+ DataFile string `optional short:"d" help:"path to the wt data file (default:$HOME/.local/wt.data)" type:"path"`
InstallCompletions struct {
Uninstall bool
- } `cmd:"" help:"install shell completions"`
+ } `cmd help:"install shell completions"`
}
func main() {
@@ 74,6 77,9 @@ func main() {
"from": predict.Something,
},
},
+ "merge": {
+ Args: predict.Files("*"),
+ },
},
Flags: map[string]complete.Predictor{
"data-file": predict.Files("*"),
@@ 90,40 96,29 @@ func main() {
kong.UsageOnError(),
)
+ var err error
switch ctx.Command() {
case "start <project> <description>":
- if err := start(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ err = start()
case "end":
- if err := end(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ end()
case "report", "report <month>":
- if err := report(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ err = report()
case "send-report", "send-report <month>":
- if err := sendreport(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ err = sendreport()
case "status":
- if err := status(); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ err = status()
+ case "merge <path>":
+ err = merge()
case "install-completions":
- if err := install.Install("wt"); err != nil {
- fmt.Fprintf(os.Stderr, "%s\n", err)
- os.Exit(1)
- }
+ err = install.Install("wt")
default:
log.Fatal(ctx.Command())
}
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
}
func loadEntries() (Entries, error) {
@@ 187,10 182,7 @@ func start() error {
},
es...,
)
- if err := saveEntries(es); err != nil {
- return err
- }
- return nil
+ return saveEntries(es)
}
func end() error {
@@ 215,6 207,7 @@ func end() error {
if err := saveEntries(es); err != nil {
return err
}
+ //TODO prettier print
fmt.Printf("Ending entry %s", last)
return nil
}
A merge.go => merge.go +62 -0
@@ 0,0 1,62 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "sort"
+
+ "github.com/gookit/color"
+)
+
+func merge() error {
+ f, err := os.Open(CLI.Merge.Path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ newEntries, err := Parse(f)
+ if err != nil {
+ return err
+ }
+
+ es, err := loadEntries()
+ if err != nil {
+ return fmt.Errorf("loading entries: %w", err)
+ }
+
+ fmt.Println()
+ for _, e := range newEntries {
+ i := sort.Search(len(es), func(i int) bool {
+ x := es[i].Compare(e)
+ return x == 1 || x == 0
+ })
+ if i < len(es) && es[i].Compare(e) == 0 {
+ // e overlaps at es[i]
+ return fmt.Errorf(
+ "Entry %s %s %s-%s overlaps existing entry in default data file: %s %s %s-%s",
+ e.Project,
+ e.Description,
+ e.Start.Format("2006-01-02 15:04:05"),
+ e.End.Format("2006-01-02 15:04:05"),
+ es[i].Project,
+ es[i].Description,
+ es[i].Start.Format("2006-01-02 15:04:05"),
+ es[i].End.Format("2006-01-02 15:04:05"),
+ )
+ } else {
+ // e is not present in es,
+ // but i is the index where it would be inserted.
+ fmt.Printf(
+ " %s\n %s\t\t%s\n\t\t\t%s-%s\n",
+ color.Style{color.FgLightWhite, color.OpBold}.Render("inserting entry:"),
+ color.FgCyan.Render(e.Project),
+ color.FgLightWhite.Render(e.Description),
+ color.FgMagenta.Sprint(e.Start.Format("15:04:05")),
+ color.FgMagenta.Sprint(e.End.Format("15:04:05")),
+ )
+ es = append(es[:i], append([]Entry{e}, es[i:]...)...)
+ }
+ }
+
+ return saveEntries(es)
+}