package main import ( "encoding/json" "fmt" "os" "os/signal" "os/user" "path/filepath" "syscall" "time" ) type Stopwatches map[string]time.Time var configPath string func main() { args := os.Args[1:] if len(args) == 0 { // Quick stop watch stopwatchUI(time.Now()) } else if len(args) == 1 && (args[0] == "help" || args[0] == "-h" || args[0] == "--help") { printHelp() } else if len(args) == 1 { namedStopwatch(args[0]) } else if len(args) == 2 && (args[0] == "clear" || args[0] == "-c" || args[0] == "--clear") { clearStopwatch(args[1]) } else if len(args) == 2 && (args[0] == "timer" || args[0] == "-t" || args[0] == "--timer") { d, err := time.ParseDuration(args[1]) if err != nil { fmt.Println("Error parsing duration:", err) } timerUI(time.Now().Add(d)) } } func namedStopwatch(name string) { stopwatches, err := loadStopwatches() if err != nil { fmt.Println("Error loading stopwatches:", err) return } startTime, exists := stopwatches[name] if !exists { stopwatches[name] = time.Now() saveStopwatches(stopwatches) } else { stopwatchUI(startTime) } } func clearStopwatch(name string) { stopwatches, err := loadStopwatches() if err != nil { fmt.Println("Error loading stopwatches:", err) return } if _, exists := stopwatches[name]; exists { delete(stopwatches, name) err := saveStopwatches(stopwatches) if err != nil { fmt.Println("Error saving stopwatches:", err) return } } else { fmt.Printf("Stopwatch '%s' does not exist.\n", name) } } func init() { usr, err := user.Current() if err != nil { fmt.Println("Error getting user home directory:", err) os.Exit(1) } configDir := filepath.Join(usr.HomeDir, ".config") configPath = filepath.Join(configDir, ".stopwatches") if _, err := os.Stat(configDir); os.IsNotExist(err) { os.MkdirAll(configDir, 0755) } } func loadStopwatches() (Stopwatches, error) { stopwatches := Stopwatches{} if _, err := os.Stat(configPath); err == nil { data, err := os.ReadFile(configPath) if err != nil { return stopwatches, err } err = json.Unmarshal(data, &stopwatches) if err != nil { return stopwatches, err } } return stopwatches, nil } func saveStopwatches(stopwatches Stopwatches) error { data, err := json.MarshalIndent(stopwatches, "", " ") if err != nil { return err } return os.WriteFile(configPath, data, 0644) } func stopwatchUI(startTime time.Time) { stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) go func() { <-stop fmt.Println() os.Exit(0) }() for { elapsed := time.Since(startTime) fmt.Printf("\r%02dd %02dh %02dm %02ds %03dms", elapsed/(24*time.Hour), (elapsed%(24*time.Hour))/time.Hour, (elapsed%time.Hour)/time.Minute, (elapsed%time.Minute)/time.Second, (elapsed%time.Second)/time.Millisecond) time.Sleep(10 * time.Millisecond) } } func timerUI(startTime time.Time) { stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) go func() { <-stop fmt.Println() os.Exit(0) }() for { elapsed := time.Until(startTime) fmt.Printf("\r%02dd %02dh %02dm %02ds %03dms", elapsed/(24*time.Hour), (elapsed%(24*time.Hour))/time.Hour, (elapsed%time.Hour)/time.Minute, (elapsed%time.Minute)/time.Second, (elapsed%time.Second)/time.Millisecond) if elapsed.Milliseconds() == 0 { fmt.Println() break } time.Sleep(1 * time.Millisecond) } } func printHelp() { fmt.Println("Usage:") fmt.Println(" stopwatch - Start a quick stopwatch") fmt.Println(" stopwatch [name] - Start or view a named stopwatch") fmt.Println(" stopwatch -t [time] - Start a quick timer") fmt.Println(" stopwatch -c [name] - Remove a named stopwatch") fmt.Println(" stopwatch -h - View help menu") }