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}