// xtemplate extends Go's html/template to be capable enough to define an entire // server-side application with a directory of Go templates. package xtemplate import ( "context" "fmt" "html/template" "log/slog" "github.com/spf13/afero" ) func New() (c *Config) { c = &Config{} c.Defaults() return } type Config struct { // The path to the templates directory within the filesystem. Default `templates`. TemplatesDir string `json:"templates_dir,omitempty" arg:"-t,--template-dir" default:"templates"` // The FS to load templates from. Default: a FS made from the current working directory. TemplatesFS afero.Fs `json:"-" arg:"-"` // File extension to search for to find template files. Default `.html`. TemplateExtension string `json:"template_extension,omitempty" arg:"--template-ext" default:".html"` // Whether html templates are minified at load time. Default `true`. Minify bool `json:"minify,omitempty" arg:"-m,--minify" default:"true"` Databases []DotDBConfig `json:"databases" arg:"-"` Flags []DotFlagsConfig `json:"flags" arg:"-"` Directories []DotDirConfig `json:"directories" arg:"-"` Nats []DotNatsConfig `json:"nats" arg:"-"` CustomProviders []DotConfig `json:"-" arg:"-"` // Left template action delimiter. Default `{{`. LDelim string `json:"left,omitempty" arg:"--ldelim" default:"{{"` // Right template action delimiter. Default `}}`. RDelim string `json:"right,omitempty" arg:"--rdelim" default:"}}"` // Additional functions to add to the template execution context. FuncMaps []template.FuncMap `json:"-" arg:"-"` // The instance context that is threaded through dot providers and can // cancel the server. Defaults to `context.Background()`. Ctx context.Context `json:"-" arg:"-"` // The default logger. Defaults to `slog.Default()`. Logger *slog.Logger `json:"-" arg:"-"` } // FillDefaults sets default values for unset fields func (config *Config) Defaults() *Config { if config.TemplatesDir == "" { config.TemplatesDir = "templates" } if config.TemplateExtension == "" { config.TemplateExtension = ".html" } if config.LDelim == "" { config.LDelim = "{{" } if config.RDelim == "" { config.RDelim = "}}" } if config.Logger == nil { config.Logger = slog.Default() } if config.Ctx == nil { config.Ctx = context.Background() } return config } func (c *Config) Options(options ...Option) (*Config, error) { for _, o := range options { if err := o(c); err != nil { return nil, fmt.Errorf("failed to apply xtemplate config option: %w", err) } } return c, nil } type Option func(*Config) error func WithTemplateFS(fs afero.Fs) Option { return func(c *Config) error { if fs == nil { return fmt.Errorf("nil fs") } c.TemplatesFS = fs return nil } } func WithLogger(logger *slog.Logger) Option { return func(c *Config) error { if logger == nil { return fmt.Errorf("nil logger") } c.Logger = logger return nil } } func WithFuncMaps(fm ...template.FuncMap) Option { return func(c *Config) error { c.FuncMaps = append(c.FuncMaps, fm...) return nil } } func WithProvider(p DotConfig) Option { return func(c *Config) error { c.CustomProviders = append(c.CustomProviders, p) return nil } }