package main import ( "bufio" "fmt" "log" "os" "path/filepath" "regexp" "strings" "time" "github.com/robfig/cron/v3" ) type inputLine struct { Schedule string Task string } var lastSeenInput = map[inputLine]struct{}{} func main() { p, err := inputFilePath() if err != nil { log.Fatalf("getting input file: %s", err) } c := cron.New() for { input, err := readInput(p) if err != nil { log.Printf("reading input: %s", err) } if inputChanged(lastSeenInput, input) { lastSeenInput = input c.Stop() c := cron.New() addJobs(c, input) c.Start() } time.Sleep(time.Minute) // re-read the input file every minute } } func inputFilePath() (string, error) { args := os.Args if len(args) != 2 { return "", fmt.Errorf("expected one argument (input file path), got %d", len(args)-1) } p := filepath.Clean(args[1]) // While we're here, check that the input path actually exists and // is stat-able. if _, err := os.Stat(p); err != nil { return "", fmt.Errorf("cannot stat input file: %w", err) } return p, nil } var inputFileRe = regexp.MustCompile(`([^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+)\s+(.*)`) func readInput(p string) (map[inputLine]struct{}, error) { input := map[inputLine]struct{}{} f, err := os.Open(p) if err != nil { return nil, fmt.Errorf("opening '%s': %w", p, err) } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } matches := inputFileRe.FindStringSubmatch(line) if matches == nil || len(matches) != 1+2 { log.Printf("failed to parse input line: '%s'", line) } input[inputLine{Schedule: matches[1], Task: matches[2]}] = struct{}{} } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("scanning: %w", err) } return input, nil } func inputChanged(old, new map[inputLine]struct{}) bool { if len(new) != len(old) { return true } for k := range old { if _, ok := new[k]; !ok { return true } } return false } func createTask(task string) {} func addJobs(c *cron.Cron, tasks map[inputLine]struct{}) { for task := range tasks { c.AddFunc(task.Schedule, func() { createTask(task.Task) }) } }