1package repository
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7 "time"
8
9 gogit "github.com/go-git/go-git/v5"
10 "github.com/go-git/go-git/v5/plumbing/format/config"
11)
12
13var _ Config = &goGitConfig{}
14
15type goGitConfig struct {
16 repo *gogit.Repository
17}
18
19func newGoGitConfig(repo *gogit.Repository) *goGitConfig {
20 return &goGitConfig{repo: repo}
21}
22
23func (ggc *goGitConfig) StoreString(key, value string) error {
24 cfg, err := ggc.repo.Config()
25 if err != nil {
26 return err
27 }
28
29 split := strings.Split(key, ".")
30
31 switch {
32 case len(split) <= 1:
33 return fmt.Errorf("invalid key")
34 case len(split) == 2:
35 cfg.Raw.Section(split[0]).SetOption(split[1], value)
36 default:
37 section := split[0]
38 subsection := strings.Join(split[1:len(split)-2], ".")
39 option := split[len(split)-1]
40 cfg.Raw.Section(section).Subsection(subsection).SetOption(option, value)
41 }
42
43 return ggc.repo.SetConfig(cfg)
44}
45
46func (ggc *goGitConfig) StoreTimestamp(key string, value time.Time) error {
47 return ggc.StoreString(key, strconv.Itoa(int(value.Unix())))
48}
49
50func (ggc *goGitConfig) StoreBool(key string, value bool) error {
51 return ggc.StoreString(key, strconv.FormatBool(value))
52}
53
54func (ggc *goGitConfig) ReadAll(keyPrefix string) (map[string]string, error) {
55 cfg, err := ggc.repo.Config()
56 if err != nil {
57 return nil, err
58 }
59
60 split := strings.Split(keyPrefix, ".")
61
62 var opts config.Options
63
64 switch {
65 case len(split) < 1:
66 return nil, fmt.Errorf("invalid key prefix")
67 case len(split) == 1:
68 opts = cfg.Raw.Section(split[0]).Options
69 default:
70 section := split[0]
71 subsection := strings.Join(split[1:len(split)-1], ".")
72 opts = cfg.Raw.Section(section).Subsection(subsection).Options
73 }
74
75 if len(opts) == 0 {
76 return nil, fmt.Errorf("invalid section")
77 }
78
79 if keyPrefix[len(keyPrefix)-1:] != "." {
80 keyPrefix += "."
81 }
82
83 result := make(map[string]string, len(opts))
84 for _, opt := range opts {
85 result[keyPrefix+opt.Key] = opt.Value
86 }
87
88 return result, nil
89}
90
91func (ggc *goGitConfig) ReadBool(key string) (bool, error) {
92 val, err := ggc.ReadString(key)
93 if err != nil {
94 return false, err
95 }
96
97 return strconv.ParseBool(val)
98}
99
100func (ggc *goGitConfig) ReadString(key string) (string, error) {
101 cfg, err := ggc.repo.Config()
102 if err != nil {
103 return "", err
104 }
105
106 split := strings.Split(key, ".")
107
108 // TODO: return ErrNoConfigEntry and ErrMultipleConfigEntry
109 // Can use forked go-git: https://github.com/go-git/go-git/pull/112
110
111 switch {
112 case len(split) <= 1:
113 return "", fmt.Errorf("invalid key")
114 case len(split) == 2:
115 return cfg.Raw.Section(split[0]).Option(split[1]), nil
116 default:
117 section := split[0]
118 subsection := strings.Join(split[1:len(split)-2], ".")
119 option := split[len(split)-1]
120 return cfg.Raw.Section(section).Subsection(subsection).Option(option), nil
121 }
122}
123
124func (ggc *goGitConfig) ReadTimestamp(key string) (time.Time, error) {
125 value, err := ggc.ReadString(key)
126 if err != nil {
127 return time.Time{}, err
128 }
129 return ParseTimestamp(value)
130}
131
132func (ggc *goGitConfig) RemoveAll(keyPrefix string) error {
133 cfg, err := ggc.repo.Config()
134 if err != nil {
135 return err
136 }
137
138 split := strings.Split(keyPrefix, ".")
139
140 // missing in go-git
141 hasOption := func(options config.Options, key string) bool {
142 for _, option := range options {
143 if option.IsKey(key) {
144 return true
145 }
146 }
147 return false
148 }
149
150 switch {
151 case len(split) < 1:
152 return fmt.Errorf("invalid key prefix")
153 case len(split) == 1:
154 if len(cfg.Raw.Section(split[0]).Options) > 0 {
155 cfg.Raw.RemoveSection(split[0])
156 } else {
157 return fmt.Errorf("invalid key prefix")
158 }
159 default:
160 section := split[0]
161 rest := strings.Join(split[1:], ".")
162
163 if cfg.Raw.Section(section).HasSubsection(rest) {
164 cfg.Raw.RemoveSubsection(section, rest)
165 } else {
166 if hasOption(cfg.Raw.Section(section).Options, rest) {
167 cfg.Raw.Section(section).RemoveOption(rest)
168 } else {
169 return fmt.Errorf("invalid key prefix")
170 }
171 }
172 }
173
174 return ggc.repo.SetConfig(cfg)
175}