164 lines
4.1 KiB
Go
164 lines
4.1 KiB
Go
package templates
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
gotmpl "text/template"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
// TemplateLoader reads a templates files and runs their respective templating on them.
|
|
type TemplateLoader struct{}
|
|
|
|
func NewTemplateLoader() *TemplateLoader {
|
|
return &TemplateLoader{}
|
|
}
|
|
|
|
type File struct {
|
|
content []byte
|
|
path string
|
|
RelPath string
|
|
}
|
|
|
|
var funcs = gotmpl.FuncMap{
|
|
"ReplaceAll": strings.ReplaceAll,
|
|
"ToLower": strings.ToLower,
|
|
"ToUpper": strings.ToUpper,
|
|
}
|
|
|
|
// TemplatePath formats the template file path using go templates, this is useful for programmatically changing the output string using go tmpls
|
|
func TemplatePath(template *Template) (string, error) {
|
|
tmpl, err := gotmpl.New("path").Funcs(funcs).Parse(template.File.Default.Path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
output := bytes.NewBufferString("")
|
|
if err := tmpl.Execute(output, template); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
templatePath := strings.TrimSpace(output.String())
|
|
|
|
return templatePath, nil
|
|
}
|
|
|
|
// Load loads the template files from disk
|
|
func (t *TemplateLoader) Load(ctx context.Context, template *Template) ([]File, error) {
|
|
templateFilePath := path.Join(template.Path, "files")
|
|
if _, err := os.Stat(templateFilePath); err != nil {
|
|
return nil, fmt.Errorf("failed to lookup template files %s, %w", templateFilePath, err)
|
|
}
|
|
|
|
filePaths := make([]string, 0)
|
|
err := filepath.WalkDir(templateFilePath, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Is a file
|
|
if d.Type().IsRegular() {
|
|
filePaths = append(filePaths, path)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read template files: %w", err)
|
|
}
|
|
|
|
var (
|
|
filesLock sync.Mutex
|
|
files = make([]File, 0)
|
|
)
|
|
egrp, _ := errgroup.WithContext(ctx)
|
|
for _, filePath := range filePaths {
|
|
egrp.Go(func() error {
|
|
fileContent, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read file: %s, %w", filePath, err)
|
|
}
|
|
|
|
filesLock.Lock()
|
|
defer filesLock.Unlock()
|
|
|
|
files = append(files, File{
|
|
content: fileContent,
|
|
path: filePath,
|
|
RelPath: strings.TrimPrefix(strings.TrimPrefix(filePath, templateFilePath), "/"),
|
|
})
|
|
|
|
return nil
|
|
})
|
|
|
|
}
|
|
if err := egrp.Wait(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
type TemplatedFile struct {
|
|
Content []byte
|
|
DestinationPath string
|
|
}
|
|
|
|
// TemplateFiles runs the actual templating on the files, and tells it where to go. The writes doesn't happen here yet.
|
|
func (l *TemplateLoader) TemplateFiles(template *Template, files []File, scaffoldDest string) ([]TemplatedFile, error) {
|
|
templatedFiles := make([]TemplatedFile, 0)
|
|
for _, file := range files {
|
|
tmpl, err := gotmpl.
|
|
New(file.RelPath).
|
|
Funcs(funcs).
|
|
Parse(string(file.content))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse template file: %s, %w", file.RelPath, err)
|
|
}
|
|
|
|
output := bytes.NewBufferString("")
|
|
if err := tmpl.Execute(output, template); err != nil {
|
|
return nil, fmt.Errorf("failed to write template file: %s, %w", file.RelPath, err)
|
|
}
|
|
|
|
fileDir := path.Dir(file.RelPath)
|
|
fileName := strings.TrimSuffix(path.Base(file.RelPath), ".gotmpl")
|
|
if fileConfig, ok := template.File.Files[strings.TrimSuffix(file.RelPath, ".gotmpl")]; ok && fileConfig.Rename != "" {
|
|
renameTmpl, err := gotmpl.New(file.RelPath).Funcs(funcs).Parse(fileConfig.Rename)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse rename for: %s in scaffold.yaml: %w", file.RelPath, err)
|
|
}
|
|
|
|
type RenameContext struct {
|
|
Template
|
|
OriginalFileName string
|
|
}
|
|
|
|
output := bytes.NewBufferString("")
|
|
if err := renameTmpl.Execute(output, RenameContext{
|
|
Template: *template,
|
|
OriginalFileName: fileName,
|
|
}); err != nil {
|
|
return nil, fmt.Errorf("failed to template rename: %s, %w", file.RelPath, err)
|
|
}
|
|
|
|
fileName = strings.TrimSpace(output.String())
|
|
}
|
|
|
|
templatedFiles = append(templatedFiles, TemplatedFile{
|
|
Content: output.Bytes(),
|
|
DestinationPath: path.Join(scaffoldDest, fileDir, fileName),
|
|
})
|
|
}
|
|
|
|
return templatedFiles, nil
|
|
}
|