-// Schedule is one schedule: its ID, the task for the agent, the cron expression
-// of a repeating schedule, empty for a one-time schedule, and its next time.
-type Schedule struct {
- ID int `json:"id"`
- Text string `json:"text"`
- Cron string `json:"cron"`
- Due time.Time `json:"due"`
+// List returns the answer of the tool: one line for each schedule, by due time,
+// with its ID, its next time, its cron expression or once, and its text on one
+// line.
+func (s *Schedules) List() string {
+ location := s.clock().Location()
+ schedules := s.sorted()
+ if len(schedules) == 0 {
+ return "No schedules."
+ }
+
+ lines := make([]string, 0, len(schedules))
+ for _, schedule := range schedules {
+ lines = append(lines, strconv.Itoa(schedule.ID)+" | "+schedule.Due.In(location).Format(scheduleTimeLayout)+" | "+cmp.Or(schedule.Cron, "once")+" | "+strings.Join(strings.Fields(schedule.Text), " "))
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+// Len returns the number of schedules.
+func (s *Schedules) Len() int {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ return len(s.file.Schedules)
+}
+
+// Remove removes the schedule id and returns the answer of the tool.
+func (s *Schedules) Remove(id int) (string, error) {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ index := slices.IndexFunc(s.file.Schedules, func(schedule Schedule) bool {
+ return schedule.ID == id
+ })
+
+ if index < 0 {
+ return "", fmt.Errorf("no schedule %d", id)
+ }
+
+ s.file.Schedules = slices.Delete(s.file.Schedules, index, index+1)
+ err := s.save()
+ if err != nil {
+ return "", fmt.Errorf("save removed schedule: %w", err)
+ }
+
+ s.change()
+
+ return fmt.Sprintf("Schedule %d removed.", id), nil