package cron import ( "fmt" "log" "time" ) // Cron handles jobs scheduling and execution type Cron struct { jobs []Job ErrChan chan error DoneChan chan struct{} SuccessChan chan string } // Job is a type that holds the details for each job. type Job struct { Name string Schedule string Action JobFunc } type JobFunc func() error func New() *Cron { return &Cron{ jobs: make([]Job, 0), ErrChan: make(chan error), SuccessChan: make(chan string), DoneChan: make(chan struct{}), } } func (c *Cron) AddJob(job Job) { c.jobs = append(c.jobs, job) } func (c *Cron) Start() { for _, j := range c.jobs { go c.scheduleJob(j) } } func (c *Cron) Stop() { close(c.DoneChan) close(c.SuccessChan) close(c.ErrChan) } // scheduleJob adds a task to the Cron schedule based on its schedule func (c *Cron) scheduleJob(j Job) { for { select { case <-c.DoneChan: log.Printf("stopping job %s", j.Name) return default: now := time.Now() var next time.Time switch j.Schedule { case "minute": next = now.Add(10 * time.Second) case "daily": next = now.AddDate(0, 0, 1).Truncate(24 * time.Hour) case "weekly": next = now.AddDate(0, 0, 7).Truncate(24 * time.Hour) case "monthly": nextMonth := now.AddDate(0, 1, 0) next = time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location()) default: log.Printf("Unknown schedule %q for job %q", j.Schedule, j.Name) return } sleepDuration := time.Until(next) log.Printf("Job %q will run in %s", j.Name, sleepDuration.String()) time.Sleep(sleepDuration) if err := j.Action(); err != nil { c.ErrChan <- fmt.Errorf("job %s failed: %w", j.Name, err) } else { c.SuccessChan <- fmt.Sprintf("job %s completed successfully", j.Name) } } } }