-
Notifications
You must be signed in to change notification settings - Fork 0
/
options.go
118 lines (103 loc) · 2.94 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package smartpoll
import (
"context"
"errors"
"fmt"
"reflect"
)
type (
Option interface {
applyOption(c *schedulerConfig) error
}
optionFunc func(c *schedulerConfig) error
schedulerConfig struct {
tasks map[any]*taskState // see Scheduler.tasks
hooks []func(ctx context.Context, internal *Internal, value reflect.Value, ok bool) error // see Scheduler.hooks
cases []reflect.SelectCase // see Scheduler.cases
runHooks []RunHook // see Scheduler.runHooks
}
)
var (
_ Option = optionFunc(nil)
)
// New initialises a [Scheduler], with the given options.
// See also `With*` prefixed functions.
func New(options ...Option) (*Scheduler, error) {
c := schedulerConfig{
tasks: make(map[any]*taskState),
}
for _, option := range options {
if err := option.applyOption(&c); err != nil {
return nil, err
}
}
if len(c.tasks) == 0 {
return nil, errors.New(`smartpoll: no tasks configured`)
}
x := Scheduler{
tasks: c.tasks,
hooks: c.hooks,
cases: c.cases,
taskLockCh: make(chan error),
taskUnlockCh: make(chan error),
taskCompleteCh: make(chan struct{}, 1),
runHooks: c.runHooks,
}
if err := x.initCases(); err != nil {
return nil, err
}
return &x, nil
}
// WithTask adds a task, identified by the given key. The provided task may
// be scheduled.
func WithTask(key any, task Task) Option {
return optionFunc(func(c *schedulerConfig) (err error) {
if task == nil {
return errors.New(`smartpoll: task func must not be nil`)
}
var success bool
defer func() {
if !success {
recover()
err = fmt.Errorf(`smartpoll: task key type %T is not comparable`, key)
}
}()
c.tasks[key] = &taskState{
task: task,
}
success = true
return nil
})
}
// WithHook adds a [Hook], wired up to the given channel.
func WithHook[T any](ch <-chan T, hook Hook[T]) Option {
return optionFunc(func(c *schedulerConfig) error {
if ch == nil {
return errors.New(`smartpoll: hook channel must not be nil`)
}
if hook == nil {
return errors.New(`smartpoll: hook func must not be nil`)
}
c.cases = append(c.cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
})
c.hooks = append(c.hooks, hook.call)
return nil
})
}
// WithRunHook adds a [RunHook] to be called on each [Scheduler.Run], just
// prior to starting the main loop. If more than one [RunHook] is configured,
// they will be called in the order they were configured.
func WithRunHook(hook RunHook) Option {
return optionFunc(func(c *schedulerConfig) error {
if hook == nil {
return errors.New(`smartpoll: run hook must not be nil`)
}
c.runHooks = append(c.runHooks, hook)
return nil
})
}
func (x optionFunc) applyOption(c *schedulerConfig) error {
return x(c)
}