github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/resources/page/page_generate/generate_page_wrappers.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 page_generate 15 16 import ( 17 "bytes" 18 "fmt" 19 "os" 20 "path/filepath" 21 "reflect" 22 23 "github.com/pkg/errors" 24 25 "github.com/gohugoio/hugo/common/maps" 26 27 "github.com/gohugoio/hugo/codegen" 28 "github.com/gohugoio/hugo/resources/page" 29 "github.com/gohugoio/hugo/source" 30 ) 31 32 const header = `// Copyright 2019 The Hugo Authors. All rights reserved. 33 // 34 // Licensed under the Apache License, Version 2.0 (the "License"); 35 // you may not use this file except in compliance with the License. 36 // You may obtain a copy of the License at 37 // http://www.apache.org/licenses/LICENSE-2.0 38 // 39 // Unless required by applicable law or agreed to in writing, software 40 // distributed under the License is distributed on an "AS IS" BASIS, 41 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 42 // See the License for the specific language governing permissions and 43 // limitations under the License. 44 45 // This file is autogenerated. 46 ` 47 48 var ( 49 fileInterfaceDeprecated = reflect.TypeOf((*source.FileWithoutOverlap)(nil)).Elem() 50 pageInterfaceDeprecated = reflect.TypeOf((*page.DeprecatedWarningPageMethods)(nil)).Elem() 51 pageInterface = reflect.TypeOf((*page.Page)(nil)).Elem() 52 53 packageDir = filepath.FromSlash("resources/page") 54 ) 55 56 func Generate(c *codegen.Inspector) error { 57 if err := generateMarshalJSON(c); err != nil { 58 return errors.Wrap(err, "failed to generate JSON marshaler") 59 } 60 61 if err := generateDeprecatedWrappers(c); err != nil { 62 return errors.Wrap(err, "failed to generate deprecate wrappers") 63 } 64 65 if err := generateFileIsZeroWrappers(c); err != nil { 66 return errors.Wrap(err, "failed to generate file wrappers") 67 } 68 69 return nil 70 } 71 72 func generateMarshalJSON(c *codegen.Inspector) error { 73 filename := filepath.Join(c.ProjectRootDir, packageDir, "page_marshaljson.autogen.go") 74 f, err := os.Create(filename) 75 if err != nil { 76 return err 77 } 78 defer f.Close() 79 80 includes := []reflect.Type{pageInterface} 81 82 // Exclude these methods 83 excludes := []reflect.Type{ 84 // We need to evaluate the deprecated vs JSON in the future, 85 // but leave them out for now. 86 pageInterfaceDeprecated, 87 88 // Leave this out for now. We need to revisit the author issue. 89 reflect.TypeOf((*page.AuthorProvider)(nil)).Elem(), 90 91 // navigation.PageMenus 92 93 // Prevent loops. 94 reflect.TypeOf((*page.SitesProvider)(nil)).Elem(), 95 reflect.TypeOf((*page.Positioner)(nil)).Elem(), 96 97 reflect.TypeOf((*page.ChildCareProvider)(nil)).Elem(), 98 reflect.TypeOf((*page.TreeProvider)(nil)).Elem(), 99 reflect.TypeOf((*page.InSectionPositioner)(nil)).Elem(), 100 reflect.TypeOf((*page.PaginatorProvider)(nil)).Elem(), 101 reflect.TypeOf((*maps.Scratcher)(nil)).Elem(), 102 } 103 104 methods := c.MethodsFromTypes( 105 includes, 106 excludes) 107 108 if len(methods) == 0 { 109 return errors.New("no methods found") 110 } 111 112 marshalJSON, pkgImports := methods.ToMarshalJSON( 113 "Page", 114 "github.com/gohugoio/hugo/resources/page", 115 // Exclusion regexps. Matches method names. 116 `\bPage\b`, 117 ) 118 119 fmt.Fprintf(f, `%s 120 121 package page 122 123 %s 124 125 126 %s 127 128 129 `, header, importsString(pkgImports), marshalJSON) 130 131 return nil 132 } 133 134 func generateDeprecatedWrappers(c *codegen.Inspector) error { 135 filename := filepath.Join(c.ProjectRootDir, packageDir, "page_wrappers.autogen.go") 136 f, err := os.Create(filename) 137 if err != nil { 138 return err 139 } 140 defer f.Close() 141 142 // Generate a wrapper for deprecated page methods 143 144 reasons := map[string]string{ 145 "IsDraft": "Use .Draft.", 146 "Hugo": "Use the global hugo function.", 147 "LanguagePrefix": "Use .Site.LanguagePrefix.", 148 "GetParam": "Use .Param or .Params.myParam.", 149 "RSSLink": `Use the Output Format's link, e.g. something like: 150 {{ with .OutputFormats.Get "RSS" }}{{ .RelPermalink }}{{ end }}`, 151 "URL": "Use .Permalink or .RelPermalink. If what you want is the front matter URL value, use .Params.url", 152 } 153 154 deprecated := func(name string, tp reflect.Type) string { 155 var alternative string 156 if tp == fileInterfaceDeprecated { 157 alternative = "Use .File." + name 158 } else { 159 var found bool 160 alternative, found = reasons[name] 161 if !found { 162 panic(fmt.Sprintf("no deprecated reason found for %q", name)) 163 } 164 } 165 166 return fmt.Sprintf("helpers.Deprecated(%q, %q, false)", "Page."+name, alternative) 167 } 168 169 var buff bytes.Buffer 170 171 methods := c.MethodsFromTypes([]reflect.Type{fileInterfaceDeprecated, pageInterfaceDeprecated}, nil) 172 173 for _, m := range methods { 174 fmt.Fprint(&buff, m.Declaration("*pageDeprecated")) 175 fmt.Fprintln(&buff, " {") 176 fmt.Fprintf(&buff, "\t%s\n", deprecated(m.Name, m.Owner)) 177 fmt.Fprintf(&buff, "\t%s\n}\n", m.Delegate("p", "p")) 178 179 } 180 181 pkgImports := append(methods.Imports(), "github.com/gohugoio/hugo/helpers") 182 183 fmt.Fprintf(f, `%s 184 185 package page 186 187 %s 188 // NewDeprecatedWarningPage adds deprecation warnings to the given implementation. 189 func NewDeprecatedWarningPage(p DeprecatedWarningPageMethods) DeprecatedWarningPageMethods { 190 return &pageDeprecated{p: p} 191 } 192 193 type pageDeprecated struct { 194 p DeprecatedWarningPageMethods 195 } 196 197 %s 198 199 `, header, importsString(pkgImports), buff.String()) 200 201 return nil 202 } 203 204 func generateFileIsZeroWrappers(c *codegen.Inspector) error { 205 filename := filepath.Join(c.ProjectRootDir, packageDir, "zero_file.autogen.go") 206 f, err := os.Create(filename) 207 if err != nil { 208 return err 209 } 210 defer f.Close() 211 212 // Generate warnings for zero file access 213 214 warning := func(name string, tp reflect.Type) string { 215 msg := fmt.Sprintf(".File.%s on zero object. Wrap it in if or with: {{ with .File }}{{ .%s }}{{ end }}", name, name) 216 217 return fmt.Sprintf("z.log.Println(%q)", msg) 218 } 219 220 var buff bytes.Buffer 221 222 methods := c.MethodsFromTypes([]reflect.Type{reflect.TypeOf((*source.File)(nil)).Elem()}, nil) 223 224 for _, m := range methods { 225 if m.Name == "IsZero" { 226 continue 227 } 228 fmt.Fprint(&buff, m.DeclarationNamed("zeroFile")) 229 fmt.Fprintln(&buff, " {") 230 fmt.Fprintf(&buff, "\t%s\n", warning(m.Name, m.Owner)) 231 if len(m.Out) > 0 { 232 fmt.Fprintln(&buff, "\treturn") 233 } 234 fmt.Fprintln(&buff, "}") 235 236 } 237 238 pkgImports := append(methods.Imports(), "github.com/gohugoio/hugo/common/loggers", "github.com/gohugoio/hugo/source") 239 240 fmt.Fprintf(f, `%s 241 242 package page 243 244 %s 245 246 // ZeroFile represents a zero value of source.File with warnings if invoked. 247 type zeroFile struct { 248 log loggers.Logger 249 } 250 251 func NewZeroFile(log loggers.Logger) source.File { 252 return zeroFile{log: log} 253 } 254 255 func (zeroFile) IsZero() bool { 256 return true 257 } 258 259 %s 260 261 `, header, importsString(pkgImports), buff.String()) 262 263 return nil 264 } 265 266 func importsString(imps []string) string { 267 if len(imps) == 0 { 268 return "" 269 } 270 271 if len(imps) == 1 { 272 return fmt.Sprintf("import %q", imps[0]) 273 } 274 275 impsStr := "import (\n" 276 for _, imp := range imps { 277 impsStr += fmt.Sprintf("%q\n", imp) 278 } 279 280 return impsStr + ")" 281 }