lens @ 57b41ad766b3c4505672c12f058f10c7a132dd5b

feat: Remove unnecessary function
  1diff --git a/cmd/server/main.go b/cmd/server/main.go
  2index 473bed9c8f84206f027b3c505fb0260e04d5e627..0fa5fea9d42fe4a4c8ea1c67444e034b938f68e9 100644
  3--- a/cmd/server/main.go
  4+++ b/cmd/server/main.go
  5@@ -135,7 +135,6 @@ 	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
  6 	defer stop()
  7 
  8 	pool.Start(ctx)
  9-	pool.Wait()
 10 }
 11 
 12 func OpenDatabase(dbType string, dbConn string) (gorm.Dialector, error) {
 13diff --git a/pkg/coroutine/coroutine.go b/pkg/coroutine/coroutine.go
 14new file mode 100644
 15index 0000000000000000000000000000000000000000..96d149ea9851318b4ff9c0c7a0f07a1a6505eb83
 16--- /dev/null
 17+++ b/pkg/coroutine/coroutine.go
 18@@ -0,0 +1,33 @@
 19+package coroutine
 20+
 21+import (
 22+	"context"
 23+)
 24+
 25+// WrapProcess wraps process into a go routine and make it cancelable through context
 26+func WrapProcess[V any](ctx context.Context, fun func() (V, error)) (V, error) {
 27+	c := make(chan V)
 28+	e := make(chan error)
 29+	go func() {
 30+		defer close(c)
 31+		defer close(e)
 32+
 33+		v, err := fun()
 34+		if err != nil {
 35+			e <- err
 36+		} else {
 37+			c <- v
 38+		}
 39+	}()
 40+
 41+	select {
 42+	case <-ctx.Done():
 43+		var zero V
 44+		return zero, ctx.Err()
 45+	case m := <-c:
 46+		return m, nil
 47+	case err := <-e:
 48+		var zero V
 49+		return zero, err
 50+	}
 51+}
 52diff --git a/pkg/coroutine/coroutine_test.go b/pkg/coroutine/coroutine_test.go
 53new file mode 100644
 54index 0000000000000000000000000000000000000000..e876ec3bd69b256426e2dad9fb6c2f1b17ab7348
 55--- /dev/null
 56+++ b/pkg/coroutine/coroutine_test.go
 57@@ -0,0 +1,63 @@
 58+//go:build unit
 59+
 60+package coroutine
 61+
 62+import (
 63+	"context"
 64+	"errors"
 65+	"sync"
 66+	"testing"
 67+	"time"
 68+
 69+	"git.sr.ht/~gabrielgio/img/pkg/testkit"
 70+)
 71+
 72+var rError = errors.New("This is a error")
 73+
 74+func imediatReturn() (string, error) {
 75+	return "A string", nil
 76+}
 77+
 78+func imediatErrorReturn() (string, error) {
 79+	return "", rError
 80+}
 81+
 82+func haltedReturn() (string, error) {
 83+	time.Sleep(time.Hour)
 84+	return "", nil
 85+}
 86+
 87+func TestImediatReturn(t *testing.T) {
 88+	ctx := context.Background()
 89+	v, err := WrapProcess(ctx, imediatReturn)
 90+	testkit.TestError(t, "WrapProcess", nil, err)
 91+	testkit.TestValue(t, "WrapProcess", "A string", v)
 92+}
 93+
 94+func TestImediatErrorReturn(t *testing.T) {
 95+	ctx := context.Background()
 96+	v, err := WrapProcess(ctx, imediatErrorReturn)
 97+	testkit.TestError(t, "WrapProcess", rError, err)
 98+	testkit.TestValue(t, "WrapProcess", "", v)
 99+}
100+
101+func TestHaltedReturn(t *testing.T) {
102+	ctx := context.Background()
103+	ctx, cancel := context.WithCancel(ctx)
104+
105+	var (
106+		err error
107+		wg  sync.WaitGroup
108+	)
109+
110+	wg.Add(1)
111+	go func(err *error) {
112+		defer wg.Done()
113+		_, *err = WrapProcess(ctx, haltedReturn)
114+	}(&err)
115+
116+	cancel()
117+	wg.Wait()
118+
119+	testkit.TestError(t, "WrapProcess", context.Canceled, err)
120+}
121diff --git a/pkg/coroutines/coroutines.go b/pkg/coroutines/coroutines.go
122deleted file mode 100644
123index c0f7247cdf3d00e2b1fd4e019f5ec334fa32ea96..0000000000000000000000000000000000000000
124--- a/pkg/coroutines/coroutines.go
125+++ /dev/null
126@@ -1 +0,0 @@
127-package coroutines
128diff --git a/pkg/worker/exif_scanner.go b/pkg/worker/exif_scanner.go
129index 4aa247d832bb5261d0373c4fbb8bb8d90c73af02..91eed1270268368fa3f43216e087648984296d15 100644
130--- a/pkg/worker/exif_scanner.go
131+++ b/pkg/worker/exif_scanner.go
132@@ -4,6 +4,7 @@ import (
133 	"context"
134 
135 	"git.sr.ht/~gabrielgio/img/pkg/components/media"
136+	"git.sr.ht/~gabrielgio/img/pkg/coroutine"
137 	"git.sr.ht/~gabrielgio/img/pkg/fileop"
138 )
139 
140@@ -33,36 +34,11 @@
141 	return medias, nil
142 }
143 
144-func wrapReadExif(ctx context.Context, path string) (*media.MediaEXIF, error) {
145-	c := make(chan *media.MediaEXIF)
146-	e := make(chan error)
147-	go func() {
148-		defer close(c)
149-		defer close(e)
150-
151-		newExif, err := fileop.ReadExif(path)
152-		if err != nil {
153-			e <- err
154-		} else {
155-			c <- newExif
156-		}
157-	}()
158-
159-	select {
160-	case <-ctx.Done():
161-		return nil, ctx.Err()
162-	case m := <-c:
163-		return m, nil
164-	case err := <-e:
165-		return nil, err
166-	}
167-}
168-
169 func (e *EXIFScanner) Process(ctx context.Context, m *media.Media) error {
170-	newExif, err := wrapReadExif(ctx, m.Path)
171+	exif, err := coroutine.WrapProcess(ctx, func() (*media.MediaEXIF, error) { return fileop.ReadExif(m.Path) })
172 	if err != nil {
173 		return err
174 	}
175 
176-	return e.repository.CreateEXIF(ctx, m.ID, newExif)
177+	return e.repository.CreateEXIF(ctx, m.ID, exif)
178 }
179diff --git a/pkg/worker/list_processor_test.go b/pkg/worker/list_processor_test.go
180index 1e4ed2d7793632a996cb6b828fd21677dcedbb55..35672f3b1e9b807fde0698ea752a7763777b7cfa 100644
181--- a/pkg/worker/list_processor_test.go
182+++ b/pkg/worker/list_processor_test.go
183@@ -10,6 +10,7 @@ 	"sync"
184 	"testing"
185 
186 	"git.sr.ht/~gabrielgio/img/pkg/testkit"
187+	"github.com/sirupsen/logrus"
188 )
189 
190 type (
191@@ -24,10 +25,13 @@ 	}
192 )
193 
194 func TestListProcessorLimit(t *testing.T) {
195-	mock := &mockCounterListProcessor{
196-		countTo: 10000,
197-	}
198-	worker := NewWorkerFromListProcessor[int](mock, nil)
199+	var (
200+		log       = logrus.New()
201+		scheduler = NewScheduler(1)
202+		mock      = &mockCounterListProcessor{countTo: 10000}
203+	)
204+
205+	worker := NewWorkerFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
206 
207 	err := worker.Start(context.Background())
208 	testkit.TestFatalError(t, "Start", err)
209@@ -36,8 +40,13 @@ 	testkit.TestValue(t, "Start", mock.countTo, mock.counter)
210 }
211 
212 func TestListProcessorContextCancelQuery(t *testing.T) {
213-	mock := &mockContextListProcessor{}
214-	worker := NewWorkerFromListProcessor[int](mock, nil)
215+	var (
216+		log       = logrus.New()
217+		scheduler = NewScheduler(1)
218+		mock      = &mockContextListProcessor{}
219+	)
220+
221+	worker := NewWorkerFromBatchProcessor[int](mock, scheduler, log.WithField("context", "testing"))
222 
223 	ctx, cancel := context.WithCancel(context.Background())
224 	var wg sync.WaitGroup
225diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go
226index 18cc0e25c15a57d778a806275f6fa354b637d8c0..359384a260713696d42fbbf02b6be54bcda9f6a6 100644
227--- a/pkg/worker/worker.go
228+++ b/pkg/worker/worker.go
229@@ -20,7 +20,6 @@ 	}
230 
231 	WorkerPool struct {
232 		workers []*Work
233-		wg      sync.WaitGroup
234 	}
235 )
236 
237@@ -36,10 +35,13 @@ 	})
238 }
239 
240 func (self *WorkerPool) Start(ctx context.Context) {
241-	self.wg.Add(len(self.workers))
242+	var wg sync.WaitGroup
243+
244+	wg.Add(len(self.workers))
245+
246 	for _, w := range self.workers {
247 		go func(w *Work) {
248-			defer self.wg.Done()
249+			defer wg.Done()
250 			if err := w.Worker.Start(ctx); err != nil && !errors.Is(err, context.Canceled) {
251 				fmt.Println("Processes finished, error", w.Name, err.Error())
252 			} else {
253@@ -47,8 +49,6 @@ 				fmt.Println(w.Name, "done")
254 			}
255 		}(w)
256 	}
257-}
258 
259-func (self *WorkerPool) Wait() {
260-	self.wg.Wait()
261+	wg.Wait()
262 }