You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

403 lines
8.9 KiB

  1. // Copyright 2021 The Hugo Authors. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package config
  14. import (
  15. "fmt"
  16. "sort"
  17. "strings"
  18. "sync"
  19. xmaps "golang.org/x/exp/maps"
  20. "github.com/spf13/cast"
  21. "github.com/gohugoio/hugo/common/maps"
  22. )
  23. var (
  24. // ConfigRootKeysSet contains all of the config map root keys.
  25. ConfigRootKeysSet = map[string]bool{
  26. "build": true,
  27. "caches": true,
  28. "cascade": true,
  29. "frontmatter": true,
  30. "languages": true,
  31. "imaging": true,
  32. "markup": true,
  33. "mediatypes": true,
  34. "menus": true,
  35. "minify": true,
  36. "module": true,
  37. "outputformats": true,
  38. "params": true,
  39. "permalinks": true,
  40. "related": true,
  41. "sitemap": true,
  42. "privacy": true,
  43. "security": true,
  44. "taxonomies": true,
  45. }
  46. // ConfigRootKeys is a sorted version of ConfigRootKeysSet.
  47. ConfigRootKeys []string
  48. )
  49. func init() {
  50. for k := range ConfigRootKeysSet {
  51. ConfigRootKeys = append(ConfigRootKeys, k)
  52. }
  53. sort.Strings(ConfigRootKeys)
  54. }
  55. // New creates a Provider backed by an empty maps.Params.
  56. func New() Provider {
  57. return &defaultConfigProvider{
  58. root: make(maps.Params),
  59. }
  60. }
  61. // NewFrom creates a Provider backed by params.
  62. func NewFrom(params maps.Params) Provider {
  63. maps.PrepareParams(params)
  64. return &defaultConfigProvider{
  65. root: params,
  66. }
  67. }
  68. // defaultConfigProvider is a Provider backed by a map where all keys are lower case.
  69. // All methods are thread safe.
  70. type defaultConfigProvider struct {
  71. mu sync.RWMutex
  72. root maps.Params
  73. keyCache sync.Map
  74. }
  75. func (c *defaultConfigProvider) Get(k string) any {
  76. if k == "" {
  77. return c.root
  78. }
  79. c.mu.RLock()
  80. key, m := c.getNestedKeyAndMap(strings.ToLower(k), false)
  81. if m == nil {
  82. c.mu.RUnlock()
  83. return nil
  84. }
  85. v := m[key]
  86. c.mu.RUnlock()
  87. return v
  88. }
  89. func (c *defaultConfigProvider) GetBool(k string) bool {
  90. v := c.Get(k)
  91. return cast.ToBool(v)
  92. }
  93. func (c *defaultConfigProvider) GetInt(k string) int {
  94. v := c.Get(k)
  95. return cast.ToInt(v)
  96. }
  97. func (c *defaultConfigProvider) IsSet(k string) bool {
  98. var found bool
  99. c.mu.RLock()
  100. key, m := c.getNestedKeyAndMap(strings.ToLower(k), false)
  101. if m != nil {
  102. _, found = m[key]
  103. }
  104. c.mu.RUnlock()
  105. return found
  106. }
  107. func (c *defaultConfigProvider) GetString(k string) string {
  108. v := c.Get(k)
  109. return cast.ToString(v)
  110. }
  111. func (c *defaultConfigProvider) GetParams(k string) maps.Params {
  112. v := c.Get(k)
  113. if v == nil {
  114. return nil
  115. }
  116. return v.(maps.Params)
  117. }
  118. func (c *defaultConfigProvider) GetStringMap(k string) map[string]any {
  119. v := c.Get(k)
  120. return maps.ToStringMap(v)
  121. }
  122. func (c *defaultConfigProvider) GetStringMapString(k string) map[string]string {
  123. v := c.Get(k)
  124. return maps.ToStringMapString(v)
  125. }
  126. func (c *defaultConfigProvider) GetStringSlice(k string) []string {
  127. v := c.Get(k)
  128. return cast.ToStringSlice(v)
  129. }
  130. func (c *defaultConfigProvider) Set(k string, v any) {
  131. c.mu.Lock()
  132. defer c.mu.Unlock()
  133. k = strings.ToLower(k)
  134. if k == "" {
  135. if p, err := maps.ToParamsAndPrepare(v); err == nil {
  136. // Set the values directly in root.
  137. maps.SetParams(c.root, p)
  138. } else {
  139. c.root[k] = v
  140. }
  141. return
  142. }
  143. switch vv := v.(type) {
  144. case map[string]any, map[any]any, map[string]string:
  145. p := maps.MustToParamsAndPrepare(vv)
  146. v = p
  147. }
  148. key, m := c.getNestedKeyAndMap(k, true)
  149. if m == nil {
  150. return
  151. }
  152. if existing, found := m[key]; found {
  153. if p1, ok := existing.(maps.Params); ok {
  154. if p2, ok := v.(maps.Params); ok {
  155. maps.SetParams(p1, p2)
  156. return
  157. }
  158. }
  159. }
  160. m[key] = v
  161. }
  162. // SetDefaults will set values from params if not already set.
  163. func (c *defaultConfigProvider) SetDefaults(params maps.Params) {
  164. maps.PrepareParams(params)
  165. for k, v := range params {
  166. if _, found := c.root[k]; !found {
  167. c.root[k] = v
  168. }
  169. }
  170. }
  171. func (c *defaultConfigProvider) Merge(k string, v any) {
  172. c.mu.Lock()
  173. defer c.mu.Unlock()
  174. k = strings.ToLower(k)
  175. if k == "" {
  176. rs, f := c.root.GetMergeStrategy()
  177. if f && rs == maps.ParamsMergeStrategyNone {
  178. // The user has set a "no merge" strategy on this,
  179. // nothing more to do.
  180. return
  181. }
  182. if p, err := maps.ToParamsAndPrepare(v); err == nil {
  183. // As there may be keys in p not in root, we need to handle
  184. // those as a special case.
  185. var keysToDelete []string
  186. for kk, vv := range p {
  187. if pp, ok := vv.(maps.Params); ok {
  188. if pppi, ok := c.root[kk]; ok {
  189. ppp := pppi.(maps.Params)
  190. maps.MergeParamsWithStrategy("", ppp, pp)
  191. } else {
  192. // We need to use the default merge strategy for
  193. // this key.
  194. np := make(maps.Params)
  195. strategy := c.determineMergeStrategy(maps.KeyParams{Key: "", Params: c.root}, maps.KeyParams{Key: kk, Params: np})
  196. np.SetMergeStrategy(strategy)
  197. maps.MergeParamsWithStrategy("", np, pp)
  198. c.root[kk] = np
  199. if np.IsZero() {
  200. // Just keep it until merge is done.
  201. keysToDelete = append(keysToDelete, kk)
  202. }
  203. }
  204. }
  205. }
  206. // Merge the rest.
  207. maps.MergeParams(c.root, p)
  208. for _, k := range keysToDelete {
  209. delete(c.root, k)
  210. }
  211. } else {
  212. panic(fmt.Sprintf("unsupported type %T received in Merge", v))
  213. }
  214. return
  215. }
  216. switch vv := v.(type) {
  217. case map[string]any, map[any]any, map[string]string:
  218. p := maps.MustToParamsAndPrepare(vv)
  219. v = p
  220. }
  221. key, m := c.getNestedKeyAndMap(k, true)
  222. if m == nil {
  223. return
  224. }
  225. if existing, found := m[key]; found {
  226. if p1, ok := existing.(maps.Params); ok {
  227. if p2, ok := v.(maps.Params); ok {
  228. maps.MergeParamsWithStrategy("", p1, p2)
  229. }
  230. }
  231. } else {
  232. m[key] = v
  233. }
  234. }
  235. func (c *defaultConfigProvider) Keys() []string {
  236. c.mu.RLock()
  237. defer c.mu.RUnlock()
  238. return xmaps.Keys(c.root)
  239. }
  240. func (c *defaultConfigProvider) WalkParams(walkFn func(params ...maps.KeyParams) bool) {
  241. var walk func(params ...maps.KeyParams)
  242. walk = func(params ...maps.KeyParams) {
  243. if walkFn(params...) {
  244. return
  245. }
  246. p1 := params[len(params)-1]
  247. i := len(params)
  248. for k, v := range p1.Params {
  249. if p2, ok := v.(maps.Params); ok {
  250. paramsplus1 := make([]maps.KeyParams, i+1)
  251. copy(paramsplus1, params)
  252. paramsplus1[i] = maps.KeyParams{Key: k, Params: p2}
  253. walk(paramsplus1...)
  254. }
  255. }
  256. }
  257. walk(maps.KeyParams{Key: "", Params: c.root})
  258. }
  259. func (c *defaultConfigProvider) determineMergeStrategy(params ...maps.KeyParams) maps.ParamsMergeStrategy {
  260. if len(params) == 0 {
  261. return maps.ParamsMergeStrategyNone
  262. }
  263. var (
  264. strategy maps.ParamsMergeStrategy
  265. prevIsRoot bool
  266. curr = params[len(params)-1]
  267. )
  268. if len(params) > 1 {
  269. prev := params[len(params)-2]
  270. prevIsRoot = prev.Key == ""
  271. // Inherit from parent (but not from the root unless it's set by user).
  272. s, found := prev.Params.GetMergeStrategy()
  273. if !prevIsRoot && !found {
  274. panic("invalid state, merge strategy not set on parent")
  275. }
  276. if found || !prevIsRoot {
  277. strategy = s
  278. }
  279. }
  280. switch curr.Key {
  281. case "":
  282. // Don't set a merge strategy on the root unless set by user.
  283. // This will be handled as a special case.
  284. case "params":
  285. strategy = maps.ParamsMergeStrategyDeep
  286. case "outputformats", "mediatypes":
  287. if prevIsRoot {
  288. strategy = maps.ParamsMergeStrategyShallow
  289. }
  290. case "menus":
  291. isMenuKey := prevIsRoot
  292. if !isMenuKey {
  293. // Can also be set below languages.
  294. // root > languages > en > menus
  295. if len(params) == 4 && params[1].Key == "languages" {
  296. isMenuKey = true
  297. }
  298. }
  299. if isMenuKey {
  300. strategy = maps.ParamsMergeStrategyShallow
  301. }
  302. default:
  303. if strategy == "" {
  304. strategy = maps.ParamsMergeStrategyNone
  305. }
  306. }
  307. return strategy
  308. }
  309. func (c *defaultConfigProvider) SetDefaultMergeStrategy() {
  310. c.WalkParams(func(params ...maps.KeyParams) bool {
  311. if len(params) == 0 {
  312. return false
  313. }
  314. p := params[len(params)-1].Params
  315. var found bool
  316. if _, found = p.GetMergeStrategy(); found {
  317. // Set by user.
  318. return false
  319. }
  320. strategy := c.determineMergeStrategy(params...)
  321. if strategy != "" {
  322. p.SetMergeStrategy(strategy)
  323. }
  324. return false
  325. })
  326. }
  327. func (c *defaultConfigProvider) getNestedKeyAndMap(key string, create bool) (string, maps.Params) {
  328. var parts []string
  329. v, ok := c.keyCache.Load(key)
  330. if ok {
  331. parts = v.([]string)
  332. } else {
  333. parts = strings.Split(key, ".")
  334. c.keyCache.Store(key, parts)
  335. }
  336. current := c.root
  337. for i := 0; i < len(parts)-1; i++ {
  338. next, found := current[parts[i]]
  339. if !found {
  340. if create {
  341. next = make(maps.Params)
  342. current[parts[i]] = next
  343. } else {
  344. return "", nil
  345. }
  346. }
  347. var ok bool
  348. current, ok = next.(maps.Params)
  349. if !ok {
  350. // E.g. a string, not a map that we can store values in.
  351. return "", nil
  352. }
  353. }
  354. return parts[len(parts)-1], current
  355. }