mirror of https://gerrit.hackerspace.pl/hscloud
140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"html/template"
|
|
"strings"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
|
|
"code.hackerspace.pl/hscloud/devtools/hackdoc/source"
|
|
)
|
|
|
|
// Config is a configuration concerning a given path of the source. It is built
|
|
// from files present in the source, and from global configuration.
|
|
type Config struct {
|
|
// DefaultIndex is the filenames that should attempt to be rendered if no exact path is given
|
|
DefaultIndex []string
|
|
// Templates are the templates available to render markdown files, keyed by template name.
|
|
Templates map[string]*template.Template
|
|
|
|
// Errors that occured while building this config (due to config file errors, etc).
|
|
Errors map[string]error
|
|
}
|
|
|
|
type configToml struct {
|
|
DefaultIndex []string `toml:"default_index"`
|
|
Templates map[string]*configTomlTemplate `toml:"template"`
|
|
}
|
|
|
|
type configTomlTemplate struct {
|
|
Sources []string `toml:"sources"`
|
|
}
|
|
|
|
func parseToml(data []byte) (*configToml, error) {
|
|
var c configToml
|
|
err := toml.Unmarshal(data, &c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if c.Templates == nil {
|
|
c.Templates = make(map[string]*configTomlTemplate)
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
func configFileLocations(path string) []string {
|
|
// Support for unix-style filesystem prefix (/foo/bar/baz) and
|
|
// perforce-depot-style prefix (//foo/bar/baz).
|
|
// Also support relative paths.
|
|
pathTrimmed := strings.TrimLeft(path, "/")
|
|
prefixLen := len(path) - len(pathTrimmed)
|
|
prefix := path[:prefixLen]
|
|
path = pathTrimmed
|
|
if len(prefix) > 2 {
|
|
return nil
|
|
}
|
|
|
|
// Turn path into possible directory names, including root.
|
|
path = strings.Trim(path, "/")
|
|
parts := strings.Split(path, "/")
|
|
if parts[0] != "" {
|
|
parts = append([]string{""}, parts...)
|
|
}
|
|
|
|
locations := []string{}
|
|
for i, _ := range parts {
|
|
p := strings.Join(parts[:i+1], "/")
|
|
p += "/hackdoc.toml"
|
|
p = prefix + strings.Trim(p, "/")
|
|
locations = append(locations, p)
|
|
}
|
|
return locations
|
|
}
|
|
|
|
func ForPath(ctx context.Context, s source.Source, path string) (*Config, error) {
|
|
if path != "//" {
|
|
path = strings.TrimRight(path, "/")
|
|
}
|
|
|
|
cfg := &Config{
|
|
Templates: make(map[string]*template.Template),
|
|
Errors: make(map[string]error),
|
|
}
|
|
|
|
tomlPaths := configFileLocations(path)
|
|
for _, p := range tomlPaths {
|
|
file, err := s.IsFile(ctx, p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("IsFile(%q): %w", path, err)
|
|
}
|
|
if !file {
|
|
continue
|
|
}
|
|
data, err := s.ReadFile(ctx, p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ReadFile(%q): %w", path, err)
|
|
}
|
|
|
|
c, err := parseToml(data)
|
|
if err != nil {
|
|
cfg.Errors[p] = err
|
|
continue
|
|
}
|
|
|
|
err = cfg.updateFromToml(ctx, p, s, c)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("updating from %q: %w", p, err)
|
|
}
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func (c *Config) updateFromToml(ctx context.Context, p string, s source.Source, t *configToml) error {
|
|
if t.DefaultIndex != nil {
|
|
c.DefaultIndex = t.DefaultIndex
|
|
}
|
|
|
|
for k, v := range t.Templates {
|
|
tmpl := template.New(k)
|
|
|
|
for _, source := range v.Sources {
|
|
data, err := s.ReadFile(ctx, source)
|
|
if err != nil {
|
|
c.Errors[p] = fmt.Errorf("reading template file %q: %w", source, err)
|
|
return nil
|
|
}
|
|
tmpl, err = tmpl.Parse(string(data))
|
|
if err != nil {
|
|
c.Errors[p] = fmt.Errorf("parsing template file %q: %w", source, err)
|
|
return nil
|
|
}
|
|
}
|
|
c.Templates[k] = tmpl
|
|
}
|
|
|
|
return nil
|
|
}
|