From 784eaa8d6f2f0822d41cb7ff3e4ed8c5bd1d7820 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Sat, 9 Mar 2019 20:06:14 +0800 Subject: [PATCH] Mark values with scope, such as system, global, self After mark values with scope, we can filter config values in order to show config variables of specific scope. Signed-off-by: Jiang Xin --- git-config.go | 99 +++++++++++++++++++++++++++++++++++++++++----- git-config_test.go | 56 +++++++++++++++++++++++++- goconfig.go | 2 +- load.go | 6 +-- 4 files changed, 149 insertions(+), 14 deletions(-) diff --git a/git-config.go b/git-config.go index 9ae3fd2..306334e 100644 --- a/git-config.go +++ b/git-config.go @@ -1,15 +1,69 @@ package goconfig import ( + "sort" "strconv" "strings" ) +// Scope is used to mark where config variable comes from +type Scope int + +// Define scopes for config variables +const ( + ScopeInclude = 1 << iota + ScopeSystem + ScopeGlobal + ScopeSelf + + ScopeAll = 0xFFFF + ScopeMask = ^ScopeInclude +) + +// String show user friendly display of scope +func (v *Scope) String() string { + i := int(*v) + inc := "" + if i&ScopeInclude == ScopeInclude { + inc = "-inc" + } + + if i&ScopeSystem == ScopeSystem { + return "system" + inc + } else if i&ScopeGlobal == ScopeGlobal { + return "global" + inc + } else if i&ScopeSelf == ScopeSelf { + return "self" + inc + } + return "unknown" + inc +} + // GitConfig maps section to key-value pairs type GitConfig map[string]GitConfigKeys // GitConfigKeys maps key to values -type GitConfigKeys map[string][]string +type GitConfigKeys map[string][]*GitConfigValue + +// GitConfigValue holds value and its scope +type GitConfigValue struct { + scope Scope + value string +} + +// Set is used to set value +func (v *GitConfigValue) Set(value string) { + v.value = value +} + +// Value is used to show value +func (v *GitConfigValue) Value() string { + return v.value +} + +// Scope is used to show user friendly scope +func (v *GitConfigValue) Scope() string { + return v.scope.String() +} // NewGitConfig returns GitConfig with initialized maps func NewGitConfig() GitConfig { @@ -25,6 +79,7 @@ func (v GitConfig) Keys() []string { allKeys = append(allKeys, s+"."+key) } } + sort.Strings(allKeys) return allKeys } @@ -42,9 +97,9 @@ func (v GitConfig) _add(section, key, value string) { } if _, ok := v[section][key]; !ok { - v[section][key] = []string{} + v[section][key] = []*GitConfigValue{} } - v[section][key] = append(v[section][key], value) + v[section][key] = append(v[section][key], &GitConfigValue{value: value}) } // Get value from key @@ -106,9 +161,25 @@ func (v GitConfig) GetUint64(key string, defaultValue uint64) (uint64, error) { func (v GitConfig) GetAll(key string) []string { section, key := toSectionKey(key) - keys := v[section] - if keys != nil { - return keys[key] + values := []string{} + + if v[section] != nil && v[section][key] != nil { + for _, value := range v[section][key] { + if value != nil { + values = append(values, value.value) + } + } + return values + } + return nil +} + +// GetRaw gets all values of a key +func (v GitConfig) GetRaw(key string) []*GitConfigValue { + section, key := toSectionKey(key) + + if v[section] != nil && v[section][key] != nil { + return v[section][key] } return nil } @@ -141,16 +212,26 @@ func toSectionKey(name string) (string, string) { // Merge will merge another GitConfig, and new value(s) of the same key will // append to the end of value list, and new value has higher priority. -func (v GitConfig) Merge(c GitConfig) GitConfig { +func (v GitConfig) Merge(c GitConfig, scope Scope) GitConfig { for sec, keys := range c { if _, ok := v[sec]; !ok { v[sec] = make(GitConfigKeys) } for key, values := range keys { if v[sec][key] == nil { - v[sec][key] = []string{} + v[sec][key] = []*GitConfigValue{} + } + for _, value := range values { + if value == nil { + continue + } + v[sec][key] = append(v[sec][key], + &GitConfigValue{ + scope: (value.scope & ^ScopeMask) | scope, + value: value.Value(), + }) + } - v[sec][key] = append(v[sec][key], values...) } } return v diff --git a/git-config_test.go b/git-config_test.go index ffb7127..4b6992d 100644 --- a/git-config_test.go +++ b/git-config_test.go @@ -1,6 +1,7 @@ package goconfig import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -182,7 +183,7 @@ func TestMerge(t *testing.T) { assert.Equal("other-c", cfg2.Get("a.c")) assert.Equal("other-d", cfg2.Get("a.d")) - cfg.Merge(cfg2) + cfg.Merge(cfg2, ScopeInclude) assert.Equal("value-b", cfg.Get("a.b")) assert.Equal("other-c", cfg.Get("a.c")) assert.Equal("other-d", cfg.Get("a.d")) @@ -191,3 +192,56 @@ func TestMerge(t *testing.T) { "other-c", }, cfg.GetAll("a.c")) } + +func TestScope(t *testing.T) { + assert := assert.New(t) + assert.Equal(0x1, ScopeInclude) + assert.Equal(^0x1, ScopeMask) +} + +func ExampleMerge() { + sys := NewGitConfig() + sys.Add("sect1.Name1", "value-1.1.1") + sys.Add("sect1.Name2", "value-1.1.2") + + inc1 := NewGitConfig() + inc1.Add("sect1.Name3", "value-0.1.3") + inc1.Add("sect2.name1", "value-0.2.1") + + sys.Merge(inc1, ScopeInclude) + + global := NewGitConfig() + global.Add("sect1.name2", "value-2.1.2") + global.Add("sect1.name3", "value-2.1.3") + global.Add("sect1.name4", "value-2.1.4") + global.Add("sect3.name1", "value-2.3.1") + + repo := NewGitConfig() + repo.Add("sect1.name2", "value-3.1.2") + repo.Add("sect1.name3", "value-3.1.3") + repo.Add("sect1.name4", "value-3.1.4") + + all := NewGitConfig() + all.Merge(sys, ScopeSystem) + all.Merge(global, ScopeGlobal) + all.Merge(repo, ScopeSelf) + + fmt.Println() + for _, k := range all.Keys() { + for _, value := range all.GetRaw(k) { + fmt.Printf("%s = %-8s (%s)\n", k, value.Value(), value.Scope()) + } + } + // Output: + // sect1.name1 = value-1.1.1 (system) + // sect1.name2 = value-1.1.2 (system) + // sect1.name2 = value-2.1.2 (global) + // sect1.name2 = value-3.1.2 (self) + // sect1.name3 = value-0.1.3 (system-inc) + // sect1.name3 = value-2.1.3 (global) + // sect1.name3 = value-3.1.3 (self) + // sect1.name4 = value-2.1.4 (global) + // sect1.name4 = value-3.1.4 (self) + // sect2.name1 = value-0.2.1 (system-inc) + // sect3.name1 = value-2.3.1 (global) +} diff --git a/goconfig.go b/goconfig.go index 67d0f6e..6858180 100644 --- a/goconfig.go +++ b/goconfig.go @@ -103,7 +103,7 @@ func (cf *parser) parse() (GitConfig, error) { if err != nil { return cfg, err } - cfg.Merge(config) + cfg.Merge(config, ScopeInclude) } } } diff --git a/load.go b/load.go index 7e24d17..30edce4 100644 --- a/load.go +++ b/load.go @@ -116,15 +116,15 @@ func loadAll(name string, searching bool) (GitConfig, error) { } if sysConfig != nil { - cfg.Merge(sysConfig) + cfg.Merge(sysConfig, ScopeSystem) } if globalConfig != nil { - cfg.Merge(globalConfig) + cfg.Merge(globalConfig, ScopeGlobal) } if repoConfig != nil { - cfg.Merge(repoConfig) + cfg.Merge(repoConfig, ScopeSelf) } return cfg, nil