template

package module
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 14, 2025 License: MIT Imports: 15 Imported by: 0

README

Simple Go Template Engine

Overview

This Go Template Engine library introduces dynamic templating for Go applications, enabling variable interpolation and data manipulation with filters. Drawing inspiration from the syntax of Liquid and Django, it simplifies the generation of dynamic content.

Getting Started

Installation

Ensure you have Go set up on your system. To add the Go Template Engine to your project, run:

go get github.com/kaptinlin/template

This command downloads the library and prepares it for use in your project.

Basic Usage
Parsing and Executing a Template

Create and parse a template, then execute it with a context:

package main

import (
	"fmt"
	"github.com/kaptinlin/template"
)

func main() {
    // Define your template
    source := "Hello, {{ name }}!"
    // Parse the template
    tpl, err := template.Parse(source)
    if err != nil {
        panic(err)
    }
    
    // Create a context and add variables
    context := template.NewContext()
    context.Set("name", "World")
    
    // Execute the template
    output, err := template.Execute(tpl, context)
    if err != nil {
        panic(err)
    }
    
    fmt.Println(output) // Output: Hello, World!
}
Quick Parsing and Execution with Render

Directly parse and execute a template in one step:

package main

import (
	"fmt"
	"github.com/kaptinlin/template"
)

func main() {
    // Define your template and context
    source := "Goodbye, {{ name }}!"
    context := template.NewContext()
    context.Set("name", "Mars")
    
    // Render the template
    output, err := template.Render(source, context)
    if err != nil {
        panic(err)
    }
    
    fmt.Println(output) // Output: Goodbye, Mars!
}
Ignoring Errors with MustExecute

Execute a template and ignore any errors, useful for templates guaranteed not to fail:

package main

import (
	"fmt"
	"github.com/kaptinlin/template"
)

func main() {
    // Define your template
    source := "Welcome, {{ name }}!"
    // Parse the template
    tpl, err := template.Parse(source)
    if err != nil {
        panic(err)
    }
    
    // Create a context and add variables
    context := template.NewContext()
    context.Set("name", "Universe")
    
    // MustExecute the template, ignoring errors
    output := template.MustExecute(tpl, context)
    
    fmt.Println(output) // Output: Welcome, Universe!
}

Syntax and Features

Variables

Enclose variables in {{ }} to embed dynamic content:

{{ userName }}

For extended syntax, refer to the documentation.

Filters

Use the pipe | to apply filters to variables:

Hello, {{ name|capitalize }}!

Detailed usage can be found in the documentation.

For Loops

Use {% for item in collection %} and {% endfor %} to iterate over arrays, maps:

{% for item in items %}
    {{ item }}
{% endfor %}
If Conditionals

Use {% if condition %}, {% else %} and {% endif %} to conditionally render content based on expressions:

{% if user.age >= 18 %}
    Adult
{% else %}
    Minor
{% endif %}

Control structures can be nested and support complex expressions. For more details, see Control Structures Documentation.

Custom Filters

Easily extend functionality by adding custom filters. For example, a filter to capitalize a string:

package main

import (
	"github.com/kaptinlin/template"
	"strings"
)

func capitalize(input interface{}, args ...string) (interface{}, error) {
	s, ok := input.(string)
	if !ok {
		return input, nil
	}
	return strings.Title(s), nil
}

func init() {
	template.RegisterFilter("capitalize", capitalize)
}

Use the custom filter like so:

{{ "john doe"|capitalize }}

Context Management

Contexts pass variables to templates. Here’s how to create and use one:

context := template.NewContext()
context.Set("key", "value")

How to Contribute

Contributions to the template package are welcome. If you'd like to contribute, please follow the contribution guidelines.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package template provides a simple and efficient template engine for Go.

Index

Constants

View Source
const (
	NodeTypeBreak    = "break"
	NodeTypeContinue = "continue"
)

Node type constants

Variables

