Skip to content

Commit

Permalink
wrap errors
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnRoesler committed Feb 28, 2021
1 parent 2a8757a commit f517e8f
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 405 deletions.
2 changes: 1 addition & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func ExampleJob_Err() {
j := s.Jobs()[0]
fmt.Println(j.Err())
// Output:
// time format error
// the given time format is not supported
}

func ExampleJob_LastRun() {
Expand Down
29 changes: 21 additions & 8 deletions gocron.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package gocron

import (
"errors"
"fmt"
"reflect"
"regexp"
"runtime"
Expand All @@ -16,16 +17,28 @@ import (

// Error declarations for gocron related errors
var (
ErrTimeFormat = errors.New("time format error")
ErrNotAFunction = errors.New("only functions can be schedule into the job queue")
ErrNotScheduledWeekday = errors.New("job not scheduled weekly on a weekday")
ErrJobNotFoundWithTag = errors.New("no jobs found with given tag")
ErrUnsupportedTimeFormat = errors.New("the given time format is not supported")
ErrInvalidInterval = errors.New(".Every() interval must be greater than 0")
ErrInvalidIntervalType = errors.New(".Every() interval must be int, time.Duration, or string")
ErrInvalidSelection = errors.New("an .Every() duration interval cannot be used with units (e.g. .Seconds())")
ErrNotAFunction = errors.New("only functions can be schedule into the job queue")
ErrNotScheduledWeekday = errors.New("job not scheduled weekly on a weekday")
ErrJobNotFoundWithTag = errors.New("no jobs found with given tag")
ErrUnsupportedTimeFormat = errors.New("the given time format is not supported")
ErrInvalidInterval = errors.New(".Every() interval must be greater than 0")
ErrInvalidIntervalType = errors.New(".Every() interval must be int, time.Duration, or string")
ErrInvalidIntervalUnitsSelection = errors.New("an .Every() duration interval cannot be used with units (e.g. .Seconds())")

ErrAtTimeNotSupported = errors.New("the At() not supported for time unit")
ErrWeekdayNotSupported = errors.New("weekday is not supported for time unit")
)

func wrapOrError(toWrap error, err error) error {
var returnErr error
if toWrap != nil {
returnErr = fmt.Errorf("%s: %w", err, toWrap)
} else {
returnErr = err
}
return returnErr
}

// regex patterns for supported time formats
var (
timeWithSeconds = regexp.MustCompile(`(?m)^\d{1,2}:\d\d:\d\d$`)
Expand Down
13 changes: 11 additions & 2 deletions job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestTags(t *testing.T) {
Expand All @@ -21,8 +22,16 @@ func TestTags(t *testing.T) {
}

func TestGetScheduledTime(t *testing.T) {
j, _ := NewScheduler(time.UTC).Every(1).Minute().At("10:30").Do(task)
assert.Equal(t, "10:30", j.ScheduledAtTime())
t.Run("valid", func(t *testing.T) {
j, err := NewScheduler(time.UTC).Every(1).Day().At("10:30").Do(task)
require.NoError(t, err)
assert.Equal(t, "10:30", j.ScheduledAtTime())
})
t.Run("invalid", func(t *testing.T) {
j, err := NewScheduler(time.UTC).Every(1).Minute().At("10:30").Do(task)
assert.EqualError(t, err, ErrAtTimeNotSupported.Error())
assert.Nil(t, j)
})
}

func TestGetWeekday(t *testing.T) {
Expand Down
83 changes: 46 additions & 37 deletions scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ func (s *Scheduler) start() {
}

func (s *Scheduler) runJobs(jobs []*Job) {
for _, j := range jobs {
s.scheduleNextRun(j)
for _, job := range jobs {
s.scheduleNextRun(job)
}
}

Expand Down Expand Up @@ -109,10 +109,10 @@ func (s *Scheduler) Len() int {

// Swap places each job into the other job's position given
// the provided job indexes.
func (s *Scheduler) Swap(i, j int) {
func (s *Scheduler) Swap(i, job int) {
s.jobsMutex.Lock()
defer s.jobsMutex.Unlock()
s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i]
s.jobs[i], s.jobs[job] = s.jobs[job], s.jobs[i]
}

// Less compares the next run of jobs based on their index.
Expand Down Expand Up @@ -201,7 +201,7 @@ func (s *Scheduler) durationToNextRun(lastRun time.Time, job *Job) time.Duration
func (s *Scheduler) calculateMonths(job *Job, lastRun time.Time) time.Duration {
lastRunRoundedMidnight := s.roundToMidnight(lastRun)

if job.dayOfTheMonth > 0 { // calculate days to j.dayOfTheMonth
if job.dayOfTheMonth > 0 { // calculate days to job.dayOfTheMonth
jobDay := time.Date(lastRun.Year(), lastRun.Month(), job.dayOfTheMonth, 0, 0, 0, 0, s.Location()).Add(job.getAtTime())
daysDifference := int(math.Abs(lastRun.Sub(jobDay).Hours()) / 24)
nextRun := s.roundToMidnight(lastRun).Add(job.getAtTime())
Expand Down Expand Up @@ -326,7 +326,7 @@ func (s *Scheduler) Every(interval interface{}) *Scheduler {
case int:
job := NewJob(interval)
if interval <= 0 {
job.err = ErrInvalidInterval
job.err = wrapOrError(job.err, ErrInvalidInterval)
}
s.setJobs(append(s.Jobs(), job))
case time.Duration:
Expand All @@ -338,14 +338,14 @@ func (s *Scheduler) Every(interval interface{}) *Scheduler {
job := NewJob(0)
d, err := time.ParseDuration(interval)
if err != nil {
job.err = err
job.err = wrapOrError(job.err, err)
}
job.duration = d
job.unit = duration
s.setJobs(append(s.Jobs(), job))
default:
job := NewJob(0)
job.err = ErrInvalidIntervalType
job.err = wrapOrError(job.err, ErrInvalidIntervalType)
s.setJobs(append(s.Jobs(), job))
}
return s
Expand Down Expand Up @@ -376,25 +376,25 @@ func (s *Scheduler) RunAllWithDelay(d time.Duration) {
}
}

// Remove specific Job j by function
// Remove specific Job job by function
//
// Removing a job stops that job's timer. However, if a job has already
// been started by by the job's timer before being removed, there is no way to stop
// it through gocron as https://pkg.go.dev/time#Timer.Stop explains.
// The job function would need to have implemented a means of
// stopping, e.g. using a context.WithCancel().
func (s *Scheduler) Remove(j interface{}) {
func (s *Scheduler) Remove(job interface{}) {
s.removeByCondition(func(someJob *Job) bool {
return someJob.name == getFunctionName(j)
return someJob.name == getFunctionName(job)
})
}

// RemoveByReference removes specific Job j by reference
func (s *Scheduler) RemoveByReference(j *Job) {
// RemoveByReference removes specific Job job by reference
func (s *Scheduler) RemoveByReference(job *Job) {
s.removeByCondition(func(someJob *Job) bool {
j.RLock()
defer j.RUnlock()
return someJob == j
job.RLock()
defer job.RUnlock()
return someJob == job
})
}

Expand All @@ -413,14 +413,14 @@ func (s *Scheduler) removeByCondition(shouldRemove func(*Job) bool) {

// RemoveByTag will remove a job by a given tag.
func (s *Scheduler) RemoveByTag(tag string) error {
jobindex, err := s.findJobsIndexByTag(tag)
index, err := s.findJobsIndexByTag(tag)
if err != nil {
return err
}
// Remove job if job index is valid
s.jobs[jobindex].stopTimer()
s.jobs[jobindex].cancel()
s.setJobs(removeAtIndex(s.jobs, jobindex))
s.jobs[index].stopTimer()
s.jobs[index].cancel()
s.setJobs(removeAtIndex(s.jobs, index))
return nil
}

Expand Down Expand Up @@ -477,11 +477,11 @@ func (s *Scheduler) TaskPresent(j interface{}) bool {
return false
}

func (s *Scheduler) jobPresent(j *Job) bool {
func (s *Scheduler) jobPresent(job *Job) bool {
s.jobsMutex.RLock()
defer s.jobsMutex.RUnlock()
for _, job := range s.Jobs() {
if job == j {
if job == job {
return true
}
}
Expand All @@ -507,45 +507,54 @@ func (s *Scheduler) stop() {

// Do specifies the jobFunc that should be called every time the Job runs
func (s *Scheduler) Do(jobFun interface{}, params ...interface{}) (*Job, error) {
j := s.getCurrentJob()
if j.err != nil {
job := s.getCurrentJob()

if job.atTime != 0 && job.unit <= hours {
job.err = wrapOrError(job.err, ErrAtTimeNotSupported)
}

if job.scheduledWeekday != nil && job.unit != weeks {
job.err = wrapOrError(job.err, ErrWeekdayNotSupported)
}

if job.err != nil {
// delete the job from the scheduler as this job
// cannot be executed
s.RemoveByReference(j)
return nil, j.err
s.RemoveByReference(job)
return nil, job.err
}

typ := reflect.TypeOf(jobFun)
if typ.Kind() != reflect.Func {
// delete the job for the same reason as above
s.RemoveByReference(j)
s.RemoveByReference(job)
return nil, ErrNotAFunction
}

fname := getFunctionName(jobFun)
j.functions[fname] = jobFun
j.params[fname] = params
j.name = fname
job.functions[fname] = jobFun
job.params[fname] = params
job.name = fname

// we should not schedule if not running since we cant foresee how long it will take for the scheduler to start
if s.IsRunning() {
s.scheduleNextRun(j)
s.scheduleNextRun(job)
}

return j, nil
return job, nil
}

// At schedules the Job at a specific time of day in the form "HH:MM:SS" or "HH:MM"
func (s *Scheduler) At(t string) *Scheduler {
j := s.getCurrentJob()
job := s.getCurrentJob()
hour, min, sec, err := parseTime(t)
if err != nil {
j.err = ErrTimeFormat
job.err = wrapOrError(job.err, err)
return s
}
// save atTime start as duration from midnight
j.setAtTime(time.Duration(hour)*time.Hour + time.Duration(min)*time.Minute + time.Duration(sec)*time.Second)
j.startsImmediately = false
job.setAtTime(time.Duration(hour)*time.Hour + time.Duration(min)*time.Minute + time.Duration(sec)*time.Second)
job.startsImmediately = false
return s
}

Expand All @@ -569,7 +578,7 @@ func (s *Scheduler) StartAt(t time.Time) *Scheduler {
func (s *Scheduler) setUnit(unit timeUnit) {
job := s.getCurrentJob()
if job.unit == duration {
job.err = ErrInvalidSelection
job.err = wrapOrError(job.err, ErrInvalidIntervalUnitsSelection)
}
job.unit = unit
}
Expand Down
Loading

0 comments on commit f517e8f

Please sign in to comment.