cerrado @ 2dd4cf35aab8324608a83d337459fd8354521b92

feat: Wraps handler into its own package

Although this creates more complex folder structure will allow in the
feature for a easier testing of those given handlers.
diff --git a/main.go b/main.go
index 76da07bd4f55d968e2d8de6fde919aaca7325301..eedff5eb6a12149528573530facbba8b740bd578 100644
--- a/main.go
+++ b/main.go
@@ -10,7 +10,6 @@ 	"os/signal"
 	"time"
 
 	"git.gabrielgio.me/cerrado/pkg/config"
-	"git.gabrielgio.me/cerrado/pkg/git"
 	"git.gabrielgio.me/cerrado/pkg/handler"
 	"git.gabrielgio.me/cerrado/pkg/service"
 	"git.gabrielgio.me/cerrado/pkg/worker"
@@ -33,38 +32,21 @@ 	)
 
 	flag.Parse()
 
-	mux := http.NewServeMux()
-
-	staticHandler, err := handler.NewStaticHander("/static/")
+	// repositorie
+	configRepo, err := config.LoadConfigurationRepository(*configPath)
 	if err != nil {
 		return err
 	}
 
-	f, err := os.Open(*configPath)
-	if err != nil {
-		return err
-	}
+	// services
+	gitService := service.NewGitService(configRepo)
 
-	config, err := config.Parse(f)
+	handler, err := handler.MountHandler(gitService, configRepo)
 	if err != nil {
 		return err
 	}
 