View Source
var (
	// ErrContextKeyNotFound is returned when a key is not found in the context.
	ErrContextKeyNotFound = errors.New("key not found in context")

	// ErrContextInvalidKeyType is returned when an unexpected type is encountered while navigating the context.
	ErrContextInvalidKeyType = errors.New("invalid key type for navigation")

	// ErrContextIndexOutOfRange is returned when an index is out of range in the context.
	ErrContextIndexOutOfRange = errors.New("index out of range in context")

	// ErrFilterNotFound indicates that the requested filter was not found in the global registry.
	ErrFilterNotFound = errors.New("filter not found")

	// ErrFilterInputInvalid indicates an issue with the filter input value being of an unexpected type or format.
	ErrFilterInputInvalid = errors.New("filter input is invalid")

	// ErrFilterArgsInvalid indicates an issue with the filter arguments, such as wrong type, format, or number of arguments.
	ErrFilterArgsInvalid = errors.New("filter arguments are invalid")

	// ErrFilterInputEmpty indicates that the input value is empty or nil.
	ErrFilterInputEmpty = errors.New("filter input is empty")

	// ErrInsufficientArgs indicates that the filter was called with insufficient arguments.
	ErrInsufficientArgs = errors.New("insufficient arguments provided")

	// ErrFilterInputNotSlice indicates a filter expected a slice but received a different type.
	ErrFilterInputNotSlice = errors.New("filter input is not a slice")

	// ErrFilterInputNotNumeric indicates a filter expected a numeric value but received a different type.
	ErrFilterInputNotNumeric = errors.New("filter input is not numeric")

	// ErrFilterInputInvalidTimeFormat indicates a filter expected a valid time format but didn't receive it.
	ErrFilterInputInvalidTimeFormat = errors.New("filter input has an invalid time format")

	// ErrFilterInputUnsupportedType indicates the filter received a type it does not support.
	ErrFilterInputUnsupportedType = errors.New("filter input is of an unsupported type")

	// ErrInvalidFilterName is returned when a filter name does not meet the required criteria.
	ErrInvalidFilterName = errors.New("invalid filter name")

	// ErrUnknownFilterArgumentType is returned when a filter argument type is unknown.
	ErrUnknownFilterArgumentType = errors.New("unknown argument type")

	// ErrUnknownNodeType is returned when an unexpected node type is encountered.
	ErrUnknownNodeType = errors.New("unknown node type")

	// ErrExpectedFilterName is returned when a filter name is expected after the pipe symbol.
	ErrExpectedFilterName = errors.New("expected filter name after '|'")

	// ErrInvalidNumber is returned when a number cannot be parsed.
	ErrInvalidNumber = errors.New("invalid number")

	// ErrExpectedRParen is returned when a right parenthesis is expected.
	ErrExpectedRParen = errors.New("expected ')'")

	// ErrUnexpectedToken is returned when an unexpected token is encountered.
	ErrUnexpectedToken = errors.New("unexpected token")

	// ErrUnsupportedType is returned when an unsupported type is encountered.
	ErrUnsupportedType = errors.New("unsupported type")

	// ErrUnsupportedOperator is returned when an unsupported operator is used.
	ErrUnsupportedOperator = errors.New("unsupported operator")

	// ErrUnsupportedUnaryOp is returned when an unsupported unary operator is used.
	ErrUnsupportedUnaryOp = errors.New("unsupported unary operator")

	// ErrUndefinedVariable is returned when a variable is not found in the context.
	ErrUndefinedVariable = errors.New("undefined variable")

	// ErrUndefinedProperty is returned when a property is not found in an object.
	ErrUndefinedProperty = errors.New("undefined property")

	// ErrNonStructProperty is returned when attempting to access a property on a non-struct value.
	ErrNonStructProperty = errors.New("cannot access property of non-struct value")

	// ErrCannotAccessProperty is returned when property access fails.
	ErrCannotAccessProperty = errors.New("cannot access property")

	// ErrCannotAddTypes is returned when attempting to add incompatible types.
	ErrCannotAddTypes = errors.New("cannot add values of these types")

	// ErrCannotSubtractTypes is returned when attempting to subtract incompatible types.
	ErrCannotSubtractTypes = errors.New("cannot subtract values of these types")

	// ErrCannotMultiplyTypes is returned when attempting to multiply incompatible types.
	ErrCannotMultiplyTypes = errors.New("cannot multiply values of these types")

	// ErrDivisionByZero is returned when attempting to divide by zero.
	ErrDivisionByZero = errors.New("division by zero")

	// ErrCannotDivideTypes is returned when attempting to divide incompatible types.
	ErrCannotDivideTypes = errors.New("cannot divide values of these types")

	// ErrCannotConvertToBool is returned when a value cannot be converted to boolean.
	ErrCannotConvertToBool = errors.New("cannot convert type to boolean")

	// ErrCannotCompareTypes is returned when attempting to compare incompatible types.
	ErrCannotCompareTypes = errors.New("cannot compare values of these types")

	// ErrInvalidIndexType is returned when an invalid type is used as an array index.
	ErrInvalidIndexType = errors.New("invalid index type")

	// ErrInvalidArrayIndex is returned when an array index is invalid.
	ErrInvalidArrayIndex = errors.New("invalid array index")

	// ErrIndexOutOfRange is returned when an array index is out of bounds.
	ErrIndexOutOfRange = errors.New("index out of range")

	// ErrUnsupportedArrayType is returned when an unsupported array type is encountered.
	ErrUnsupportedArrayType = errors.New("unsupported array type")

	// ErrNonObjectProperty is returned when attempting to access a property on a non-object value.
	ErrNonObjectProperty = errors.New("cannot access property of non-object")

	// ErrInvalidVariableAccess is returned when variable access is invalid.
	ErrInvalidVariableAccess = errors.New("invalid variable access")

	// ErrUnsupportedCollectionType is returned when an unsupported collection type is used in a for loop.
	ErrUnsupportedCollectionType = errors.New("unsupported collection type for for loop")

	// ErrUnexpectedCharacter is returned when the lexer encounters an unexpected character.
	ErrUnexpectedCharacter = errors.New("unexpected character")

	// ErrUnterminatedString is returned when a string literal is not properly terminated.
	ErrUnterminatedString = errors.New("unterminated string literal")

	// ErrIntegerOverflow is returned when an unsigned integer value exceeds the maximum int64 value.
	ErrIntegerOverflow = errors.New("unsigned integer value exceeds maximum int64 value")

	// ErrBreakOutsideLoop is returned when a break statement is used outside of a loop.
	ErrBreakOutsideLoop = errors.New("break statement outside of loop")

	// ErrContinueOutsideLoop is returned when a continue statement is used outside of a loop.
	ErrContinueOutsideLoop = errors.New("continue statement outside of loop")

	// ErrMultipleElseStatements indicates that multiple else statements are found in an if block
	ErrMultipleElseStatements = errors.New("multiple 'else' statements found in if block. Use 'elif' for additional conditions")
)

