summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Schlachter <t480-debian-git@schlachter.ca>2025-11-11 00:24:55 -0500
committerDavid Schlachter <t480-debian-git@schlachter.ca>2025-11-11 00:26:06 -0500
commitb36d98e47cdb4939be2507cd8b72ae5df487bdf2 (patch)
treec6c0aac8372a77bdd26f919d0c3e875ef2b688e0
parent8d47cd773142132e48fab2b04e4b71126d7bb7d5 (diff)
Change how tasks are added and removed
-rw-r--r--README.md4
-rw-r--r--main.go95
-rw-r--r--main_test.go18
3 files changed, 54 insertions, 63 deletions
diff --git a/README.md b/README.md
index 1087f5d..114734d 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,3 @@ At program start, and every minute thereafter:
- if there were any lines that we did not see during this read of the file, but that we saw previously, remove them from the scheduler
The lines will be formatted like a cron file, but instead of a command, we will simply have the name of the task to be created.
-
-## Future work
-
-It turns out jobs can be added AND removed by an ID that is returned from the Add function. It could be interesting to add/remove based on the last seen map (with the job IDs), instead of completely recreating all the jobs if the input file changes.
diff --git a/main.go b/main.go
index bcfca7a..bf58df2 100644
--- a/main.go
+++ b/main.go
@@ -26,6 +26,8 @@ type InputLine struct {
type TaskAddingJob struct {
Schedule cron.Schedule
Task cron.Job
+ EntryID cron.EntryID // this doesn't fit super well, but we'll figure that out soon I guess
+ Delete bool
}
type Task struct {
@@ -39,7 +41,7 @@ func (t Task) Run() {
}
}
-var lastSeenInput = map[InputLine]TaskAddingJob{}
+var jobsFromInput = map[InputLine]TaskAddingJob{}
var todoistToken string
var httpClient = http.Client{
Timeout: 10 * time.Second,
@@ -57,25 +59,36 @@ func main() {
log.Printf("Started watching '%s'", p)
c := cron.New()
+ c.Start()
for {
- input, err := readInput(p)
+ err = readInput(p)
if err != nil {
log.Printf("Error reading input: %s", err)
continue
}
- if inputChanged(lastSeenInput, input) {
- log.Print("Updating task list")
- lastSeenInput = input
- c.Stop()
- c := cron.New()
- addJobs(c, input)
- c.Start()
- log.Print("Done updating task list")
+ for inputLine, taskAddingJob := range jobsFromInput {
+ if jobsFromInput[inputLine].EntryID == 0 {
+ taskAddingJob.EntryID = c.Schedule(
+ jobsFromInput[inputLine].Schedule,
+ jobsFromInput[inputLine].Task,
+ )
+ jobsFromInput[inputLine] = taskAddingJob
+
+ log.Printf(
+ "Added '%s' with recurrence '%s'",
+ inputLine.RawTask,
+ inputLine.RawSchedule,
+ )
+ } else if taskAddingJob.Delete {
+ c.Remove(taskAddingJob.EntryID)
+ delete(jobsFromInput, inputLine)
+ log.Printf("Removed '%s'", inputLine.RawTask)
+ }
}
- time.Sleep(time.Minute) // re-read the input file every minute
+ time.Sleep(time.Minute)
}
}
@@ -108,15 +121,19 @@ func inputFilePath() (string, error) {
var inputFileRe = regexp.MustCompile(`([^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+\s+[^\s]+)\s+(.*)`)
-func readInput(p string) (map[InputLine]TaskAddingJob, error) {
- input := map[InputLine]TaskAddingJob{}
-
+func readInput(p string) error {
f, err := os.Open(p)
if err != nil {
- return nil, fmt.Errorf("opening '%s': %w", p, err)
+ return fmt.Errorf("opening '%s': %w", p, err)
}
defer f.Close()
+ previousInput := map[InputLine]struct{}{}
+ for k := range jobsFromInput {
+ previousInput[k] = struct{}{}
+ }
+ currentInput := map[InputLine]struct{}{}
+
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
@@ -135,38 +152,41 @@ func readInput(p string) (map[InputLine]TaskAddingJob, error) {
RawTask: matches[2],
}
+ currentInput[inputLine] = struct{}{}
+ if _, ok := previousInput[inputLine]; ok {
+ continue
+ }
+
schedule, err := cron.ParseStandard(inputLine.RawSchedule)
if err != nil {
- log.Printf("Failed to add '%s' with recurrence '%s': %s", inputLine.RawTask, inputLine.RawSchedule, err)
+ log.Printf(
+ "Failed to add '%s' with recurrence '%s': %s",
+ inputLine.RawTask,
+ inputLine.RawSchedule,
+ err,
+ )
continue
}
- job := Task{Name: inputLine.RawTask}
- taskAddingJob := TaskAddingJob{
+ jobsFromInput[inputLine] = TaskAddingJob{
Schedule: schedule,
- Task: job,
+ Task: Task{Name: inputLine.RawTask},
}
-
- input[inputLine] = taskAddingJob
}
-
if err := scanner.Err(); err != nil {
- return nil, fmt.Errorf("scanning: %w", err)
+ return fmt.Errorf("scanning: %w", err)
}
- return input, nil
-}
-
-func inputChanged(old, new map[InputLine]TaskAddingJob) bool {
- if len(new) != len(old) {
- return true
- }
- for k := range old {
- if _, ok := new[k]; !ok {
- return true
+ // Mark stuff that we previously saw, but is now missing, for pruning.
+ for k := range previousInput {
+ if _, ok := currentInput[k]; !ok {
+ v := jobsFromInput[k]
+ v.Delete = true
+ jobsFromInput[k] = v
}
}
- return false
+
+ return nil
}
func createTask(task string) error {
@@ -198,10 +218,3 @@ func createTask(task string) error {
log.Printf("Created task '%s'", task)
return nil
}
-
-func addJobs(c *cron.Cron, tasks map[InputLine]TaskAddingJob) {
- for _, task := range tasks {
- _ = c.Schedule(task.Schedule, task.Task)
- }
- log.Printf("Added %d jobs", len(tasks))
-}
diff --git a/main_test.go b/main_test.go
deleted file mode 100644
index 3e79bf9..0000000
--- a/main_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- "testing"
-)
-
-func TestMain(t *testing.T) {
- tasks, err := readInput("testdata/test_tasks.txt")
- if err != nil {
- t.Logf("Expected no error when reading input file, got '%s'", err)
- t.FailNow()
- }
-
- if len(tasks) != 3 {
- t.Logf("Expected 3 tasks to be parsed, got %d", len(tasks))
- t.FailNow()
- }
-}