Skip to content

Instantly share code, notes, and snippets.

@irozgar
Created January 24, 2024 18:58
Show Gist options
  • Save irozgar/c2a0a8e6f16fdc6aa800d3edd4dd381f to your computer and use it in GitHub Desktop.
Save irozgar/c2a0a8e6f16fdc6aa800d3edd4dd381f to your computer and use it in GitHub Desktop.
wlg
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"time"
)
const appConfigHome = "wlg"
type Break struct {
StartTime time.Time
EndTime *time.Time
}
type Workday struct {
Date time.Time
StartTime time.Time
EndTime *time.Time
Breaks []Break
}
type AppData struct {
ActiveDay *Workday `json:"activeDay"`
Workdays []Workday `json:"workdays"`
}
func createBreak(startTime time.Time) Break {
return Break{StartTime: startTime}
}
func createWorkday(date time.Time, startTime time.Time) Workday {
return Workday{Date: date, StartTime: startTime}
}
func createAppData() AppData {
return AppData{Workdays: []Workday{}}
}
func loadAppData(a *AppData) error {
home, err := os.UserConfigDir()
if err != nil {
panic(err)
}
configFilepath := filepath.Join(home, appConfigHome, "config.json")
file, err := os.Open(configFilepath)
if err != nil {
return err
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
panic(err)
}
}(file)
data, err := io.ReadAll(file)
if err != nil {
panic(err)
}
err = json.Unmarshal(data, &a)
if err != nil {
panic(err)
}
return nil
}
func (w *Workday) endWork(endTime time.Time) {
w.EndTime = &endTime
}
func (w *Workday) startBreak(startTime time.Time) {
lb := w.getLastBreak()
if lb != nil && lb.EndTime == nil {
panic("Last break is still active")
}
w.Breaks = append(w.Breaks, createBreak(startTime))
}
func (w *Workday) endBreak(endTime time.Time) {
lb := w.getLastBreak()
if lb == nil || lb.EndTime != nil {
panic("No active break")
}
lb.endBreak(endTime)
}
func (w *Workday) getBreakTime() time.Duration {
total := time.Duration(0)
for _, b := range w.Breaks {
if b.EndTime == nil {
total += time.Now().Sub(b.StartTime)
} else {
total += b.EndTime.Sub(b.StartTime)
}
}
return total
}
func (w *Workday) getWorkTime() time.Duration {
if w.EndTime == nil {
return time.Now().Sub(w.StartTime) - w.getBreakTime()
}
return w.EndTime.Sub(w.StartTime) - w.getBreakTime()
}
func (w *Workday) getLastBreak() *Break {
if len(w.Breaks) == 0 {
return nil
}
return &w.Breaks[len(w.Breaks)-1]
}
func (w *Workday) getEstimatedEndTime() time.Time {
return w.StartTime.Add(8*time.Hour + w.getBreakTime())
}
func (b *Break) endBreak(endTime time.Time) {
b.EndTime = &endTime
}
func (a *AppData) writeToFile() {
home, err := os.UserConfigDir()
if err != nil {
panic(err)
}
configHome := filepath.Join(home, appConfigHome)
err = os.MkdirAll(configHome, os.ModePerm)
if err != nil {
panic(err)
}
configFilepath := filepath.Join(configHome, "data.json")
file, err := os.Create(configFilepath)
if err != nil {
panic(err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
panic(err)
}
}(file)
data, err := json.Marshal(a)
if err != nil {
panic(err)
}
log.Println(string(data))
_, err = file.WriteString(string(data))
if err != nil {
panic(err)
}
}
func formatDuration(d time.Duration) string {
total := int(d.Seconds())
hours := total / 3600
minutes := (total % 3600) / 60
seconds := total % 60
return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
}
func defaultConfigPath() string {
home, err := os.UserConfigDir()
if err != nil {
panic(err)
}
return filepath.Join(home, appConfigHome, "config.json")
}
func main() {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s <command>\n", os.Args[0])
fmt.Fprintf(flag.CommandLine.Output(), "Commands:\n")
fmt.Fprintf(flag.CommandLine.Output(), " day <subcommand>\n")
fmt.Fprintf(flag.CommandLine.Output(), " arrive\n")
fmt.Fprintf(flag.CommandLine.Output(), " leave\n")
fmt.Fprintf(flag.CommandLine.Output(), " check\n")
fmt.Fprintf(flag.CommandLine.Output(), " break\n")
fmt.Fprintf(flag.CommandLine.Output(), " back\n")
fmt.Fprintf(flag.CommandLine.Output(), "\n")
}
flag.Parse()
d := createAppData()
err := loadAppData(&d)
if err != nil && os.IsNotExist(err) {
d.writeToFile()
} else if err != nil {
panic(err)
}
if len(os.Args) < 3 {
flag.Usage()
os.Exit(1)
}
switch os.Args[1] {
case "day":
switch os.Args[2] {
case "arrive":
if d.ActiveDay != nil {
panic("Active workday already exists")
}
w := createWorkday(time.Now(), time.Now())
d.ActiveDay = &w
d.writeToFile()
fmt.Println("Hello, your workday will end at " + w.StartTime.Add(8*time.Hour).Format("15:04:05"))
case "leave":
w := d.ActiveDay
if w == nil {
panic("No active workday")
}
w.endWork(time.Now())
d.ActiveDay = nil
d.Workdays = append(d.Workdays, *w)
d.writeToFile()
fmt.Println("Goodbye, you worked " + formatDuration(w.EndTime.Sub(w.StartTime)))
case "check":
w := d.ActiveDay
if w == nil {
panic("No active workday")
}
fmt.Println("Worked time: " + formatDuration(w.getWorkTime()))
fmt.Println("Total Break time: " + formatDuration(w.getBreakTime()))
fmt.Println("End time: " + w.getEstimatedEndTime().Format("15:04:05"))
case "break":
w := d.ActiveDay
if w == nil {
panic("No active workday")
}
w.startBreak(time.Now())
d.writeToFile()
case "back":
w := d.ActiveDay
if w == nil {
panic("No active workday")
}
w.endBreak(time.Now())
d.writeToFile()
default:
flag.Usage()
os.Exit(1)
}
default:
flag.Usage()
os.Exit(1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment