diff --git a/README.md b/README.md index f90e716..e800aa1 100644 --- a/README.md +++ b/README.md @@ -80,3 +80,26 @@ Please see `examples/main.go` for basic usage. http.HandleFunc("/debug/cron", m.Handler) ``` + +## Schedule parser customization + +To use non-default schedule parser, invoke the `NewManagerWithParser()` function: + +```go +// Init some custom parser, +// see https://pkg.go.dev/github.com/robfig/cron/v3#hdr-Alternative_Formats for more info. +m := cron.NewManagerWithParser(cron.WithParseOptions(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)) + +// Or use the predefined one: +m = cron.NewManagerWithParser(cron.ParserWithSeconds) + +// Use a schedule format acceptable by the custom parser. +m.AddFunc("f1", "* * * * * *", func(_ context.Context) error { + // Do some important stuff... + return nil +}) + +if err := m.Run(context.Background()); err != nil { + log.Fatal(err) +} +``` \ No newline at end of file diff --git a/cron.go b/cron.go index 4c9f0c6..6680244 100644 --- a/cron.go +++ b/cron.go @@ -27,6 +27,11 @@ var ( ErrDuplicate = errors.New("duplicate cron name") ) +var ( + // ParserWithSeconds is a schedule parser with the leading seconds support. + ParserWithSeconds = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) +) + type ( contextKey string cronState string @@ -48,6 +53,7 @@ func (ss Schedule) IsActive() bool { return ss != Schedule(stateDisabled) && ss // Manager is a Cron manager with context and middleware support. type Manager struct { cron *cron.Cron + parser *cron.Parser middleware []MiddlewareFunc jobs []job muState sync.Mutex @@ -78,6 +84,14 @@ func NewManager() *Manager { } } +// NewManagerWithParser creates new Manager instance with the custom schedule parser. +func NewManagerWithParser(parser cron.Parser) *Manager { + return &Manager{ + cron: cron.New(cron.WithParser(parser)), + parser: &parser, + } +} + // AddFunc adds func to cron. func (cm *Manager) AddFunc(name string, schedule Schedule, fn Func) { cm.jobs = append(cm.jobs, newJob(name, schedule, fn, false)) @@ -106,7 +120,7 @@ func (cm *Manager) validateJobs() (string, error) { // parse schedule if job.schedule.IsActive() { - _, err := cron.ParseStandard(job.schedule.String()) + _, err := cm.parseSchedule(job.schedule) if err != nil { return job.name, err } @@ -189,6 +203,18 @@ func (cm *Manager) Stop() context.Context { return cm.cron.Stop() } +// parseSchedule parses the given schedule using the registered Parser or with Standard Parser if none. +func (cm *Manager) parseSchedule(s Schedule) (cron.Schedule, error) { + var fn func(s string) (cron.Schedule, error) + if cm.parser != nil { + fn = cm.parser.Parse + } else { + fn = cron.ParseStandard + } + + return fn(s.String()) +} + // updateState set. func (cm *Manager) updateState(idx int, state cronState, err error) { cm.muState.Lock() diff --git a/cron_test.go b/cron_test.go index f4bf0f9..d697a76 100644 --- a/cron_test.go +++ b/cron_test.go @@ -47,6 +47,46 @@ func TestManager_Validate(t *testing.T) { }) } +func TestManager_Options(t *testing.T) { + Convey("Test NewManager with custom parser", t, func() { + m := NewManagerWithParser(ParserWithSeconds) + + Convey("When standard schedule format", func() { + m.AddFunc("f1", "0 0 * * *", newCronFunc("f1")) + + name, err := m.validateJobs() + So(err, ShouldNotBeNil) + So(name, ShouldEqual, "f1") + }) + + Convey("When custom schedule format", func() { + m.AddFunc("f2", "10 * * * * *", newCronFunc("f1")) + + _, err := m.validateJobs() + So(err, ShouldBeNil) + }) + }) + + Convey("Test NewManager with standard parser", t, func() { + m := NewManager() + + Convey("When standard schedule format", func() { + m.AddFunc("f1", "0 0 * * *", newCronFunc("f1")) + + _, err := m.validateJobs() + So(err, ShouldBeNil) + }) + + Convey("When custom schedule format", func() { + m.AddFunc("f2", "10 * * * * *", newCronFunc("f1")) + + name, err := m.validateJobs() + So(err, ShouldNotBeNil) + So(name, ShouldEqual, "f2") + }) + }) +} + func TestManager_Run(t *testing.T) { Convey("Test validate function", t, func() { ctx := t.Context()