Functions

func ApplyFilters

func ApplyFilters(value interface{}, fs []Filter, ctx Context) (interface{}, error)

ApplyFilters executes a series of filters on a value within a context, supporting variable arguments.

func Execute

func Execute(tpl *Template, ctx Context) (string, error)

Execute renders the template with provided context.

func MustExecute

func MustExecute(tpl *Template, ctx Context) string

MustExecute renders the template with provided context, ignoring errors.

func RegisterFilter

func RegisterFilter(name string, fn FilterFunc) error

RegisterFilter adds a filter to the global registry with name validation.

func Render

func Render(source string, ctx Context) (string, error)

Render combines parsing and executing a template with the given context for convenience.

Types

type BinaryExpressionNode added in v0.2.0

type BinaryExpressionNode struct {
	Left     ExpressionNode
	Right    ExpressionNode
	Operator string
}

BinaryExpressionNode represents a binary expression in the AST.

func (*BinaryExpressionNode) Evaluate added in v0.2.0

func (n *BinaryExpressionNode) Evaluate(ctx Context) (*Value, error)

Evaluate method implementation for BinaryExpressionNode

type BooleanLiteralNode added in v0.2.0

type BooleanLiteralNode struct {
	Value bool
}

BooleanLiteralNode represents a boolean literal in the AST.

