summaryrefslogtreecommitdiff
path: root/cgitrc/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'cgitrc/config.go')
-rw-r--r--cgitrc/config.go139
1 files changed, 139 insertions, 0 deletions
diff --git a/cgitrc/config.go b/cgitrc/config.go
new file mode 100644
index 0000000..82b5f69
--- /dev/null
+++ b/cgitrc/config.go
@@ -0,0 +1,139 @@
+package cgitrc
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "strings"
+)
+
+// Config is a cgit configuration file.
+type Config struct {
+ CloneURL string
+ Repos map[string]*Repo
+
+ files map[string]bool
+}
+
+// Repo describes a cgit repository configuration.
+type Repo struct {
+ URL string
+ Desc string
+ CloneURL string
+}
+
+// Open reads a config file.
+func Open(filename string) (*Config, error) {
+ cfg := &Config{
+ Repos: make(map[string]*Repo),
+ files: make(map[string]bool, 1),
+ }
+ if err := cfg.fromFile(filename); err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
+
+// ResolveRepoCloneURL returns the effective clone URL for a repository.
+// Returns an empty string if no clone URL can be determined.
+func (cfg *Config) ResolveRepoCloneURL(r *Repo) string {
+ if r.CloneURL != "" {
+ return cfg.CloneURL
+ }
+ if cfg.CloneURL != "" {
+ return strings.Replace(cfg.CloneURL, "$CGIT_REPO_URL", r.URL, 1)
+ }
+ return ""
+}
+
+func (cfg *Config) fromFile(filename string) error {
+ filename = path.Clean(filename)
+ if _, ok := cfg.files[filename]; ok {
+ return errors.New("recursive include")
+ }
+ file, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ parser := &Parser{}
+ if _, err := io.Copy(parser, file); err != nil {
+ return err
+ }
+ if err := parser.Close(); err != nil {
+ return err
+ }
+ cfg.files[filename] = true
+ return cfg.fromFields(parser.Fields)
+}
+
+func (cfg *Config) fromFields(fields []*ParserField) error {
+ var (
+ repo *Repo
+ newRepo bool
+ )
+ for _, f := range fields {
+ switch f.Keys[0] {
+ case "include":
+ if len(f.Keys) != 1 {
+ return errors.New("invalid key")
+ }
+ if err := cfg.fromFile(f.Value); err != nil {
+ return wrapFieldErr(f, fmt.Errorf("include %s: %v", f.Value, err))
+ }
+ case "repo":
+ if len(f.Keys) == 2 && f.Keys[1] == "url" {
+ repo = &Repo{}
+ newRepo = true
+ }
+ if repo == nil {
+ return wrapFieldErr(f, errors.New("unexpected key"))
+ }
+ if err := repo.setField(f.Keys[1:], f.Value); err != nil {
+ return wrapFieldErr(f, err)
+ }
+ if newRepo {
+ cfg.Repos[repo.URL] = repo
+ newRepo = false
+ }
+ default:
+ if err := cfg.setField(f.Keys, f.Value); err != nil {
+ return wrapFieldErr(f, err)
+ }
+ }
+ }
+ return nil
+}
+
+func (cfg *Config) setField(keys []string, v string) error {
+ if len(keys) != 1 {
+ return errors.New("invalid key")
+ }
+ switch keys[0] {
+ case "clone-url":
+ cfg.CloneURL = v
+ }
+ return nil
+}
+
+func (r *Repo) setField(keys []string, v string) error {
+ if len(keys) != 1 {
+ return errors.New("invalid key")
+ }
+ switch keys[0] {
+ case "url":
+ r.URL = v
+ case "desc":
+ r.Desc = v
+ case "clone-url":
+ r.CloneURL = v
+ }
+ return nil
+}
+
+func wrapFieldErr(f *ParserField, err error) error {
+ return fmt.Errorf("on line %d: %v", f.LineNo, err)
+}