Files
mark/includes/templates.go

148 lines
2.9 KiB
Go
Raw Normal View History

2019-08-02 22:58:08 +03:00
package includes
import (
"bytes"
"fmt"
2023-01-03 18:54:04 +01:00
"os"
2019-08-02 22:58:08 +03:00
"path/filepath"
"regexp"
"strings"
"text/template"
2026-02-08 01:09:24 +01:00
"go.yaml.in/yaml/v3"
2019-08-02 22:58:08 +03:00
2026-03-28 09:55:58 +01:00
"github.com/rs/zerolog/log"
2019-08-02 22:58:08 +03:00
)
2020-11-03 17:12:51 +03:00
// <!-- Include: <template path>
2023-01-03 18:54:04 +01:00
//
// (Delims: (none | "<left>","<right>"))?
// <optional yaml data> -->
2020-11-03 17:12:51 +03:00
var reIncludeDirective = regexp.MustCompile(
`(?s)` +
2022-06-07 14:21:11 +06:00
`<!--\s*Include:\s*(?P<template>.+?)\s*` +
`(?:\n\s*Delims:\s*(?:(none|"(?P<left>.*?)"\s*,\s*"(?P<right>.*?)")))?\s*` +
`(?:\n(?P<config>.*?))?-->`,
)
2019-08-02 22:58:08 +03:00
func LoadTemplate(
base string,
includePath string,
2019-08-02 22:58:08 +03:00
path string,
left string,
right string,
2019-08-02 22:58:08 +03:00
templates *template.Template,
) (*template.Template, error) {
2019-08-02 22:58:08 +03:00
var (
name = strings.TrimSuffix(path, filepath.Ext(path))
2019-08-02 22:58:08 +03:00
)
if template := templates.Lookup(name); template != nil {
return template, nil
2019-08-02 22:58:08 +03:00
}
var body []byte
2023-01-03 18:54:04 +01:00
body, err := os.ReadFile(filepath.Join(base, path))
2019-08-02 22:58:08 +03:00
if err != nil {
if includePath != "" {
body, err = os.ReadFile(filepath.Join(includePath, path))
}
if err != nil {
return nil, fmt.Errorf("unable to read template file %q: %w", path, err)
}
2019-08-02 22:58:08 +03:00
}
2021-11-08 20:15:59 +06:00
body = bytes.ReplaceAll(
body,
[]byte("\r\n"),
[]byte("\n"),
)
templates, err = templates.New(name).Delims(left, right).Parse(string(body))
2019-08-02 22:58:08 +03:00
if err != nil {
return nil, fmt.Errorf("unable to parse template %q: %w", name, err)
2019-08-02 22:58:08 +03:00
}
return templates, nil
2019-08-02 22:58:08 +03:00
}
func ProcessIncludes(
base string,
includePath string,
2019-08-02 22:58:08 +03:00
contents []byte,
templates *template.Template,
) (*template.Template, []byte, bool, error) {
formatVardump := func(
data map[string]any,
) string {
var parts []string
2019-08-02 22:58:08 +03:00
for key, value := range data {
parts = append(parts, fmt.Sprintf("%s=%v", key, value))
2019-08-02 22:58:08 +03:00
}
return strings.Join(parts, ", ")
2019-08-02 22:58:08 +03:00
}
var (
recurse bool
err error
)
contents = reIncludeDirective.ReplaceAllFunc(
contents,
func(spec []byte) []byte {
if err != nil {
return spec
2019-08-02 22:58:08 +03:00
}
groups := reIncludeDirective.FindSubmatch(spec)
var (
2022-06-07 14:21:11 +06:00
path = string(groups[1])
delimsNone = string(groups[2])
left = string(groups[3])
right = string(groups[4])
config = groups[5]
data = map[string]any{}
2019-08-02 22:58:08 +03:00
)
2022-06-07 14:21:11 +06:00
if delimsNone == "none" {
left = "\x00"
right = "\x01"
}
2022-06-07 14:21:11 +06:00
2019-08-02 22:58:08 +03:00
err = yaml.Unmarshal(config, &data)
if err != nil {
err = fmt.Errorf("unable to unmarshal template data config (path=%q, config=%q): %w", path, string(config), err)
2019-08-02 22:58:08 +03:00
return spec
2019-08-02 22:58:08 +03:00
}
log.Trace().Interface("vardump", data).Msgf("including template %q", path)
2019-08-02 22:58:08 +03:00
templates, err = LoadTemplate(base, includePath, path, left, right, templates)
2019-08-02 22:58:08 +03:00
if err != nil {
err = fmt.Errorf("unable to load template %q: %w", path, err)
return spec
2019-08-02 22:58:08 +03:00
}
var buffer bytes.Buffer
err = templates.Execute(&buffer, data)
2019-08-02 22:58:08 +03:00
if err != nil {
err = fmt.Errorf("unable to execute template %q (vars: %s): %w", path, formatVardump(data), err)
2019-08-02 22:58:08 +03:00
return spec
2019-08-02 22:58:08 +03:00
}
recurse = true
return buffer.Bytes()
},
)
return templates, contents, recurse, err
}