package flaky
import (
)
var (
ErrNoNextSchedule = errors.New("flaky: no next scheduled time")
ErrScheduleDeadline = errors.New("flaky: scheduled time exceeds context deadline")
)
type Schedule interface {
Next(now time.Time) time.Time
}
type untilSchedule struct {
sched Schedule
until time.Time
}
func ( Schedule, time.Time) Schedule {
return &untilSchedule{
sched: ,
until: ,
}
}
func ( *untilSchedule) ( time.Time) time.Time {
:= .sched.Next()
if .until.Before() {
return time.Time{}
}
return
}
type midnightSchedule struct {
d time.Duration
loc *time.Location
}
func ( time.Duration, *time.Location) Schedule {
if == nil {
= time.UTC
}
return &midnightSchedule{, }
}
func ( *midnightSchedule) ( time.Time) time.Time {
if .d < 0 {
return
}
:= .Location()
= .In(.loc)
:= time.Date(.Year(), .Month(), .Day(), 0, 0, 0, 0, .Location())
:= .Add(.d)
if .Before() {
= .Add(24 * time.Hour)
}
return .In()
}
type sleepSchedule struct {
d time.Duration
}
func ( time.Duration) Schedule {
return sleepSchedule{}
}
func ( sleepSchedule) ( time.Time) time.Time {
if .d < 0 {
return
}
return .Add(.d)
}
type ScheduleExecutor struct {
clock *clock.Clock
sched Schedule
exec Executor
}
func ( Executor, Schedule) *ScheduleExecutor {
return &ScheduleExecutor{
clock: clock.System(),
sched: ,
exec: ,
}
}
func ( *ScheduleExecutor) ( *clock.Clock) *ScheduleExecutor {
if == nil {
= clock.System()
}
return &ScheduleExecutor{
clock: ,
sched: .sched,
exec: .exec,
}
}
func ( *ScheduleExecutor) ( context.Context, Op) error {
:= .clock.Now()
:= .sched.Next()
if .Equal() {
return .exec.Execute(, )
}
if .Before() {
return ErrNoNextSchedule
}
:= .Sub()
if !withinDeadline(, ) {
return ErrScheduleDeadline
}
:= .clock.Timer()
defer .Stop()
select {
case <-.Done():
return .Err()
case <-.C():
}
return .exec.Execute(, )
}