renderer/renderer.go (79 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package renderer import ( "fmt" "io/fs" "os" "path/filepath" "text/template" "github.com/elastic/crd-ref-docs/config" "github.com/elastic/crd-ref-docs/types" ) const mainTemplate = "gvList" type Renderer interface { Render(gvd []types.GroupVersionDetails) error } func New(conf *config.Config) (Renderer, error) { switch conf.Renderer { case "asciidoctor": return NewAsciidoctorRenderer(conf) case "markdown": return NewMarkdownRenderer(conf) default: return nil, fmt.Errorf("unknown renderer: %s", conf.Renderer) } } func loadTemplate(templatesFS fs.FS, funcs template.FuncMap) (*template.Template, error) { return template.New("").Funcs(funcs).ParseFS(templatesFS, "*.tpl") } type funcMap struct { prefix string funcs template.FuncMap } func combinedFuncMap(funcs ...funcMap) template.FuncMap { m := make(template.FuncMap) for _, f := range funcs { for k, v := range f.funcs { m[f.prefix+k] = v } } return m } // renderTemplate applies a given template to a set of GroupVersionDetails and writes the output to files, it supports // two output modes as specified in the configuration: single mode or group mode. // In single mode, all data is rendered into one output file. // In group mode, separate files are created for each group. func renderTemplate(tmpl *template.Template, conf *config.Config, fileExtension string, gvds []types.GroupVersionDetails) error { switch conf.OutputMode { case config.OutputModeSingle: fileName := fmt.Sprintf("%s.%s", "out", fileExtension) file, err := createOutFile(conf.OutputPath, false, fileName) defer file.Close() if err != nil { return err } if err := tmpl.ExecuteTemplate(file, mainTemplate, gvds); err != nil { return err } case config.OutputModeGroup: for _, gvd := range gvds { fileName := fmt.Sprintf("%s.%s", gvd.Group, fileExtension) file, err := createOutFile(conf.OutputPath, true, fileName) defer file.Close() if err != nil { return err } if err := tmpl.ExecuteTemplate(file, mainTemplate, []types.GroupVersionDetails{gvd}); err != nil { return err } } } return nil } // createOutFile creates the file pointed to by outputPath if it does not exist, or if it exists and is a directory, // then creates a file in the directory using the given defaultFilename. If expectedDir is true, outputPath must be an // existing directory where defaultFileName is created. func createOutFile(outputPath string, expectedDir bool, defaultFileName string) (*os.File, error) { finfo, err := os.Stat(outputPath) if err != nil && !os.IsNotExist(err) { return nil, err } if finfo != nil && finfo.IsDir() { outputPath = filepath.Join(outputPath, defaultFileName) } else if expectedDir { return nil, fmt.Errorf("output path must point to an existing directory") } return os.Create(outputPath) }