func (*BooleanLiteralNode) Evaluate added in v0.2.0

func (n *BooleanLiteralNode) Evaluate(_ Context) (*Value, error)

Evaluate method implementation for BooleanLiteralNode

type Context

type Context map[string]interface{}

Context stores template variables in a map structure, used for passing and accessing variables during template execution. Keys are strings, and values can be of any type, supporting dot-notation (.) for nested access.

func NewContext

func NewContext() Context

NewContext creates and returns a new empty Context instance. Example usage: ctx := NewContext()

func (Context) Get

func (c Context) Get(key string) (interface{}, error)

Get retrieves a value from the Context for the specified key, supporting nested key access. Uses jsonpointer.Get to handle complex key paths, such as array indices and nested properties. Parameters:

  • key: The key to retrieve, can be a dot-separated nested key like "user.profile.name"

Returns:

  • interface{}: The retrieved value
  • error: Returns an appropriate error if the key doesn't exist or access fails

func (Context) Set

func (c Context) Set(key string, value interface{})

Set inserts a value into the Context with the specified key, supporting dot-notation (.) for nested keys. This method preserves original data types for top-level keys and creates minimal map structures only when needed for nested access. Relies on jsonpointer's powerful reading capabilities.

type ControlFlow added in v0.2.5

type ControlFlow int

ControlFlow represents the type of control flow operation

const (
	// ControlFlowNone indicates no control flow change
	ControlFlowNone ControlFlow = iota
	// ControlFlowBreak indicates a break control flow
	ControlFlowBreak
	// ControlFlowContinue indicates a continue control flow
	ControlFlowContinue
)

type ExpressionNode added in v0.2.0

type ExpressionNode interface {
	Evaluate(ctx Context) (*Value, error)
}

ExpressionNode is the interface for all expression nodes

type Filter

type Filter struct {
	Name string
	Args []FilterArg
}

Filter defines a transformation to apply to a template variable.

type FilterArg

type FilterArg interface {
	Value() interface{}
	Type() string
}

FilterArg represents the interface for filter arguments.

type FilterExpressionNode added in v0.2.0

type FilterExpressionNode struct {
	Expression ExpressionNode // Expression to be filtered
	Filter     string         // Filter name and parameters
}

FilterExpressionNode defines filter expression node

func (*FilterExpressionNode) Evaluate added in v0.2.0

func (n *FilterExpressionNode) Evaluate(ctx Context) (*Value, error)

Evaluate method implementation for FilterExpressionNode

type FilterFunc

type FilterFunc func(interface{}, ...string) (interface{}, error)

FilterFunc represents the signature of functions that can be applied as filters.

type Grammar added in v0.2.0

type Grammar struct {
	// contains filtered or unexported fields
}

Grammar represents the parser structure

func NewGrammar added in v0.2.0

func NewGrammar(tokens []Token) *Grammar

NewGrammar creates a new parser

func (*Grammar) Parse added in v0.2.0

func (g *Grammar) Parse() (ExpressionNode, error)

Parse starts parsing

type Lexer added in v0.2.0

type Lexer struct {
	// contains filtered or unexported fields
}

Lexer tokenizes template expressions into a sequence of tokens.

func (*Lexer) Lex added in v0.2.0

func (l *Lexer) Lex() ([]Token, error)

Lex tokenizes the input string into a list of tokens.

type LoopContext added in v0.2.8

type LoopContext struct {
	Index    int  // Current index (starting from 0)
	Revindex int  // Reverse index (length-1 to 0)
	First    bool // Whether this is the first iteration
	Last     bool // Whether this is the last iteration
	Length   int  // Total length of the collection
}

LoopContext represents the loop information available in templates

type NilLiteralNode added in v0.3.1

type NilLiteralNode struct{}

NilLiteralNode represents a nil/null/none literal in the AST.

func (*NilLiteralNode) Evaluate added in v0.3.1

func (n *NilLiteralNode) Evaluate(_ Context) (*Value, error)

Evaluate method implementation for NilLiteralNode