-	// repositories
-	gitServer := git.NewGitServerRepository(config.Scan.Path)
-
-	// services
-	gitService := service.NewGitService(gitServer)
-
-	//handlers
-	gitHandler := handler.NewGitHandler(gitService)
-	aboutHandler := handler.NewAboutHandler(config.RootReadme)
-
-	mux.Handle("/static/", staticHandler)
-	mux.HandleFunc("/config", handler.ConfigFile(*configPath))
-	mux.HandleFunc("/about", aboutHandler.About)
-	mux.HandleFunc("/", gitHandler.List)
-	serverTask := worker.NewServerTask(&http.Server{Handler: mux, Addr: "0.0.0.0:8080"})
+	serverTask := worker.NewServerTask(&http.Server{Handler: handler, Addr: "0.0.0.0:8080"})
 
 	pool := worker.NewTaskPool()
 	pool.AddTask("http-server", 5*time.Second, serverTask)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 9b6accea2b385fa1180aec54df7e0a64bb3ee6fe..419d49dac3a06f7357de72162885c2b822be8e07 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,27 +1,126 @@
 package config
 
 import (
+	"errors"
 	"fmt"
 	"io"
+	"os"
+	"path"
 	"strconv"
 
 	"git.gabrielgio.me/cerrado/pkg/u"
 	"git.sr.ht/~emersion/go-scfg"
+)
+
+var (
+	ScanPathErr = errors.New("Scan path does not exist")
+	RepoPathErr = errors.New("Repository path does not exist")
 )
 
 type (
-	Scan struct {
+
+	// scan represents piece of the scan from the configuration file.
+	scan struct {
 		Path   string
 		Public bool
 	}
 
-	Configuration struct {
-		Scan       *Scan
+	// configuration represents file configuration.
+	configuration struct {
+		Scan       *scan
 		RootReadme string
 	}
+
+	// This is a per repository configuration.
+	GitRepositoryConfiguration struct {
+		Name   string
+		Path   string
+		Public bool
+	}
+
+	// ConfigurationRepository represents the configuration repository (as in
+	// database repositories).
+	// This holds all the function necessary to ask for configuration
+	// information.
+	ConfigurationRepository struct {
+		rootReadme   string
+		repositories []*GitRepositoryConfiguration
+	}
 )
 
-func Parse(r io.Reader) (*Configuration, error) {
+func LoadConfigurationRepository(configPath string) (*ConfigurationRepository, error) {
+	f, err := os.Open(configPath)
+	if err != nil {
+		return nil, err
+	}
+
+	config, err := parse(f)
+	if err != nil {
+		return nil, err
+	}
+
+	repo := &ConfigurationRepository{
+		rootReadme: config.RootReadme,
+	}
+
+	err = repo.expandOnScanPath(config.Scan.Path, config.Scan.Public)
+	if err != nil {
+		return nil, err
+	}
+	return repo, nil
+
+}
+
+// GetRootReadme returns root read path
+func (c *ConfigurationRepository) GetRootReadme() string {
+	return c.rootReadme
+}
+
+// GetByName returns configuration of repository for a given name.
+// It returns nil if there is not match for it.
+func (c *ConfigurationRepository) GetByName(name string) *GitRepositoryConfiguration {
+	for _, r := range c.repositories {
+		if r.Name == name {
+			return r
+		}
+	}
+	return nil
+}
+
+// List returns all the configuration for all repositories.
+func (c *ConfigurationRepository) List() []*GitRepositoryConfiguration {
+	return c.repositories
+}
+
+// expandOnScanPath scans the scanPath for folders taking them as repositories
+// and applying them default configuration.
+func (c *ConfigurationRepository) expandOnScanPath(scanPath string, public bool) error {
+	if !u.FileExist(scanPath) {
+		return ScanPathErr
+	}
+
+	entries, err := os.ReadDir(scanPath)
+	if err != nil {
+		return err
+	}
+
+	c.repositories = make([]*GitRepositoryConfiguration, 0)
+	for _, e := range entries {
+		if !e.IsDir() {
+			continue
+		}
+
+		fullPath := path.Join(scanPath, e.Name())
+		c.repositories = append(c.repositories, &GitRepositoryConfiguration{
+			Name:   e.Name(),
+			Path:   fullPath,
+			Public: public,
+		})
+	}
+	return nil
+}
+
+func parse(r io.Reader) (*configuration, error) {
 	block, err := scfg.Read(r)
 	if err != nil {
 		return nil, err
@@ -42,9 +141,9 @@
 	return config, nil
 }
 
-func defaultConfiguration() *Configuration {
-	return &Configuration{
-		Scan: &Scan{
+func defaultConfiguration() *configuration {
+	return &configuration{
+		Scan: &scan{
 			Public: true,
 			Path:   "",
 		},
@@ -57,7 +156,7 @@ 	scanDir := block.Get("root-readme")
 	return setString(scanDir, readme)
 }
 
-func setScan(block scfg.Block, scan *Scan) error {
+func setScan(block scfg.Block, scan *scan) error {
 	scanDir := block.Get("scan")
 	err := setString(scanDir, &scan.Path)
 	if err != nil {
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index c8cd8876055e2cabe66e1ee574b5c36df358efcb..7afbaef263f86b987b586cc5cb70cc6488a2d994 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -8,17 +8,17 @@
 	"github.com/google/go-cmp/cmp"
 )
 
-func TestConfig(t *testing.T) {
+func TestFileParsing(t *testing.T) {
 	testCases := []struct {
 		name           string
 		config         string
-		expectedConfig *Configuration
+		expectedConfig *configuration
 	}{
 		{
 			name:   "minimal scan",
 			config: `scan "/srv/git"`,
-			expectedConfig: &Configuration{
-				Scan: &Scan{
+			expectedConfig: &configuration{
+				Scan: &scan{
 					Public: true,
 					Path:   "/srv/git",
 				},
@@ -29,8 +29,8 @@ 			name: "complete scan",
 			config: `scan "/srv/git" {
 	public false
 }`,
-			expectedConfig: &Configuration{
-				Scan: &Scan{
+			expectedConfig: &configuration{
+				Scan: &scan{
 					Public: false,
 					Path:   "/srv/git",
 				},
@@ -41,7 +41,7 @@
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			r := strings.NewReader(tc.config)
-			config, err := Parse(r)
+			config, err := parse(r)
 			if err != nil {
 				t.Fatalf("Error parsing config %s", err.Error())
 			}
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 85a3b9560ac63e1915751dc8407365e72539c8fc..b9ab235f7226301c7f57ca245c5b1660fa6230b9 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -2,63 +2,29 @@ package git
 
 import (
 	"errors"
-	"os"
-	"path"
 
-	"git.gabrielgio.me/cerrado/pkg/u"
 	"github.com/go-git/go-git/v5"
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
+var ()
+
 var (
-	ScanPathErr    = errors.New("Scan path does not exist")
-	RepoPathErr    = errors.New("Repository path does not exist")
-	missingHeadErr = errors.New("Head not found")
+	MissingHeadErr = errors.New("Head not found")
 )
 
 type (
-	GitServerRepository struct {
-		scanPath string
-	}
-
 	GitRepository struct {
 		path string
 	}
 )
 
-func NewGitServerRepository(scanPath string) *GitServerRepository {
-	return &GitServerRepository{scanPath}
-}
-
 func NewGitRepository(dir string) *GitRepository {
 	return &GitRepository{
 		path: dir,
 	}
 }
 
-func (g *GitServerRepository) List() ([]*GitRepository, error) {
-	if !u.FileExist(g.scanPath) {
-		return nil, ScanPathErr
-	}
-
-	entries, err := os.ReadDir(g.scanPath)
-	if err != nil {
-		return nil, err
-	}
-
-	repos := make([]*GitRepository, 0)
-	for _, e := range entries {
-		if !e.IsDir() {
-			continue
-		}
-
-		fullPath := path.Join(g.scanPath, e.Name())
-		repos = append(repos, NewGitRepository(fullPath))
-	}
-
-	return repos, nil
-}
-
 func (g *GitRepository) Path() string {
 	return g.path
 }
@@ -71,7 +37,7 @@ 	}
 
 	ref, err := repo.Head()
 	if err != nil {
-		return nil, errors.Join(missingHeadErr, err)
+		return nil, errors.Join(MissingHeadErr, err)
 	}
 
 	c, err := repo.CommitObject(ref.Hash())
diff --git a/pkg/handler/about.go b/pkg/handler/about/handler.go
rename from pkg/handler/about.go
rename to pkg/handler/about/handler.go
index 3ab2de880549e94be04543d4a0d4698d5d87f2b5..a2caa4eeea02e1e6885069fc90f0d1ef479b4798 100644
--- a/pkg/handler/about.go
+++ b/pkg/handler/about/handler.go
@@ -1,4 +1,4 @@
-package handler
+package about
 
 import (
 	"io"
@@ -13,12 +13,18 @@
 	"git.gabrielgio.me/cerrado/templates"
 )
 
-type AboutHandler struct {
-	readmePath string
-}
+type (
+	AboutHandler struct {
+		readmePath string
+	}
 
-func NewAboutHandler(readmePath string) *AboutHandler {
-	return &AboutHandler{readmePath}
+	configurationRepository interface {
+		GetRootReadme() string
+	}
+)
+
+func NewAboutHandler(configRepo configurationRepository) *AboutHandler {
+	return &AboutHandler{configRepo.GetRootReadme()}
 }
 
 func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) {
diff --git a/pkg/handler/git.go b/pkg/handler/git/handler.go
rename from pkg/handler/git.go
rename to pkg/handler/git/handler.go
index 1ed2c49d357ffbb4b74f2a2331cd84fd6a660794..236ac414a2ce8afb92d6c86bc64781a7f8189904 100644
--- a/pkg/handler/git.go
+++ b/pkg/handler/git/handler.go
@@ -1,4 +1,4 @@
-package handler
+package git
 
 import (
 	"log/slog"
@@ -8,12 +8,20 @@ 	"git.gabrielgio.me/cerrado/pkg/service"
 	"git.gabrielgio.me/cerrado/templates"
 )
 
-type GitHandler struct {
-	gitService *service.GitService
-}
+type (
+	GitHandler struct {
+		gitService gitService
+	}
 
-func NewGitHandler(gitService *service.GitService) *GitHandler {
-	return &GitHandler{gitService}
+	gitService interface {
+		ListRepositories() ([]*service.Repository, error)
+	}
+)
+
+func NewGitHandler(gitService gitService) *GitHandler {
+	return &GitHandler{
+		gitService: gitService,
+	}
 }
 
 func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {
diff --git a/pkg/handler/router.go b/pkg/handler/router.go
new file mode 100644
index 0000000000000000000000000000000000000000..a8c9c6f227152e0602af3786f023508c563419d0
--- /dev/null
+++ b/pkg/handler/router.go
@@ -0,0 +1,38 @@
+package handler
+
+import (
+	"net/http"
+
+	serverconfig "git.gabrielgio.me/cerrado/pkg/config"
+	"git.gabrielgio.me/cerrado/pkg/handler/about"
+	"git.gabrielgio.me/cerrado/pkg/handler/config"
+	"git.gabrielgio.me/cerrado/pkg/handler/git"
+	"git.gabrielgio.me/cerrado/pkg/handler/static"
+	"git.gabrielgio.me/cerrado/pkg/service"
+)
+
+// Mount handler gets the requires service and repository to build the handlers
+// This functons wraps the whole handler package and wraps it into one part so
+// its sub package don't leak in other places.
+func MountHandler(
+	gitService *service.GitService,
+	configRepo *serverconfig.ConfigurationRepository,
+) (http.Handler, error) {
+	var (
+		gitHandler   = git.NewGitHandler(gitService)
+		aboutHandler = about.NewAboutHandler(configRepo)
+		configHander = config.ConfigFile(configRepo)
+	)
+
+	staticHandler, err := static.NewStaticHander("/static/")
+	if err != nil {
+		return nil, err
+	}
+
+	mux := http.NewServeMux()
+	mux.Handle("/static/", staticHandler)
+	mux.HandleFunc("/config", configHander)
+	mux.HandleFunc("/about", aboutHandler.About)
+	mux.HandleFunc("/", gitHandler.List)
+	return mux, nil
+}
diff --git a/pkg/handler/static.go b/pkg/handler/static/handler.go
rename from pkg/handler/static.go
rename to pkg/handler/static/handler.go
index 9f312f41f09ef14dad4649bfaa0b76d61f8abb5e..6a826cc988aa4d11f5f71417b4a3198a7d611882 100644
--- a/pkg/handler/static.go
+++ b/pkg/handler/static/handler.go
@@ -1,4 +1,4 @@
-package handler
+package static
 
 import (
 	"io/fs"
diff --git a/pkg/handler/status.go b/pkg/handler/config/handler.go
rename from pkg/handler/status.go
rename to pkg/handler/config/handler.go
index 9baac2c47a3fce61a82cc9451ecc92e8de179c87..c278e35d0e624ac09c1a0065f0f906bd6e88dfe1 100644
--- a/pkg/handler/status.go
+++ b/pkg/handler/config/handler.go
@@ -1,11 +1,10 @@
-package handler
+package config
 
 import (
 	"bytes"
 	"encoding/json"
 	"log/slog"
 	"net/http"
-	"os"
 
 	"github.com/alecthomas/chroma/v2/formatters/html"
 	"github.com/alecthomas/chroma/v2/lexers"
@@ -15,21 +14,25 @@ 	"git.gabrielgio.me/cerrado/pkg/config"
 	"git.gabrielgio.me/cerrado/templates"
 )
 
-func ConfigFile(configPath string) func(http.ResponseWriter, *http.Request) {
+type (
+	configurationRepository interface {
+		GetRootReadme() string
+		List() []*config.GitRepositoryConfiguration
+	}
+)
+
+func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, _ *http.Request) {
-		f, err := os.Open(configPath)
-		if err != nil {
-			slog.Error("Error openning config file", "error", err, "path", configPath)
-			return
-		}
 
-		c, err := config.Parse(f)
-		if err != nil {
-			slog.Error("Error parsing config", "error", err, "path", configPath)
-			return
+		config := struct {
+			RootReadme   string
+			Repositories []*config.GitRepositoryConfiguration
+		}{
+			RootReadme:   configRepo.GetRootReadme(),
+			Repositories: configRepo.List(),
 		}
 
-		b, err := json.MarshalIndent(c, "", "	")
+		b, err := json.MarshalIndent(config, "", "  ")
 		if err != nil {
 			slog.Error("Error parsing json", "error", err)
 			return
diff --git a/pkg/service/git.go b/pkg/service/git.go
index 94ca75e454f3af3b40f834ea5f858b37b6e56efd..2b1fe252f8e7785238896329ceca3305c71b817c 100644
--- a/pkg/service/git.go
+++ b/pkg/service/git.go
@@ -3,44 +3,48 @@
 import (
 	"path"
 
+	"git.gabrielgio.me/cerrado/pkg/config"
 	"git.gabrielgio.me/cerrado/pkg/git"
 )
 
 type (
-	GitService struct {
-		server *git.GitServerRepository
-	}
 	Repository struct {
 		Name              string
 		Title             string
 		LastCommitMessage string
 		LastCommitDate    string
 	}
+
+	GitService struct {
+		configRepo configurationRepository
+	}
+
+	configurationRepository interface {
+		List() []*config.GitRepositoryConfiguration
+	}
 )
 
 // TODO: make it configurable
 const timeFormat = "2006.01.02 15:04:05"
 
-func NewGitService(server *git.GitServerRepository) *GitService {
+func NewGitService(configRepo configurationRepository) *GitService {
 	return &GitService{
-		server: server,
+		configRepo: configRepo,
 	}
 }
 
 func (g *GitService) ListRepositories() ([]*Repository, error) {
-	rs, err := g.server.List()
-	if err != nil {
-		return nil, err
-	}
+	rs := g.configRepo.List()
 
 	repos := make([]*Repository, len(rs))
 	for i, r := range rs {
-		obj, err := r.LastCommit()
+		repo := git.NewGitRepository(r.Path)
+		obj, err := repo.LastCommit()
 		if err != nil {
 			return nil, err
 		}
 
-		baseName := path.Base(r.Path())
+		baseName := path.Base(r.Path)
 		repos[i] = &Repository{
 			Name:              baseName,
 			Title:             baseName,