60 lines
1.9 KiB
Go
60 lines
1.9 KiB
Go
// Package schedule wraps `pkg/cronutil` with the force-next-turn skip
|
|
// rule used by Game Master's scheduler.
|
|
//
|
|
// The wrapper is pure: callers pass the current `skip_next_tick` flag
|
|
// and the wrapper returns both the next firing time and a boolean that
|
|
// reports whether the flag was consumed. The runtime-record store is
|
|
// responsible for persisting the cleared flag; this package never
|
|
// touches it.
|
|
//
|
|
// `gamemaster/README.md §Force-next-turn` describes the rule:
|
|
//
|
|
// If `skip_next_tick=true`, advance by one extra cron step and clear
|
|
// the flag.
|
|
package schedule
|
|
|
|
import (
|
|
"time"
|
|
|
|
"galaxy/cronutil"
|
|
)
|
|
|
|
// Schedule wraps `cronutil.Schedule` with the GM-specific
|
|
// skip-next-tick semantics. The zero value is not usable; callers
|
|
// obtain a Schedule from Parse.
|
|
type Schedule struct {
|
|
inner cronutil.Schedule
|
|
}
|
|
|
|
// Parse parses expr as a five-field cron expression and returns the
|
|
// resulting Schedule. Parse returns an error if expr is rejected by the
|
|
// underlying cronutil parser.
|
|
func Parse(expr string) (Schedule, error) {
|
|
inner, err := cronutil.Parse(expr)
|
|
if err != nil {
|
|
return Schedule{}, err
|
|
}
|
|
return Schedule{inner: inner}, nil
|
|
}
|
|
|
|
// Next returns the next firing time strictly after `after`, honouring
|
|
// the skip flag.
|
|
//
|
|
// When `skip` is false, Next returns `cronutil.Schedule.Next(after)`
|
|
// and reports `skipConsumed=false`.
|
|
//
|
|
// When `skip` is true, Next computes the cron step immediately after
|
|
// `after`, then advances by one further cron step and returns that
|
|
// time with `skipConsumed=true`. The caller is responsible for
|
|
// persisting the cleared flag after observing `skipConsumed`.
|
|
//
|
|
// All returned times are in UTC; cronutil.Schedule already enforces
|
|
// UTC normalisation on its inputs and outputs.
|
|
func (s Schedule) Next(after time.Time, skip bool) (time.Time, bool) {
|
|
first := s.inner.Next(after)
|
|
if !skip {
|
|
return first, false
|
|
}
|
|
return s.inner.Next(first), true
|
|
}
|