blob: 69ddd17143ff23b0edb114f9a4894c4638fd24ea [file] [log] [blame]
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +04001package tasks
2
gioe72b54f2024-04-22 10:44:41 +04003import (
4 "fmt"
5)
6
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +04007type Status int
8
9const (
10 StatusPending Status = 0
11 StatusRunning = 1
12 StatusFailed = 2
13 StatusDone = 3
14)
15
16type TaskDoneListener func(err error)
17
gio1cd65152024-08-16 08:18:49 +040018type Subtasks interface {
19 Tasks() []Task
20}
21
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040022type Task interface {
23 Title() string
24 Start()
25 Status() Status
26 Err() error
27 Subtasks() []Task
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040028 OnDone(l TaskDoneListener)
29}
30
31type basicTask struct {
Giorgi Lekveishviliab7ff6e2024-03-29 13:11:30 +040032 title string
33 status Status
34 err error
35 listeners []TaskDoneListener
36 beforeStart func()
37 afterDone func()
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040038}
39
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040040func newBasicTask(title string) basicTask {
41 return basicTask{
42 title: title,
43 status: StatusPending,
44 err: nil,
45 listeners: make([]TaskDoneListener, 0),
46 }
47}
48
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040049func (b *basicTask) Title() string {
50 return b.title
51}
52
53func (b *basicTask) Status() Status {
54 return b.status
55}
56
57func (b *basicTask) Err() error {
58 return b.err
59}
60
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040061func (b *basicTask) OnDone(l TaskDoneListener) {
62 b.listeners = append(b.listeners, l)
63}
64
65func (b *basicTask) callDoneListeners(err error) {
gioe72b54f2024-04-22 10:44:41 +040066 if err != nil {
67 fmt.Printf("%s %s\n", b.title, err.Error())
68 }
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040069 for _, l := range b.listeners {
70 go l(err)
71 }
72 if err == nil {
73 b.status = StatusDone
74 } else {
75 b.status = StatusFailed
76 b.err = err
77 }
78}
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040079
80type leafTask struct {
81 basicTask
82 start func() error
83}
84
85func newLeafTask(title string, start func() error) leafTask {
86 return leafTask{
87 basicTask: newBasicTask(title),
88 start: start,
89 }
90}
91
92func (b *leafTask) Subtasks() []Task {
93 return make([]Task, 0)
94}
95
96func (b *leafTask) Start() {
Giorgi Lekveishviliab7ff6e2024-03-29 13:11:30 +040097 b.status = StatusRunning
98 if b.beforeStart != nil {
99 b.beforeStart()
100 }
101 err := b.start()
102 defer b.callDoneListeners(err)
103 if b.afterDone != nil {
104 b.afterDone()
105 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400106}
107
108type parentTask struct {
109 leafTask
gio1cd65152024-08-16 08:18:49 +0400110 subtasks Subtasks
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400111 showChildren bool
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400112}
113
gio1cd65152024-08-16 08:18:49 +0400114type TaskSlice []Task
115
116func (s TaskSlice) Tasks() []Task {
117 return s
118}
119
120func newParentTask(title string, showChildren bool, start func() error, subtasks Subtasks) parentTask {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400121 return parentTask{
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400122 leafTask: newLeafTask(title, start),
123 subtasks: subtasks,
124 showChildren: showChildren,
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400125 }
126}
127
128func (t *parentTask) Subtasks() []Task {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400129 if t.showChildren {
gio1cd65152024-08-16 08:18:49 +0400130 return t.subtasks.Tasks()
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400131 } else {
132 return make([]Task, 0)
133 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400134}
135
gio1cd65152024-08-16 08:18:49 +0400136func newSequentialParentTask(title string, showChildren bool, subtasks ...Task) *parentTask {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400137 start := func() error {
138 errCh := make(chan error)
139 for i := range subtasks[:len(subtasks)-1] {
140 next := i + 1
141 subtasks[i].OnDone(func(err error) {
142 if err == nil {
143 go subtasks[next].Start()
144 } else {
145 errCh <- err
146 }
147 })
148 }
149 subtasks[len(subtasks)-1].OnDone(func(err error) {
150 errCh <- err
151 })
152 go subtasks[0].Start()
153 return <-errCh
154 }
gio1cd65152024-08-16 08:18:49 +0400155 t := newParentTask(title, showChildren, start, TaskSlice(subtasks))
156 return &t
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400157}
158
gio1cd65152024-08-16 08:18:49 +0400159func newConcurrentParentTask(title string, showChildren bool, subtasks ...Task) *parentTask {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400160 start := func() error {
161 errCh := make(chan error)
162 for i := range subtasks {
163 subtasks[i].OnDone(func(err error) {
164 errCh <- err
165 })
166 go subtasks[i].Start()
167 }
Giorgi Lekveishvili0a0ca0e2024-04-01 09:07:41 +0400168 cnt := 0
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400169 for _ = range subtasks {
170 err := <-errCh
171 if err != nil {
172 return err
173 }
Giorgi Lekveishvili0a0ca0e2024-04-01 09:07:41 +0400174 cnt++
175 if cnt == len(subtasks) {
176 break
177 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400178 }
179 return nil
180 }
gio1cd65152024-08-16 08:18:49 +0400181 t := newParentTask(title, showChildren, start, TaskSlice(subtasks))
182 return &t
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400183}