cerrado @ 2fcc394c53f995750b52ad06153041f61f0a0c55

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