diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-01-04 18:24:36 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-05-16 18:01:29 +0200 |
commit | 241b21b0fd34d91fccb2ce69874110dceae6f926 (patch) | |
tree | d4e0118eac7e9c42f065815447a70805f8d6ad3e /common/maps | |
parent | 6aededf6b42011c3039f5f66487a89a8dd65e0e7 (diff) |
Create a struct with all of Hugo's config options
Primary motivation is documentation, but it will also hopefully simplify the code.
Also,
* Lower case the default output format names; this is in line with the custom ones (map keys) and how
it's treated all the places. This avoids doing `stringds.EqualFold` everywhere.
Closes #10896
Closes #10620
Diffstat (limited to 'common/maps')
-rw-r--r-- | common/maps/maps.go | 35 | ||||
-rw-r--r-- | common/maps/maps_test.go | 8 | ||||
-rw-r--r-- | common/maps/params.go | 98 | ||||
-rw-r--r-- | common/maps/params_test.go | 16 |
4 files changed, 117 insertions, 40 deletions
diff --git a/common/maps/maps.go b/common/maps/maps.go index 2d8a122ca..6aefde927 100644 --- a/common/maps/maps.go +++ b/common/maps/maps.go @@ -43,25 +43,25 @@ func ToStringMapE(in any) (map[string]any, error) { // ToParamsAndPrepare converts in to Params and prepares it for use. // If in is nil, an empty map is returned. // See PrepareParams. -func ToParamsAndPrepare(in any) (Params, bool) { +func ToParamsAndPrepare(in any) (Params, error) { if types.IsNil(in) { - return Params{}, true + return Params{}, nil } m, err := ToStringMapE(in) if err != nil { - return nil, false + return nil, err } PrepareParams(m) - return m, true + return m, nil } // MustToParamsAndPrepare calls ToParamsAndPrepare and panics if it fails. func MustToParamsAndPrepare(in any) Params { - if p, ok := ToParamsAndPrepare(in); ok { - return p - } else { - panic(fmt.Sprintf("cannot convert %T to maps.Params", in)) + p, err := ToParamsAndPrepare(in) + if err != nil { + panic(fmt.Sprintf("cannot convert %T to maps.Params: %s", in, err)) } + return p } // ToStringMap converts in to map[string]interface{}. @@ -96,6 +96,8 @@ func ToSliceStringMap(in any) ([]map[string]any, error) { switch v := in.(type) { case []map[string]any: return v, nil + case Params: + return []map[string]any{v}, nil case []any: var s []map[string]any for _, entry := range v { @@ -123,6 +125,23 @@ func LookupEqualFold[T any | string](m map[string]T, key string) (T, bool) { return s, false } +// MergeShallow merges src into dst, but only if the key does not already exist in dst. +// The keys are compared case insensitively. +func MergeShallow(dst, src map[string]any) { + for k, v := range src { + found := false + for dk := range dst { + if strings.EqualFold(dk, k) { + found = true + break + } + } + if !found { + dst[k] = v + } + } +} + type keyRename struct { pattern glob.Glob newKey string diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index 0b84d2dd7..0e8589d34 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -116,11 +116,11 @@ func TestToSliceStringMap(t *testing.T) { func TestToParamsAndPrepare(t *testing.T) { c := qt.New(t) - _, ok := ToParamsAndPrepare(map[string]any{"A": "av"}) - c.Assert(ok, qt.IsTrue) + _, err := ToParamsAndPrepare(map[string]any{"A": "av"}) + c.Assert(err, qt.IsNil) - params, ok := ToParamsAndPrepare(nil) - c.Assert(ok, qt.IsTrue) + params, err := ToParamsAndPrepare(nil) + c.Assert(err, qt.IsNil) c.Assert(params, qt.DeepEquals, Params{}) } diff --git a/common/maps/params.go b/common/maps/params.go index 4bf95f43b..eb60fbbfc 100644 --- a/common/maps/params.go +++ b/common/maps/params.go @@ -23,30 +23,37 @@ import ( // Params is a map where all keys are lower case. type Params map[string]any -// Get does a lower case and nested search in this map. +// KeyParams is an utility struct for the WalkParams method. +type KeyParams struct { + Key string + Params Params +} + +// GetNested does a lower case and nested search in this map. // It will return nil if none found. -func (p Params) Get(indices ...string) any { +// Make all of these methods internal somehow. +func (p Params) GetNested(indices ...string) any { v, _, _ := getNested(p, indices) return v } -// Set overwrites values in p with values in pp for common or new keys. +// Set overwrites values in dst with values in src for common or new keys. // This is done recursively. -func (p Params) Set(pp Params) { - for k, v := range pp { - vv, found := p[k] +func SetParams(dst, src Params) { + for k, v := range src { + vv, found := dst[k] if !found { - p[k] = v + dst[k] = v } else { switch vvv := vv.(type) { case Params: if pv, ok := v.(Params); ok { - vvv.Set(pv) + SetParams(vvv, pv) } else { - p[k] = v + dst[k] = v } default: - p[k] = v + dst[k] = v } } } @@ -70,18 +77,17 @@ func (p Params) IsZero() bool { } -// Merge transfers values from pp to p for new keys. +// MergeParamsWithStrategy transfers values from src to dst for new keys using the merge strategy given. // This is done recursively. -func (p Params) Merge(pp Params) { - p.merge("", pp) +func MergeParamsWithStrategy(strategy string, dst, src Params) { + dst.merge(ParamsMergeStrategy(strategy), src) } -// MergeRoot transfers values from pp to p for new keys where p is the -// root of the tree. +// MergeParamsWithStrategy transfers values from src to dst for new keys using the merge encoded in dst. // This is done recursively. -func (p Params) MergeRoot(pp Params) { - ms, _ := p.GetMergeStrategy() - p.merge(ms, pp) +func MergeParams(dst, src Params) { + ms, _ := dst.GetMergeStrategy() + dst.merge(ms, src) } func (p Params) merge(ps ParamsMergeStrategy, pp Params) { @@ -116,6 +122,7 @@ func (p Params) merge(ps ParamsMergeStrategy, pp Params) { } } +// For internal use. func (p Params) GetMergeStrategy() (ParamsMergeStrategy, bool) { if v, found := p[mergeStrategyKey]; found { if s, ok := v.(ParamsMergeStrategy); ok { @@ -125,6 +132,7 @@ func (p Params) GetMergeStrategy() (ParamsMergeStrategy, bool) { return ParamsMergeStrategyShallow, false } +// For internal use. func (p Params) DeleteMergeStrategy() bool { if _, found := p[mergeStrategyKey]; found { delete(p, mergeStrategyKey) @@ -133,7 +141,8 @@ func (p Params) DeleteMergeStrategy() bool { return false } -func (p Params) SetDefaultMergeStrategy(s ParamsMergeStrategy) { +// For internal use. +func (p Params) SetMergeStrategy(s ParamsMergeStrategy) { switch s { case ParamsMergeStrategyDeep, ParamsMergeStrategyNone, ParamsMergeStrategyShallow: default: @@ -187,7 +196,7 @@ func GetNestedParam(keyStr, separator string, candidates ...Params) (any, error) keySegments := strings.Split(keyStr, separator) for _, m := range candidates { - if v := m.Get(keySegments...); v != nil { + if v := m.GetNested(keySegments...); v != nil { return v, nil } } @@ -236,6 +245,55 @@ const ( mergeStrategyKey = "_merge" ) +// CleanConfigStringMapString removes any processing instructions from m, +// m will never be modified. +func CleanConfigStringMapString(m map[string]string) map[string]string { + if m == nil || len(m) == 0 { + return m + } + if _, found := m[mergeStrategyKey]; !found { + return m + } + // Create a new map and copy all the keys except the merge strategy key. + m2 := make(map[string]string, len(m)-1) + for k, v := range m { + if k != mergeStrategyKey { + m2[k] = v + } + } + return m2 +} + +// CleanConfigStringMap is the same as CleanConfigStringMapString but for +// map[string]any. +func CleanConfigStringMap(m map[string]any) map[string]any { + if m == nil || len(m) == 0 { + return m + } + if _, found := m[mergeStrategyKey]; !found { + return m + } + // Create a new map and copy all the keys except the merge strategy key. + m2 := make(map[string]any, len(m)-1) + for k, v := range m { + if k != mergeStrategyKey { + m2[k] = v + } + switch v2 := v.(type) { + case map[string]any: + m2[k] = CleanConfigStringMap(v2) + case Params: + var p Params = CleanConfigStringMap(v2) + m2[k] = p + case map[string]string: + m2[k] = CleanConfigStringMapString(v2) + } + + } + return m2 + +} + func toMergeStrategy(v any) ParamsMergeStrategy { s := ParamsMergeStrategy(cast.ToString(v)) switch s { diff --git a/common/maps/params_test.go b/common/maps/params_test.go index a070e6f60..7e1dbbae7 100644 --- a/common/maps/params_test.go +++ b/common/maps/params_test.go @@ -81,7 +81,7 @@ func TestParamsSetAndMerge(t *testing.T) { p1, p2 := createParamsPair() - p1.Set(p2) + SetParams(p1, p2) c.Assert(p1, qt.DeepEquals, Params{ "a": "abv", @@ -97,7 +97,7 @@ func TestParamsSetAndMerge(t *testing.T) { p1, p2 = createParamsPair() - p1.Merge(p2) + MergeParamsWithStrategy("", p1, p2) // Default is to do a shallow merge. c.Assert(p1, qt.DeepEquals, Params{ @@ -111,8 +111,8 @@ func TestParamsSetAndMerge(t *testing.T) { }) p1, p2 = createParamsPair() - p1.SetDefaultMergeStrategy(ParamsMergeStrategyNone) - p1.Merge(p2) + p1.SetMergeStrategy(ParamsMergeStrategyNone) + MergeParamsWithStrategy("", p1, p2) p1.DeleteMergeStrategy() c.Assert(p1, qt.DeepEquals, Params{ @@ -125,8 +125,8 @@ func TestParamsSetAndMerge(t *testing.T) { }) p1, p2 = createParamsPair() - p1.SetDefaultMergeStrategy(ParamsMergeStrategyShallow) - p1.Merge(p2) + p1.SetMergeStrategy(ParamsMergeStrategyShallow) + MergeParamsWithStrategy("", p1, p2) p1.DeleteMergeStrategy() c.Assert(p1, qt.DeepEquals, Params{ @@ -140,8 +140,8 @@ func TestParamsSetAndMerge(t *testing.T) { }) p1, p2 = createParamsPair() - p1.SetDefaultMergeStrategy(ParamsMergeStrategyDeep) - p1.Merge(p2) + p1.SetMergeStrategy(ParamsMergeStrategyDeep) + MergeParamsWithStrategy("", p1, p2) p1.DeleteMergeStrategy() c.Assert(p1, qt.DeepEquals, Params{ |