type Node

type Node struct {
	Type       string
	Text       string
	Variable   string
	Collection string
	Filters    []Filter
	Children   []*Node
	EndText    string
}

Node defines a single element within a template, such as text, variable, or control structure.

type NumberArg

type NumberArg struct {
	// contains filtered or unexported fields
}

NumberArg holds a number argument.

func (NumberArg) Type

func (a NumberArg) Type() string

Type returns the argument type as "number".

func (NumberArg) Value

func (a NumberArg) Value() interface{}

Value returns the number argument value.

type NumberLiteralNode added in v0.2.0

type NumberLiteralNode struct {
	Value float64
}

NumberLiteralNode represents a number literal in the AST.

func (*NumberLiteralNode) Evaluate added in v0.2.0

func (n *NumberLiteralNode) Evaluate(_ Context) (*Value, error)

Evaluate method implementation for NumberLiteralNode

type Parser

type Parser struct{}

Parser analyzes template syntax.

func NewParser

func NewParser() *Parser

NewParser creates a Parser with a compiled regular expression for efficiency.

func (*Parser) Parse

func (p *Parser) Parse(src string) (*Template, error)

Parse converts a template string into a Template object. It recognizes the following syntax: - Text content - Variable expressions {{ variable }} - Control structures {% if/for ... %}{% endif/endfor %} Returns the parsed template and any error encountered.

type StringArg

type StringArg struct {
	// contains filtered or unexported fields
}

StringArg holds a string argument.

func (StringArg) Type

func (a StringArg) Type() string

Type returns the argument type as "string".

func (StringArg) Value

func (a StringArg) Value() interface{}

Value returns the string argument value.

type StringLiteralNode added in v0.2.0

type StringLiteralNode struct {
	Value string
}

StringLiteralNode represents a string literal in the AST.

func (*StringLiteralNode) Evaluate added in v0.2.0

func (n *StringLiteralNode) Evaluate(_ Context) (*Value, error)

Evaluate method implementation for StringLiteralNode

type Template

type Template struct {
	Nodes []*Node
}

Template represents a structured template that can be executed with a given context.

func NewTemplate

func NewTemplate() *Template

NewTemplate creates an empty template, ready to be populated with nodes.

func Parse

func Parse(source string) (*Template, error)

Parse parses a template string and returns a Template instance.

func (*Template) Execute

func (t *Template) Execute(ctx Context) (string, error)

Execute combines template data with the provided context to produce a string.

func (*Template) MustExecute

func (t *Template) MustExecute(ctx Context) string

MustExecute combines template data with the provided context to produce a string, ignoring errors.

type Token added in v0.2.0

type Token struct {
	Typ TokenType // Token type
	Val string    // Token value
}

Token is a token in the template language. It represents a type and a value.

type TokenType added in v0.2.0

type TokenType int

TokenType represents the type of a token in the template language.

const (
	// TokenIdentifier represents a variable identifier (e.g., user.age)
	TokenIdentifier TokenType = iota
	// TokenBool represents a boolean value (e.g., true, false)
	TokenBool
	// TokenNumber represents a number (e.g., 18)
	TokenNumber
	// TokenString represents a string constant
	TokenString
	// TokenOperator represents operators (e.g., ==, !=, <, >, &&, ||)
	TokenOperator
	// TokenArithOp represents arithmetic operators
	TokenArithOp
	// TokenNot represents the not operator (!)
	TokenNot
	// TokenLParen represents a left parenthesis (()
	TokenLParen
	// TokenRParen represents a right parenthesis ())
	TokenRParen
	// TokenPipe represents the pipe operator (|)
	TokenPipe
	// TokenFilter represents a filter (including name and args, e.g., upper, truncate:30)
	TokenFilter
	// TokenEOF represents the end of input marker
	TokenEOF
	// TokenDot represents the dot operator (.)
	TokenDot
)

type UnaryExpressionNode added in v0.2.0

type UnaryExpressionNode struct {
	Operator string
	Right    ExpressionNode
}

UnaryExpressionNode represents a unary expression in the AST.

