cerrado @ c06945c189c1d8ef3cedeb51e416ba0fec36368f

  1diff --git a/pkg/config/config.go b/pkg/config/config.go
  2index ba1614f6fa318316db0b44a319846bf4a275bba6..776c69888056005fa086fd14509354449665baa4 100644
  3--- a/pkg/config/config.go
  4+++ b/pkg/config/config.go
  5@@ -5,6 +5,7 @@ 	"fmt"
  6 	"io"
  7 	"strconv"
  8 
  9+	"git.gabrielgio.me/cerrado/pkg/u"
 10 	"git.sr.ht/~emersion/go-scfg"
 11 )
 12 
 13@@ -56,9 +57,9 @@ 	return setBool(public, &scan.Public)
 14 }
 15 
 16 func setBool(dir *scfg.Directive, field *bool) error {
 17-
 18 	if dir != nil {
 19-		p1 := first(dir.Params)
 20+
 21+		p1, _ := u.First(dir.Params)
 22 		v, err := strconv.ParseBool(p1)
 23 		if err != nil {
 24 			return fmt.Errorf("Error parsing bool param of %s: %w", dir.Name, err)
 25@@ -70,15 +71,7 @@ }
 26 
 27 func setString(dir *scfg.Directive, field *string) error {
 28 	if dir != nil {
 29-		*field = first(dir.Params)
 30+		*field, _ = u.First(dir.Params)
 31 	}
 32 	return nil
 33 }
 34-
 35-func first[T any](v []T) T {
 36-	if len(v) == 0 {
 37-		var zero T
 38-		return zero
 39-	}
 40-	return v[0]
 41-}
 42diff --git a/pkg/u/util.go b/pkg/u/util.go
 43new file mode 100644
 44index 0000000000000000000000000000000000000000..34eafd11492cecb0eddfe8cc7ad4c4df8bf96511
 45--- /dev/null
 46+++ b/pkg/u/util.go
 47@@ -0,0 +1,17 @@
 48+package u
 49+
 50+func First[T any](v []T) (T, bool) {
 51+	if len(v) == 0 {
 52+		var zero T
 53+		return zero, false
 54+	}
 55+	return v[0], true
 56+}
 57+
 58+func ChunkBy[T any](items []T, chunkSize int) [][]T {
 59+	var chunks = make([][]T, 0, (len(items)/chunkSize)+1)
 60+	for chunkSize < len(items) {
 61+		items, chunks = items[chunkSize:], append(chunks, items[0:chunkSize:chunkSize])
 62+	}
 63+	return append(chunks, items)
 64+}
 65diff --git a/pkg/u/util_test.go b/pkg/u/util_test.go
 66new file mode 100644
 67index 0000000000000000000000000000000000000000..a6d84c7eaa6ceb401da5f22978f0f285ea1b7478
 68--- /dev/null
 69+++ b/pkg/u/util_test.go
 70@@ -0,0 +1,96 @@
 71+// go:build unit
 72+
 73+package u
 74+
 75+import (
 76+	"testing"
 77+
 78+	"github.com/google/go-cmp/cmp"
 79+)
 80+
 81+func TestFirst(t *testing.T) {
 82+	testCases := []struct {
 83+		name  string
 84+		slice []int
 85+		first int
 86+		exist bool
 87+	}{
 88+		{
 89+			name:  "multiple items slice",
 90+			slice: []int{1, 2, 3},
 91+			first: 1,
 92+			exist: true,
 93+		},
 94+		{
 95+			name:  "single item slice",
 96+			slice: []int{1},
 97+			first: 1,
 98+			exist: true,
 99+		},
100+		{
101+			name:  "empty slice",
102+			slice: []int{},
103+			first: 0,
104+			exist: false,
105+		},
106+	}
107+	for _, tc := range testCases {
108+		t.Run(tc.name, func(t *testing.T) {
109+
110+			first, empty := First(tc.slice)
111+
112+			if first != tc.first {
113+				t.Errorf("Error first, want %d got %d", tc.first, first)
114+			}
115+
116+			if empty != tc.exist {
117+				t.Errorf("Error empty, want %t got %t", tc.exist, empty)
118+			}
119+
120+		})
121+	}
122+}
123+
124+func TestSubList(t *testing.T) {
125+	testCases := []struct {
126+		name  string
127+		slice []int
128+		size  int
129+		want  [][]int
130+	}{
131+		{
132+			name:  "sigle size sub list",
133+			slice: []int{1, 2, 3},
134+			size:  1,
135+			want:  [][]int{{1}, {2}, {3}},
136+		},
137+		{
138+			name:  "multiple size sub list",
139+			slice: []int{1, 2, 3, 4},
140+			size:  2,
141+			want:  [][]int{{1, 2}, {3, 4}},
142+		},
143+		{
144+			name:  "uneven multiple size sub list",
145+			slice: []int{1, 2, 3, 4, 5},
146+			size:  2,
147+			want:  [][]int{{1, 2}, {3, 4}, {5}},
148+		},
149+		{
150+			name:  "empty sub list",
151+			slice: []int{},
152+			size:  2,
153+			want:  [][]int{{}},
154+		},
155+	}
156+	for _, tc := range testCases {
157+		t.Run(tc.name, func(t *testing.T) {
158+
159+			subList := ChunkBy(tc.slice, tc.size)
160+
161+			if diff := cmp.Diff(tc.want, subList); diff != "" {
162+				t.Errorf("Wrong result given - wanted + got\n %s", diff)
163+			}
164+		})
165+	}
166+}