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		rest = strings.TrimSuffix(rest, ".")
 83		for _, subsection := range section.Subsections {
 84			if strings.HasPrefix(subsection.Name, rest) {
 85				for _, option := range subsection.Options {
 86					result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
 87				}
 88			}
 89		}
 90	}
 91
 92	return result, nil
 93}
 94
 95func (cr *goGitConfigReader) ReadBool(key string) (bool, error) {
 96	val, err := cr.ReadString(key)
 97	if err != nil {
 98		return false, err
 99	}
100
101	return strconv.ParseBool(val)
102}
103
104func (cr *goGitConfigReader) ReadString(key string) (string, error) {
105	cfg, err := cr.getConfig()
106	if err != nil {
107		return "", err
108	}
109
110	split := strings.Split(key, ".")
111
112	if len(split) <= 1 {
113		return "", fmt.Errorf("invalid key")
114	}
115
116	sectionName := split[0]
117	if !cfg.Raw.HasSection(sectionName) {
118		return "", ErrNoConfigEntry
119	}
120	section := cfg.Raw.Section(sectionName)
121
122	switch {
123	case len(split) == 2:
124		optionName := split[1]
125		if !section.HasOption(optionName) {
126			return "", ErrNoConfigEntry
127		}
128		if len(section.OptionAll(optionName)) > 1 {
129			return "", ErrMultipleConfigEntry
130		}
131		return section.Option(optionName), nil
132	default:
133		subsectionName := strings.Join(split[1:len(split)-2], ".")
134		optionName := split[len(split)-1]
135		if !section.HasSubsection(subsectionName) {
136			return "", ErrNoConfigEntry
137		}
138		subsection := section.Subsection(subsectionName)
139		if !subsection.HasOption(optionName) {
140			return "", ErrNoConfigEntry
141		}
142		if len(subsection.OptionAll(optionName)) > 1 {
143			return "", ErrMultipleConfigEntry
144		}
145		return subsection.Option(optionName), nil
146	}
147}
148
149func (cr *goGitConfigReader) ReadTimestamp(key string) (time.Time, error) {
150	value, err := cr.ReadString(key)
151	if err != nil {
152		return time.Time{}, err
153	}
154	return ParseTimestamp(value)
155}
156
157var _ ConfigWrite = &goGitConfigWriter{}
158
159// Only works for the local config as go-git only support that
160type goGitConfigWriter struct {
161	repo *gogit.Repository
162}
163
164func (cw *goGitConfigWriter) StoreString(key, value string) error {
165	cfg, err := cw.repo.Config()
166	if err != nil {
167		return err
168	}
169
170	split := strings.Split(key, ".")
171
172	switch {
173	case len(split) <= 1:
174		return fmt.Errorf("invalid key")
175	case len(split) == 2:
176		cfg.Raw.Section(split[0]).SetOption(split[1], value)
177	default:
178		section := split[0]
179		subsection := strings.Join(split[1:len(split)-1], ".")
180		option := split[len(split)-1]
181		cfg.Raw.Section(section).Subsection(subsection).SetOption(option, value)
182	}
183
184	return cw.repo.SetConfig(cfg)
185}
186
187func (cw *goGitConfigWriter) StoreTimestamp(key string, value time.Time) error {
188	return cw.StoreString(key, strconv.Itoa(int(value.Unix())))
189}
190
191func (cw *goGitConfigWriter) StoreBool(key string, value bool) error {
192	return cw.StoreString(key, strconv.FormatBool(value))
193}
194
195func (cw *goGitConfigWriter) RemoveAll(keyPrefix string) error {
196	cfg, err := cw.repo.Config()
197	if err != nil {
198		return err
199	}
200
201	split := strings.Split(keyPrefix, ".")
202
203	switch {
204	case keyPrefix == "":
205		cfg.Raw.Sections = nil
206		// warning: this does not actually remove everything as go-git config hold
207		// some entries in multiple places (cfg.User ...)
208	case len(split) == 1:
209		if cfg.Raw.HasSection(split[0]) {
210			cfg.Raw.RemoveSection(split[0])
211		} else {
212			return fmt.Errorf("invalid key prefix")
213		}
214	default:
215		if !cfg.Raw.HasSection(split[0]) {
216			return fmt.Errorf("invalid key prefix")
217		}
218		section := cfg.Raw.Section(split[0])
219		rest := strings.Join(split[1:], ".")
220
221		ok := false
222		if section.HasSubsection(rest) {
223			section.RemoveSubsection(rest)
224			ok = true
225		}
226		if section.HasOption(rest) {
227			section.RemoveOption(rest)
228			ok = true
229		}
230		if !ok {
231			return fmt.Errorf("invalid key prefix")
232		}
233	}
234
235	return cw.repo.SetConfig(cfg)
236}