aboutsummaryrefslogblamecommitdiffstats
path: root/repository/gogit_config.go
blob: ba61adcac651a9217e3c36dccc3a9747c1d68d2e (plain) (tree)
1
2
3
4
5
6
7
8
9








                                           
                                            




                             

                   

 




                                                                        

 




                                                                                   




                                                                                          
         

 
                                       
 

                                                

 

                                                                                   




                                              
                                         

                










                                                                                                                                 
                             
                                                  
                                       









                                                                                                                         
                
                                                  
                                       


                                                    
                                                    






                                                                                                                                 

         


                          

                                                                 






                                     

                                                                     





                                        








                                                    

                
                             







                                                           
                












                                                                          


         

                                                                           





                                       







































                                                                                        





                                              
                



                                                                                          
                             
                                                 




                                                               



                                                               

                                                    










                                                               


                 
                                     
 
package repository

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	gogit "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/config"
)

var _ Config = &goGitConfig{}

type goGitConfig struct {
	ConfigRead
	ConfigWrite
}

func newGoGitLocalConfig(repo *gogit.Repository) *goGitConfig {
	return &goGitConfig{
		ConfigRead:  &goGitConfigReader{getConfig: repo.Config},
		ConfigWrite: &goGitConfigWriter{repo: repo},
	}
}

func newGoGitGlobalConfig() *goGitConfig {
	// TODO: replace that with go-git native implementation once it's supported
	// see: https://github.com/go-git/go-git
	// see: https://github.com/src-d/go-git/issues/760

	return &goGitConfig{
		ConfigRead: &goGitConfigReader{getConfig: func() (*config.Config, error) {
			return config.LoadConfig(config.GlobalScope)
		}},
		ConfigWrite: &configPanicWriter{},
	}
}

var _ ConfigRead = &goGitConfigReader{}

type goGitConfigReader struct {
	getConfig func() (*config.Config, error)
}

func (cr *goGitConfigReader) ReadAll(keyPrefix string) (map[string]string, error) {
	cfg, err := cr.getConfig()
	if err != nil {
		return nil, err
	}

	split := strings.Split(keyPrefix, ".")
	result := make(map[string]string)

	switch {
	case keyPrefix == "":
		for _, section := range cfg.Raw.Sections {
			for _, option := range section.Options {
				result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
			}
			for _, subsection := range section.Subsections {
				for _, option := range subsection.Options {
					result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
				}
			}
		}
	case len(split) == 1:
		if !cfg.Raw.HasSection(split[0]) {
			return nil, nil
		}
		section := cfg.Raw.Section(split[0])
		for _, option := range section.Options {
			result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
		}
		for _, subsection := range section.Subsections {
			for _, option := range subsection.Options {
				result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
			}
		}
	default:
		if !cfg.Raw.HasSection(split[0]) {
			return nil, nil
		}
		section := cfg.Raw.Section(split[0])
		rest := strings.Join(split[1:], ".")
		rest = strings.TrimSuffix(rest, ".")
		for _, subsection := range section.Subsections {
			if strings.HasPrefix(subsection.Name, rest) {
				for _, option := range subsection.Options {
					result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
				}
			}
		}
	}

	return result, nil
}

func (cr *goGitConfigReader) ReadBool(key string) (bool, error) {
	val, err := cr.ReadString(key)
	if err != nil {
		return false, err
	}

	return strconv.ParseBool(val)
}

func (cr *goGitConfigReader) ReadString(key string) (string, error) {
	cfg, err := cr.getConfig()
	if err != nil {
		return "", err
	}

	split := strings.Split(key, ".")

	if len(split) <= 1 {
		return "", fmt.Errorf("invalid key")
	}

	sectionName := split[0]
	if !cfg.Raw.HasSection(sectionName) {
		return "", ErrNoConfigEntry
	}
	section := cfg.Raw.Section(sectionName)

	switch {
	case len(split) == 2:
		optionName := split[1]
		if !section.HasOption(optionName) {
			return "", ErrNoConfigEntry
		}
		if len(section.OptionAll(optionName)) > 1 {
			return "", ErrMultipleConfigEntry
		}
		return section.Option(optionName), nil
	default:
		subsectionName := strings.Join(split[1:len(split)-2], ".")
		optionName := split[len(split)-1]
		if !section.HasSubsection(subsectionName) {
			return "", ErrNoConfigEntry
		}
		subsection := section.Subsection(subsectionName)
		if !subsection.HasOption(optionName) {
			return "", ErrNoConfigEntry
		}
		if len(subsection.OptionAll(optionName)) > 1 {
			return "", ErrMultipleConfigEntry
		}
		return subsection.Option(optionName), nil
	}
}

func (cr *goGitConfigReader) ReadTimestamp(key string) (time.Time, error) {
	value, err := cr.ReadString(key)
	if err != nil {
		return time.Time{}, err
	}
	return ParseTimestamp(value)
}

var _ ConfigWrite = &goGitConfigWriter{}

// Only works for the local config as go-git only support that
type goGitConfigWriter struct {
	repo *gogit.Repository
}

func (cw *goGitConfigWriter) StoreString(key, value string) error {
	cfg, err := cw.repo.Config()
	if err != nil {
		return err
	}

	split := strings.Split(key, ".")

	switch {
	case len(split) <= 1:
		return fmt.Errorf("invalid key")
	case len(split) == 2:
		cfg.Raw.Section(split[0]).SetOption(split[1], value)
	default:
		section := split[0]
		subsection := strings.Join(split[1:len(split)-1], ".")
		option := split[len(split)-1]
		cfg.Raw.Section(section).Subsection(subsection).SetOption(option, value)
	}

	return cw.repo.SetConfig(cfg)
}

func (cw *goGitConfigWriter) StoreTimestamp(key string, value time.Time) error {
	return cw.StoreString(key, strconv.Itoa(int(value.Unix())))
}

func (cw *goGitConfigWriter) StoreBool(key string, value bool) error {
	return cw.StoreString(key, strconv.FormatBool(value))
}

func (cw *goGitConfigWriter) RemoveAll(keyPrefix string) error {
	cfg, err := cw.repo.Config()
	if err != nil {
		return err
	}

	split := strings.Split(keyPrefix, ".")

	switch {
	case keyPrefix == "":
		cfg.Raw.Sections = nil
		// warning: this does not actually remove everything as go-git config hold
		// some entries in multiple places (cfg.User ...)
	case len(split) == 1:
		if cfg.Raw.HasSection(split[0]) {
			cfg.Raw.RemoveSection(split[0])
		} else {
			return fmt.Errorf("invalid key prefix")
		}
	default:
		if !cfg.Raw.HasSection(split[0]) {
			return fmt.Errorf("invalid key prefix")
		}
		section := cfg.Raw.Section(split[0])
		rest := strings.Join(split[1:], ".")

		ok := false
		if section.HasSubsection(rest) {
			section.RemoveSubsection(rest)
			ok = true
		}
		if section.HasOption(rest) {
			section.RemoveOption(rest)
			ok = true
		}
		if !ok {
			return fmt.Errorf("invalid key prefix")
		}
	}

	return cw.repo.SetConfig(cfg)
}