gogit_config.go

  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/config"
 11)
 12
 13var _ Config = &goGitConfig{}
 14
 15type goGitConfig struct {
 16	ConfigRead
 17	ConfigWrite
 18}
 19
 20func newGoGitLocalConfig(repo *gogit.Repository) *goGitConfig {
 21	return &goGitConfig{
 22		ConfigRead:  &goGitConfigReader{getConfig: repo.Config},
 23		ConfigWrite: &goGitConfigWriter{repo: repo},
 24	}
 25}
 26
 27func newGoGitGlobalConfig(repo *gogit.Repository) *goGitConfig {
 28	return &goGitConfig{
 29		ConfigRead: &goGitConfigReader{getConfig: func() (*config.Config, error) {
 30			return config.LoadConfig(config.GlobalScope)
 31		}},
 32		ConfigWrite: &configPanicWriter{},
 33	}
 34}
 35
 36var _ ConfigRead = &goGitConfigReader{}
 37
 38type goGitConfigReader struct {
 39	getConfig func() (*config.Config, error)
 40}
 41
 42func (cr *goGitConfigReader) ReadAll(keyPrefix string) (map[string]string, error) {
 43	cfg, err := cr.getConfig()
 44	if err != nil {
 45		return nil, err
 46	}
 47
 48	split := strings.Split(keyPrefix, ".")
 49	result := make(map[string]string)
 50
 51	switch {
 52	case keyPrefix == "":
 53		for _, section := range cfg.Raw.Sections {
 54			for _, option := range section.Options {
 55				result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
 56			}
 57			for _, subsection := range section.Subsections {
 58				for _, option := range subsection.Options {
 59					result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
 60				}
 61			}
 62		}
 63	case len(split) == 1:
 64		if !cfg.Raw.HasSection(split[0]) {
 65			return nil, nil
 66		}
 67		section := cfg.Raw.Section(split[0])
 68		for _, option := range section.Options {
 69			result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
 70		}
 71		for _, subsection := range section.Subsections {
 72			for _, option := range subsection.Options {
 73				result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
 74			}
 75		}
 76	default:
 77		if !cfg.Raw.HasSection(split[0]) {
 78			return nil, nil
 79		}
 80		section := cfg.Raw.Section(split[0])
 81		rest := strings.Join(split[1:], ".")
 82		for _, subsection := range section.Subsections {
 83			if strings.HasPrefix(subsection.Name, rest) {
 84				for _, option := range subsection.Options {
 85					result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
 86				}
 87			}
 88		}
 89	}
 90
 91	return result, nil
 92}
 93
 94func (cr *goGitConfigReader) ReadBool(key string) (bool, error) {
 95	val, err := cr.ReadString(key)
 96	if err != nil {
 97		return false, err
 98	}
 99
100	return strconv.ParseBool(val)
101}
102
103func (cr *goGitConfigReader) ReadString(key string) (string, error) {
104	cfg, err := cr.getConfig()
105	if err != nil {
106		return "", err
107	}
108
109	split := strings.Split(key, ".")
110
111	if len(split) <= 1 {
112		return "", fmt.Errorf("invalid key")
113	}
114
115	sectionName := split[0]
116	if !cfg.Raw.HasSection(sectionName) {
117		return "", ErrNoConfigEntry
118	}
119	section := cfg.Raw.Section(sectionName)
120
121	switch {
122	case len(split) == 2:
123		optionName := split[1]
124		if !section.HasOption(optionName) {
125			return "", ErrNoConfigEntry
126		}
127		if len(section.OptionAll(optionName)) > 1 {
128			return "", ErrMultipleConfigEntry
129		}
130		return section.Option(optionName), nil
131	default:
132		subsectionName := strings.Join(split[1:len(split)-2], ".")
133		optionName := split[len(split)-1]
134		if !section.HasSubsection(subsectionName) {
135			return "", ErrNoConfigEntry
136		}
137		subsection := section.Subsection(subsectionName)
138		if !subsection.HasOption(optionName) {
139			return "", ErrNoConfigEntry
140		}
141		if len(subsection.OptionAll(optionName)) > 1 {
142			return "", ErrMultipleConfigEntry
143		}
144		return subsection.Option(optionName), nil
145	}
146}
147
148func (cr *goGitConfigReader) ReadTimestamp(key string) (time.Time, error) {
149	value, err := cr.ReadString(key)
150	if err != nil {
151		return time.Time{}, err
152	}
153	return ParseTimestamp(value)
154}
155
156var _ ConfigWrite = &goGitConfigWriter{}
157
158// Only works for the local config as go-git only support that
159type goGitConfigWriter struct {
160	repo *gogit.Repository
161}
162
163func (cw *goGitConfigWriter) StoreString(key, value string) error {
164	cfg, err := cw.repo.Config()
165	if err != nil {
166		return err
167	}
168
169	split := strings.Split(key, ".")
170
171	switch {
172	case len(split) <= 1:
173		return fmt.Errorf("invalid key")
174	case len(split) == 2:
175		cfg.Raw.Section(split[0]).SetOption(split[1], value)
176	default:
177		section := split[0]
178		subsection := strings.Join(split[1:len(split)-1], ".")
179		option := split[len(split)-1]
180		cfg.Raw.Section(section).Subsection(subsection).SetOption(option, value)
181	}
182
183	return cw.repo.SetConfig(cfg)
184}
185
186func (cw *goGitConfigWriter) StoreTimestamp(key string, value time.Time) error {
187	return cw.StoreString(key, strconv.Itoa(int(value.Unix())))
188}
189
190func (cw *goGitConfigWriter) StoreBool(key string, value bool) error {
191	return cw.StoreString(key, strconv.FormatBool(value))
192}
193
194func (cw *goGitConfigWriter) RemoveAll(keyPrefix string) error {
195	cfg, err := cw.repo.Config()
196	if err != nil {
197		return err
198	}
199
200	split := strings.Split(keyPrefix, ".")
201
202	switch {
203	case keyPrefix == "":
204		cfg.Raw.Sections = nil
205		// warning: this does not actually remove everything as go-git config hold
206		// some entries in multiple places (cfg.User ...)
207	case len(split) == 1:
208		if cfg.Raw.HasSection(split[0]) {
209			cfg.Raw.RemoveSection(split[0])
210		} else {
211			return fmt.Errorf("invalid key prefix")
212		}
213	default:
214		if !cfg.Raw.HasSection(split[0]) {
215			return fmt.Errorf("invalid key prefix")
216		}
217		section := cfg.Raw.Section(split[0])
218		rest := strings.Join(split[1:], ".")
219
220		ok := false
221		if section.HasSubsection(rest) {
222			section.RemoveSubsection(rest)
223			ok = true
224		}
225		if section.HasOption(rest) {
226			section.RemoveOption(rest)
227			ok = true
228		}
229		if !ok {
230			return fmt.Errorf("invalid key prefix")
231		}
232	}
233
234	return cw.repo.SetConfig(cfg)
235}