cerrado @ v0.0.4

  1package config
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"io"
  7	"os"
  8	"path"
  9	"path/filepath"
 10	"strconv"
 11
 12	"git.gabrielgio.me/cerrado/pkg/u"
 13	"git.sr.ht/~emersion/go-scfg"
 14)
 15
 16var (
 17	ScanPathErr        = errors.New("Scan path does not exist")
 18	RepoPathErr        = errors.New("Repository path does not exist")
 19	InvalidPropertyErr = errors.New("Invalid property")
 20)
 21
 22type (
 23
 24	// scan represents piece of the scan from the configuration file.
 25	scan struct {
 26		Path   string
 27		Public bool
 28	}
 29
 30	// configuration represents file configuration.
 31	// fields needs to be exported to cmp to work
 32	configuration struct {
 33		Scan         *scan
 34		RootReadme   string
 35		ListenAddr   string
 36		Repositories []*GitRepositoryConfiguration
 37	}
 38
 39	// This is a per repository configuration.
 40	GitRepositoryConfiguration struct {
 41		Name        string
 42		Path        string
 43		Description string
 44		Public      bool
 45	}
 46
 47	// ConfigurationRepository represents the configuration repository (as in
 48	// database repositories).
 49	// This holds all the function necessary to ask for configuration
 50	// information.
 51	ConfigurationRepository struct {
 52		rootReadme   string
 53		listenAddr   string
 54		repositories []*GitRepositoryConfiguration
 55	}
 56)
 57
 58func LoadConfigurationRepository(configPath string) (*ConfigurationRepository, error) {
 59	f, err := os.Open(configPath)
 60	if err != nil {
 61		return nil, err
 62	}
 63
 64	config, err := parse(f)
 65	if err != nil {
 66		return nil, err
 67	}
 68
 69	repo := &ConfigurationRepository{
 70		rootReadme:   config.RootReadme,
 71		listenAddr:   config.ListenAddr,
 72		repositories: config.Repositories,
 73	}
 74
 75	if config.Scan.Path != "" {
 76		err = repo.expandOnScanPath(config.Scan.Path, config.Scan.Public)
 77		if err != nil {
 78			return nil, err
 79		}
 80	}
 81
 82	return repo, nil
 83
 84}
 85
 86// GetRootReadme returns root read path
 87func (c *ConfigurationRepository) GetRootReadme() string {
 88	return c.rootReadme
 89}
 90
 91func (c *ConfigurationRepository) GetListenAddr() string {
 92	return c.listenAddr
 93}
 94
 95// GetByName returns configuration of repository for a given name.
 96// It returns nil if there is not match for it.
 97func (c *ConfigurationRepository) GetByName(name string) *GitRepositoryConfiguration {
 98	for _, r := range c.repositories {
 99		if r.Name == name {
100			return r
101		}
102	}
103	return nil
104}
105
106// List returns all the configuration for all repositories.
107func (c *ConfigurationRepository) List() []*GitRepositoryConfiguration {
108	return c.repositories
109}
110
111// expandOnScanPath scans the scanPath for folders taking them as repositories
112// and applying them default configuration.
113func (c *ConfigurationRepository) expandOnScanPath(scanPath string, public bool) error {
114	if !u.FileExist(scanPath) {
115		return ScanPathErr
116	}
117
118	entries, err := os.ReadDir(scanPath)
119	if err != nil {
120		return err
121	}
122
123	for _, e := range entries {
124		if !e.IsDir() {
125			continue
126		}
127
128		fullPath := path.Join(scanPath, e.Name())
129		if !c.repoExits(fullPath) {
130			c.repositories = append(c.repositories, &GitRepositoryConfiguration{
131				Name:   e.Name(),
132				Path:   fullPath,
133				Public: public,
134			})
135		}
136	}
137	return nil
138}
139
140func (c *ConfigurationRepository) repoExits(path string) bool {
141	for _, r := range c.repositories {
142		if path == r.Path {
143			return true
144		}
145	}
146	return false
147}
148
149func parse(r io.Reader) (*configuration, error) {
150	block, err := scfg.Read(r)
151	if err != nil {
152		return nil, err
153	}
154
155	config := defaultConfiguration()
156
157	err = setScan(block, config.Scan)
158	if err != nil {
159		return nil, err
160	}
161
162	err = setRootReadme(block, &config.RootReadme)
163	if err != nil {
164		return nil, err
165	}
166
167	err = setListenAddr(block, &config.ListenAddr)
168	if err != nil {
169		return nil, err
170	}
171
172	err = setRepositories(block, &config.Repositories)
173	if err != nil {
174		return nil, err
175	}
176
177	return config, nil
178}
179
180func setRepositories(block scfg.Block, repositories *[]*GitRepositoryConfiguration) error {
181	blocks := block.GetAll("repository")
182
183	for _, r := range blocks {
184		if len(r.Params) != 1 {
185			return fmt.Errorf(
186				"Invlid number of params for repository: %w",
187				InvalidPropertyErr,
188			)
189		}
190
191		path := u.FirstOrZero(r.Params)
192		repository := defaultRepisotryConfiguration(path)
193
194		for _, d := range r.Children {
195			// under repository there is only single param properties
196			if len(d.Params) != 1 {
197				return fmt.Errorf(
198					"Invlid number of params for %s: %w",
199					d.Name,
200					InvalidPropertyErr,
201				)
202			}
203
204			switch d.Name {
205			case "name":
206				if err := setString(d, &repository.Name); err != nil {
207					return err
208				}
209			case "description":
210				if err := setString(d, &repository.Description); err != nil {
211					return err
212				}
213			case "public":
214				if err := setBool(d, &repository.Public); err != nil {
215					return err
216				}
217			}
218		}
219
220		*repositories = append(*repositories, repository)
221	}
222
223	return nil
224}
225
226func defaultConfiguration() *configuration {
227	return &configuration{
228		Scan:         defaultScan(),
229		RootReadme:   "",
230		ListenAddr:   defaultAddr(),
231		Repositories: make([]*GitRepositoryConfiguration, 0),
232	}
233}
234
235func defaultScan() *scan {
236	return &scan{
237		Public: false,
238		Path:   "",
239	}
240}
241
242func defaultAddr() string {
243	return "tcp://localhost:8080"
244}
245
246func defaultRepisotryConfiguration(path string) *GitRepositoryConfiguration {
247	return &GitRepositoryConfiguration{
248		Path:        path,
249		Name:        filepath.Base(path),
250		Description: "",
251		Public:      false,
252	}
253}
254
255func setRootReadme(block scfg.Block, readme *string) error {
256	scanDir := block.Get("root-readme")
257	return setString(scanDir, readme)
258}
259
260func setListenAddr(block scfg.Block, listenAddr *string) error {
261	scanDir := block.Get("listen-addr")
262	return setString(scanDir, listenAddr)
263}
264
265func setScan(block scfg.Block, scan *scan) error {
266	scanDir := block.Get("scan")
267	if scanDir == nil {
268		return nil
269	}
270	err := setString(scanDir, &scan.Path)
271	if err != nil {
272		return err
273	}
274
275	public := scanDir.Children.Get("public")
276	return setBool(public, &scan.Public)
277}
278
279func setBool(dir *scfg.Directive, field *bool) error {
280	if dir != nil {
281
282		p1, _ := u.First(dir.Params)
283		v, err := strconv.ParseBool(p1)
284		if err != nil {
285			return fmt.Errorf("Error parsing bool param of %s: %w", dir.Name, err)
286		}
287		*field = v
288	}
289	return nil
290}
291
292func setString(dir *scfg.Directive, field *string) error {
293	if dir != nil {
294		*field = u.FirstOrZero(dir.Params)
295	}
296	return nil
297}