github.com/SuCicada/su-hugo@v1.0.0/hugolib/page__meta.go (about) 1 // Copyright 2019 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 14 package hugolib 15 16 import ( 17 "fmt" 18 "path" 19 "path/filepath" 20 "regexp" 21 "strings" 22 "sync" 23 "time" 24 25 "github.com/gohugoio/hugo/langs" 26 27 "github.com/gobuffalo/flect" 28 "github.com/gohugoio/hugo/markup/converter" 29 30 "github.com/gohugoio/hugo/hugofs/files" 31 32 "github.com/gohugoio/hugo/common/hugo" 33 34 "github.com/gohugoio/hugo/related" 35 36 "github.com/gohugoio/hugo/source" 37 38 "github.com/gohugoio/hugo/common/maps" 39 "github.com/gohugoio/hugo/config" 40 "github.com/gohugoio/hugo/helpers" 41 42 "github.com/gohugoio/hugo/output" 43 "github.com/gohugoio/hugo/resources/page" 44 "github.com/gohugoio/hugo/resources/page/pagemeta" 45 "github.com/gohugoio/hugo/resources/resource" 46 "github.com/spf13/cast" 47 ) 48 49 var cjkRe = regexp.MustCompile(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`) 50 51 type pageMeta struct { 52 // kind is the discriminator that identifies the different page types 53 // in the different page collections. This can, as an example, be used 54 // to to filter regular pages, find sections etc. 55 // Kind will, for the pages available to the templates, be one of: 56 // page, home, section, taxonomy and term. 57 // It is of string type to make it easy to reason about in 58 // the templates. 59 kind string 60 61 // This is a standalone page not part of any page collection. These 62 // include sitemap, robotsTXT and similar. It will have no pageOutputs, but 63 // a fixed pageOutput. 64 standalone bool 65 66 draft bool // Only published when running with -D flag 67 buildConfig pagemeta.BuildConfig 68 69 bundleType files.ContentClass 70 71 // Params contains configuration defined in the params section of page frontmatter. 72 params map[string]any 73 74 title string 75 linkTitle string 76 77 summary string 78 79 resourcePath string 80 81 weight int 82 83 markup string 84 contentType string 85 86 // whether the content is in a CJK language. 87 isCJKLanguage bool 88 89 layout string 90 91 aliases []string 92 93 description string 94 keywords []string 95 96 urlPaths pagemeta.URLPath 97 98 resource.Dates 99 100 // Set if this page is bundled inside another. 101 bundled bool 102 103 // A key that maps to translation(s) of this page. This value is fetched 104 // from the page front matter. 105 translationKey string 106 107 // From front matter. 108 configuredOutputFormats output.Formats 109 110 // This is the raw front matter metadata that is going to be assigned to 111 // the Resources above. 112 resourcesMetadata []map[string]any 113 114 f source.File 115 116 sections []string 117 118 // Sitemap overrides from front matter. 119 sitemap config.Sitemap 120 121 s *Site 122 123 contentConverterInit sync.Once 124 contentConverter converter.Converter 125 } 126 127 func (p *pageMeta) Aliases() []string { 128 return p.aliases 129 } 130 131 func (p *pageMeta) Author() page.Author { 132 helpers.Deprecated(".Author", "Use taxonomies.", false) 133 authors := p.Authors() 134 135 for _, author := range authors { 136 return author 137 } 138 return page.Author{} 139 } 140 141 func (p *pageMeta) Authors() page.AuthorList { 142 helpers.Deprecated(".Authors", "Use taxonomies.", false) 143 authorKeys, ok := p.params["authors"] 144 if !ok { 145 return page.AuthorList{} 146 } 147 authors := authorKeys.([]string) 148 if len(authors) < 1 || len(p.s.Info.Authors) < 1 { 149 return page.AuthorList{} 150 } 151 152 al := make(page.AuthorList) 153 for _, author := range authors { 154 a, ok := p.s.Info.Authors[author] 155 if ok { 156 al[author] = a 157 } 158 } 159 return al 160 } 161 162 func (p *pageMeta) BundleType() files.ContentClass { 163 return p.bundleType 164 } 165 166 func (p *pageMeta) Description() string { 167 return p.description 168 } 169 170 func (p *pageMeta) Lang() string { 171 return p.s.Lang() 172 } 173 174 func (p *pageMeta) Draft() bool { 175 return p.draft 176 } 177 178 func (p *pageMeta) File() source.File { 179 return p.f 180 } 181 182 func (p *pageMeta) IsHome() bool { 183 return p.Kind() == page.KindHome 184 } 185 186 func (p *pageMeta) Keywords() []string { 187 return p.keywords 188 } 189 190 func (p *pageMeta) Kind() string { 191 return p.kind 192 } 193 194 func (p *pageMeta) Layout() string { 195 return p.layout 196 } 197 198 func (p *pageMeta) LinkTitle() string { 199 if p.linkTitle != "" { 200 return p.linkTitle 201 } 202 203 return p.Title() 204 } 205 206 func (p *pageMeta) Name() string { 207 if p.resourcePath != "" { 208 return p.resourcePath 209 } 210 return p.Title() 211 } 212 213 func (p *pageMeta) IsNode() bool { 214 return !p.IsPage() 215 } 216 217 func (p *pageMeta) IsPage() bool { 218 return p.Kind() == page.KindPage 219 } 220 221 // Param is a convenience method to do lookups in Page's and Site's Params map, 222 // in that order. 223 // 224 // This method is also implemented on SiteInfo. 225 // TODO(bep) interface 226 func (p *pageMeta) Param(key any) (any, error) { 227 return resource.Param(p, p.s.Info.Params(), key) 228 } 229 230 func (p *pageMeta) Params() maps.Params { 231 return p.params 232 } 233 234 func (p *pageMeta) Path() string { 235 if !p.File().IsZero() { 236 const example = ` 237 {{ $path := "" }} 238 {{ with .File }} 239 {{ $path = .Path }} 240 {{ else }} 241 {{ $path = .Path }} 242 {{ end }} 243 ` 244 helpers.Deprecated(".Path when the page is backed by a file", "We plan to use Path for a canonical source path and you probably want to check the source is a file. To get the current behaviour, you can use a construct similar to the one below:\n"+example, false) 245 246 } 247 248 return p.Pathc() 249 } 250 251 // This is just a bridge method, use Path in templates. 252 func (p *pageMeta) Pathc() string { 253 if !p.File().IsZero() { 254 return p.File().Path() 255 } 256 return p.SectionsPath() 257 } 258 259 // RelatedKeywords implements the related.Document interface needed for fast page searches. 260 func (p *pageMeta) RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error) { 261 v, err := p.Param(cfg.Name) 262 if err != nil { 263 return nil, err 264 } 265 266 return cfg.ToKeywords(v) 267 } 268 269 func (p *pageMeta) IsSection() bool { 270 return p.Kind() == page.KindSection 271 } 272 273 func (p *pageMeta) Section() string { 274 if p.IsHome() { 275 return "" 276 } 277 278 if p.IsNode() { 279 if len(p.sections) == 0 { 280 // May be a sitemap or similar. 281 return "" 282 } 283 return p.sections[0] 284 } 285 286 if !p.File().IsZero() { 287 return p.File().Section() 288 } 289 290 panic("invalid page state") 291 } 292 293 func (p *pageMeta) SectionsEntries() []string { 294 return p.sections 295 } 296 297 func (p *pageMeta) SectionsPath() string { 298 return path.Join(p.SectionsEntries()...) 299 } 300 301 func (p *pageMeta) Sitemap() config.Sitemap { 302 return p.sitemap 303 } 304 305 func (p *pageMeta) Title() string { 306 return p.title 307 } 308 309 const defaultContentType = "page" 310 311 func (p *pageMeta) Type() string { 312 if p.contentType != "" { 313 return p.contentType 314 } 315 316 if sect := p.Section(); sect != "" { 317 return sect 318 } 319 320 return defaultContentType 321 } 322 323 func (p *pageMeta) Weight() int { 324 return p.weight 325 } 326 327 func (pm *pageMeta) mergeBucketCascades(b1, b2 *pagesMapBucket) { 328 if b1.cascade == nil { 329 b1.cascade = make(map[page.PageMatcher]maps.Params) 330 } 331 332 if b2 != nil && b2.cascade != nil { 333 for k, v := range b2.cascade { 334 335 vv, found := b1.cascade[k] 336 if !found { 337 b1.cascade[k] = v 338 } else { 339 // Merge 340 for ck, cv := range v { 341 if _, found := vv[ck]; !found { 342 vv[ck] = cv 343 } 344 } 345 } 346 } 347 } 348 } 349 func (pm *pageMeta) setTagsToMetadata(frontmatter map[string]any) { 350 defer func() { 351 if r := recover(); r != nil { 352 fmt.Println("Recovered from error:", r) 353 } 354 }() 355 356 file := pm.File() 357 if len(file.Dir()) > 0 { 358 var dirTagsPath string 359 if strings.HasPrefix(file.Dir(), file.Section()) { 360 dirTagsPath = file.Dir()[len(file.Section())+1:] 361 var dirTags []interface{} 362 for _, s := range strings.Split(dirTagsPath, "/") { 363 if len(s) > 0 { 364 dirTags = append(dirTags, s) 365 } 366 } 367 if dirTags != nil { 368 if tags, ok := frontmatter["tags"]; !ok { 369 frontmatter["tags"] = dirTags 370 } else { 371 switch tags.(type) { 372 case []interface{}: 373 frontmatter["tags"] = append(tags.([]interface{}), dirTags...) 374 case interface{}: 375 frontmatter["tags"] = []interface{}{tags, dirTags} 376 } 377 } 378 //fmt.Println("frontmatter:", frontmatter) 379 } 380 } 381 } 382 } 383 384 func (pm *pageMeta) setTitleToMetadata() { 385 if pm.title == "" { 386 pm.title = pm.File().ContentBaseName() 387 } 388 } 389 func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, frontmatter map[string]any) error { 390 pm.params = make(maps.Params) 391 392 if frontmatter == nil && (parentBucket == nil || parentBucket.cascade == nil) { 393 return nil 394 } 395 396 if frontmatter != nil { 397 // Needed for case insensitive fetching of params values 398 maps.PrepareParams(frontmatter) 399 if p.bucket != nil { 400 // Check for any cascade define on itself. 401 if cv, found := frontmatter["cascade"]; found { 402 var err error 403 p.bucket.cascade, err = page.DecodeCascade(cv) 404 if err != nil { 405 return err 406 } 407 } 408 } 409 } else { 410 frontmatter = make(map[string]any) 411 } 412 413 var cascade map[page.PageMatcher]maps.Params 414 415 if p.bucket != nil { 416 if parentBucket != nil { 417 // Merge missing keys from parent into this. 418 pm.mergeBucketCascades(p.bucket, parentBucket) 419 } 420 cascade = p.bucket.cascade 421 } else if parentBucket != nil { 422 cascade = parentBucket.cascade 423 } 424 425 for m, v := range cascade { 426 if !m.Matches(p) { 427 continue 428 } 429 for kk, vv := range v { 430 if _, found := frontmatter[kk]; !found { 431 frontmatter[kk] = vv 432 } 433 } 434 } 435 436 var mtime time.Time 437 var contentBaseName string 438 if !p.File().IsZero() { 439 contentBaseName = p.File().ContentBaseName() 440 if p.File().FileInfo() != nil { 441 mtime = p.File().FileInfo().ModTime() 442 } 443 } 444 445 var gitAuthorDate time.Time 446 if p.gitInfo != nil { 447 gitAuthorDate = p.gitInfo.AuthorDate 448 } 449 450 descriptor := &pagemeta.FrontMatterDescriptor{ 451 Frontmatter: frontmatter, 452 Params: pm.params, 453 Dates: &pm.Dates, 454 PageURLs: &pm.urlPaths, 455 BaseFilename: contentBaseName, 456 ModTime: mtime, 457 GitAuthorDate: gitAuthorDate, 458 Location: langs.GetLocation(pm.s.Language()), 459 } 460 461 // Handle the date separately 462 // TODO(bep) we need to "do more" in this area so this can be split up and 463 // more easily tested without the Page, but the coupling is strong. 464 err := pm.s.frontmatterHandler.HandleDates(descriptor) 465 if err != nil { 466 p.s.Log.Errorf("Failed to handle dates for page %q: %s", p.pathOrTitle(), err) 467 } 468 469 pm.buildConfig, err = pagemeta.DecodeBuildConfig(frontmatter["_build"]) 470 if err != nil { 471 return err 472 } 473 474 var sitemapSet bool 475 476 var draft, published, isCJKLanguage *bool 477 478 pm.setTagsToMetadata(frontmatter) 479 480 for k, v := range frontmatter { 481 loki := strings.ToLower(k) 482 483 if loki == "published" { // Intentionally undocumented 484 vv, err := cast.ToBoolE(v) 485 if err == nil { 486 published = &vv 487 } 488 // published may also be a date 489 continue 490 } 491 492 if pm.s.frontmatterHandler.IsDateKey(loki) { 493 continue 494 } 495 496 switch loki { 497 case "title": 498 pm.title = cast.ToString(v) 499 pm.params[loki] = pm.title 500 case "linktitle": 501 pm.linkTitle = cast.ToString(v) 502 pm.params[loki] = pm.linkTitle 503 case "summary": 504 pm.summary = cast.ToString(v) 505 pm.params[loki] = pm.summary 506 case "description": 507 pm.description = cast.ToString(v) 508 pm.params[loki] = pm.description 509 case "slug": 510 // Don't start or end with a - 511 pm.urlPaths.Slug = strings.Trim(cast.ToString(v), "-") 512 pm.params[loki] = pm.Slug() 513 case "url": 514 url := cast.ToString(v) 515 if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") { 516 return fmt.Errorf("URLs with protocol (http*) not supported: %q. In page %q", url, p.pathOrTitle()) 517 } 518 lang := p.s.GetLanguagePrefix() 519 if lang != "" && !strings.HasPrefix(url, "/") && strings.HasPrefix(url, lang+"/") { 520 if strings.HasPrefix(hugo.CurrentVersion.String(), "0.55") { 521 // We added support for page relative URLs in Hugo 0.55 and 522 // this may get its language path added twice. 523 // TODO(bep) eventually remove this. 524 p.s.Log.Warnf(`Front matter in %q with the url %q with no leading / has what looks like the language prefix added. In Hugo 0.55 we added support for page relative URLs in front matter, no language prefix needed. Check the URL and consider to either add a leading / or remove the language prefix.`, p.pathOrTitle(), url) 525 } 526 } 527 pm.urlPaths.URL = url 528 pm.params[loki] = url 529 case "type": 530 pm.contentType = cast.ToString(v) 531 pm.params[loki] = pm.contentType 532 case "keywords": 533 pm.keywords = cast.ToStringSlice(v) 534 pm.params[loki] = pm.keywords 535 case "headless": 536 // Legacy setting for leaf bundles. 537 // This is since Hugo 0.63 handled in a more general way for all 538 // pages. 539 isHeadless := cast.ToBool(v) 540 pm.params[loki] = isHeadless 541 if p.File().TranslationBaseName() == "index" && isHeadless { 542 pm.buildConfig.List = pagemeta.Never 543 pm.buildConfig.Render = pagemeta.Never 544 } 545 case "outputs": 546 o := cast.ToStringSlice(v) 547 if len(o) > 0 { 548 // Output formats are explicitly set in front matter, use those. 549 outFormats, err := p.s.outputFormatsConfig.GetByNames(o...) 550 551 if err != nil { 552 p.s.Log.Errorf("Failed to resolve output formats: %s", err) 553 } else { 554 pm.configuredOutputFormats = outFormats 555 pm.params[loki] = outFormats 556 } 557 558 } 559 case "draft": 560 draft = new(bool) 561 *draft = cast.ToBool(v) 562 case "layout": 563 pm.layout = cast.ToString(v) 564 pm.params[loki] = pm.layout 565 case "markup": 566 pm.markup = cast.ToString(v) 567 pm.params[loki] = pm.markup 568 case "weight": 569 pm.weight = cast.ToInt(v) 570 pm.params[loki] = pm.weight 571 case "aliases": 572 pm.aliases = cast.ToStringSlice(v) 573 for i, alias := range pm.aliases { 574 if strings.HasPrefix(alias, "http://") || strings.HasPrefix(alias, "https://") { 575 return fmt.Errorf("http* aliases not supported: %q", alias) 576 } 577 pm.aliases[i] = filepath.ToSlash(alias) 578 } 579 pm.params[loki] = pm.aliases 580 case "sitemap": 581 p.m.sitemap = config.DecodeSitemap(p.s.siteCfg.sitemap, maps.ToStringMap(v)) 582 pm.params[loki] = p.m.sitemap 583 sitemapSet = true 584 case "iscjklanguage": 585 isCJKLanguage = new(bool) 586 *isCJKLanguage = cast.ToBool(v) 587 case "translationkey": 588 pm.translationKey = cast.ToString(v) 589 pm.params[loki] = pm.translationKey 590 case "resources": 591 var resources []map[string]any 592 handled := true 593 594 switch vv := v.(type) { 595 case []map[any]any: 596 for _, vvv := range vv { 597 resources = append(resources, maps.ToStringMap(vvv)) 598 } 599 case []map[string]any: 600 resources = append(resources, vv...) 601 case []any: 602 for _, vvv := range vv { 603 switch vvvv := vvv.(type) { 604 case map[any]any: 605 resources = append(resources, maps.ToStringMap(vvvv)) 606 case map[string]any: 607 resources = append(resources, vvvv) 608 } 609 } 610 default: 611 handled = false 612 } 613 614 if handled { 615 pm.params[loki] = resources 616 pm.resourcesMetadata = resources 617 break 618 } 619 fallthrough 620 621 default: 622 // If not one of the explicit values, store in Params 623 switch vv := v.(type) { 624 case bool: 625 pm.params[loki] = vv 626 case string: 627 pm.params[loki] = vv 628 case int64, int32, int16, int8, int: 629 pm.params[loki] = vv 630 case float64, float32: 631 pm.params[loki] = vv 632 case time.Time: 633 pm.params[loki] = vv 634 default: // handle array of strings as well 635 switch vvv := vv.(type) { 636 case []any: 637 if len(vvv) > 0 { 638 switch vvv[0].(type) { 639 case map[any]any: 640 pm.params[loki] = vvv 641 case map[string]any: 642 pm.params[loki] = vvv 643 case []any: 644 pm.params[loki] = vvv 645 default: 646 a := make([]string, len(vvv)) 647 for i, u := range vvv { 648 a[i] = cast.ToString(u) 649 } 650 651 pm.params[loki] = a 652 } 653 } else { 654 pm.params[loki] = []string{} 655 } 656 default: 657 pm.params[loki] = vv 658 } 659 } 660 } 661 } 662 663 // custom metadata settings 664 pm.setTitleToMetadata() 665 666 fmt.Println("frontmatter", frontmatter) 667 if !sitemapSet { 668 pm.sitemap = p.s.siteCfg.sitemap 669 } 670 671 pm.markup = p.s.ContentSpec.ResolveMarkup(pm.markup) 672 673 if draft != nil && published != nil { 674 pm.draft = *draft 675 p.m.s.Log.Warnf("page %q has both draft and published settings in its frontmatter. Using draft.", p.File().Filename()) 676 } else if draft != nil { 677 pm.draft = *draft 678 } else if published != nil { 679 pm.draft = !*published 680 } 681 pm.params["draft"] = pm.draft 682 683 if isCJKLanguage != nil { 684 pm.isCJKLanguage = *isCJKLanguage 685 } else if p.s.siteCfg.hasCJKLanguage && p.source.parsed != nil { 686 if cjkRe.Match(p.source.parsed.Input()) { 687 pm.isCJKLanguage = true 688 } else { 689 pm.isCJKLanguage = false 690 } 691 } 692 693 pm.params["iscjklanguage"] = p.m.isCJKLanguage 694 695 return nil 696 } 697 698 func (p *pageMeta) noListAlways() bool { 699 return p.buildConfig.List != pagemeta.Always 700 } 701 702 func (p *pageMeta) getListFilter(local bool) contentTreeNodeCallback { 703 return newContentTreeFilter(func(n *contentNode) bool { 704 if n == nil { 705 return true 706 } 707 708 var shouldList bool 709 switch n.p.m.buildConfig.List { 710 case pagemeta.Always: 711 shouldList = true 712 case pagemeta.Never: 713 shouldList = false 714 case pagemeta.ListLocally: 715 shouldList = local 716 } 717 718 return !shouldList 719 }) 720 } 721 722 func (p *pageMeta) noRender() bool { 723 return p.buildConfig.Render != pagemeta.Always 724 } 725 726 func (p *pageMeta) noLink() bool { 727 return p.buildConfig.Render == pagemeta.Never 728 } 729 730 func (p *pageMeta) applyDefaultValues(n *contentNode) error { 731 if p.buildConfig.IsZero() { 732 p.buildConfig, _ = pagemeta.DecodeBuildConfig(nil) 733 } 734 735 if !p.s.isEnabled(p.Kind()) { 736 (&p.buildConfig).Disable() 737 } 738 739 if p.markup == "" { 740 if !p.File().IsZero() { 741 // Fall back to file extension 742 p.markup = p.s.ContentSpec.ResolveMarkup(p.File().Ext()) 743 } 744 if p.markup == "" { 745 p.markup = "markdown" 746 } 747 } 748 749 if p.title == "" && p.f.IsZero() { 750 switch p.Kind() { 751 case page.KindHome: 752 p.title = p.s.Info.title 753 case page.KindSection: 754 var sectionName string 755 if n != nil { 756 sectionName = n.rootSection() 757 } else { 758 sectionName = p.sections[0] 759 } 760 761 sectionName = helpers.FirstUpper(sectionName) 762 if p.s.Cfg.GetBool("pluralizeListTitles") { 763 p.title = flect.Pluralize(sectionName) 764 } else { 765 p.title = sectionName 766 } 767 case page.KindTerm: 768 // TODO(bep) improve 769 key := p.sections[len(p.sections)-1] 770 p.title = strings.Replace(p.s.titleFunc(key), "-", " ", -1) 771 case page.KindTaxonomy: 772 p.title = p.s.titleFunc(p.sections[0]) 773 case kind404: 774 p.title = "404 Page not found" 775 776 } 777 } 778 779 if p.IsNode() { 780 p.bundleType = files.ContentClassBranch 781 } else { 782 source := p.File() 783 if fi, ok := source.(*fileInfo); ok { 784 class := fi.FileInfo().Meta().Classifier 785 switch class { 786 case files.ContentClassBranch, files.ContentClassLeaf: 787 p.bundleType = class 788 } 789 } 790 } 791 792 return nil 793 } 794 795 func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.Converter, error) { 796 if ps == nil { 797 panic("no Page provided") 798 } 799 cp := p.s.ContentSpec.Converters.Get(markup) 800 if cp == nil { 801 return converter.NopConverter, fmt.Errorf("no content renderer found for markup %q", p.markup) 802 } 803 804 var id string 805 var filename string 806 var path string 807 if !p.f.IsZero() { 808 id = p.f.UniqueID() 809 filename = p.f.Filename() 810 path = p.f.Path() 811 } else { 812 path = p.Pathc() 813 } 814 815 cpp, err := cp.New( 816 converter.DocumentContext{ 817 Document: newPageForRenderHook(ps), 818 DocumentID: id, 819 DocumentName: path, 820 Filename: filename, 821 }, 822 ) 823 if err != nil { 824 return converter.NopConverter, err 825 } 826 827 return cpp, nil 828 } 829 830 // The output formats this page will be rendered to. 831 func (m *pageMeta) outputFormats() output.Formats { 832 if len(m.configuredOutputFormats) > 0 { 833 return m.configuredOutputFormats 834 } 835 836 return m.s.outputFormats[m.Kind()] 837 } 838 839 func (p *pageMeta) Slug() string { 840 return p.urlPaths.Slug 841 } 842 843 func getParam(m resource.ResourceParamsProvider, key string, stringToLower bool) any { 844 v := m.Params()[strings.ToLower(key)] 845 846 if v == nil { 847 return nil 848 } 849 850 switch val := v.(type) { 851 case bool: 852 return val 853 case string: 854 if stringToLower { 855 return strings.ToLower(val) 856 } 857 return val 858 case int64, int32, int16, int8, int: 859 return cast.ToInt(v) 860 case float64, float32: 861 return cast.ToFloat64(v) 862 case time.Time: 863 return val 864 case []string: 865 if stringToLower { 866 return helpers.SliceToLower(val) 867 } 868 return v 869 default: 870 return v 871 } 872 } 873 874 func getParamToLower(m resource.ResourceParamsProvider, key string) any { 875 return getParam(m, key, true) 876 }