func (*UnaryExpressionNode) Evaluate added in v0.2.0

func (n *UnaryExpressionNode) Evaluate(ctx Context) (*Value, error)

Evaluate method implementation for UnaryExpressionNode

type Value added in v0.2.0

type Value struct {
	Type   ValueType
	Int    int64
	Float  float64
	Str    string
	Bool   bool
	Slice  interface{}
	Map    interface{}
	Struct interface{}
}

Value defines the value type

func NewValue added in v0.2.0

func NewValue(v interface{}) (*Value, error)

NewValue creates a new Value from an interface{}.

func (*Value) Add added in v0.2.0

func (v *Value) Add(right *Value) (*Value, error)

Add implements addition operation

func (*Value) And added in v0.2.0

func (v *Value) And(right *Value) (*Value, error)

And implements logical AND operation

func (*Value) Divide added in v0.2.0

func (v *Value) Divide(right *Value) (*Value, error)

Divide implements division operation

func (*Value) Equal added in v0.2.0

func (v *Value) Equal(right *Value) (*Value, error)

Equal implements equality comparison

func (*Value) GreaterEqual added in v0.2.0

func (v *Value) GreaterEqual(right *Value) (*Value, error)

GreaterEqual implements greater than or equal comparison

func (*Value) GreaterThan added in v0.2.0

func (v *Value) GreaterThan(right *Value) (*Value, error)

GreaterThan implements greater than comparison

func (*Value) In added in v0.3.1

func (v *Value) In(haystack *Value) (*Value, error)

In implements membership test operator

func (*Value) LessEqual added in v0.2.0

func (v *Value) LessEqual(right *Value) (*Value, error)

LessEqual implements less than or equal comparison

func (*Value) LessThan added in v0.2.0

func (v *Value) LessThan(right *Value) (*Value, error)

LessThan implements less than comparison

func (*Value) Multiply added in v0.2.0

func (v *Value) Multiply(right *Value) (*Value, error)

Multiply implements multiplication operation

func (*Value) NotEqual added in v0.2.0

func (v *Value) NotEqual(right *Value) (*Value, error)

NotEqual implements inequality comparison

func (*Value) Or added in v0.2.0

func (v *Value) Or(right *Value) (*Value, error)

Or implements logical OR operation

func (*Value) Subtract added in v0.2.0

func (v *Value) Subtract(right *Value) (*Value, error)

Subtract implements subtraction operation

type ValueType added in v0.2.0

type ValueType int

ValueType represents the type of a value.

const (
	// TypeInt represents an integer value type
	TypeInt ValueType = iota
	// TypeFloat represents a floating-point value type
	TypeFloat
	// TypeString represents a string value type
	TypeString
	// TypeBool represents a boolean value type
	TypeBool
	// TypeSlice represents a slice value type
	TypeSlice
	// TypeMap represents a map value type
	TypeMap
	// TypeNil represents a nil value type
	TypeNil
	// TypeStruct represents a struct value type
	TypeStruct
)

type VariableArg

type VariableArg struct {
	// contains filtered or unexported fields
}

VariableArg holds a variable argument.

func (VariableArg) Type

func (a VariableArg) Type() string

Type returns the argument type as "variable".

func (VariableArg) Value

func (a VariableArg) Value() interface{}

Value returns the variable name.

type VariableNode added in v0.2.0

type VariableNode struct {
	Name string
}

VariableNode represents a variable reference in the AST.

func (*VariableNode) Evaluate added in v0.2.0

func (n *VariableNode) Evaluate(ctx Context) (*Value, error)

Evaluate method implementation for VariableNode

Directories

Path Synopsis
examples
context command
Package main demonstrates template context usage.
Package main demonstrates template context usage.
filter_with_args command
Package main demonstrates template filters with arguments.
Package main demonstrates template filters with arguments.
filters command
Package main demonstrates template filter usage.
Package main demonstrates template filter usage.
hello_world command
Package main demonstrates a simple hello world template.
Package main demonstrates a simple hello world template.
object command
Package main demonstrates template object handling.
Package main demonstrates template object handling.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL