fasttemplate

repository·master·Indexed 21 days ago

https://github.com/valyala/fasttemplate

A high-speed template engine for Go focused exclusively on placeholder substitution. Optimized for performance over logic or automatic escaping, it provides methods for both pre-parsed "frozen" templates and dynamic templates. It supports substitution via maps or custom TagFunc callbacks and is designed to be faster than text/template, strings.Replacer, and fmt.Fprintf for simple string interpolation.

Tokens
2.4K
Snippets
7
Records
9
Agent score
25%

What's inside fasttemplate

  1. What is fasttemplate and when to use it

    master

    fasttemplate is a high-performance Go template engine designed for a single task: substituting template placeholders with user-defined values.

    Key Characteristics

    • Performance: It is significantly faster than text/template, strings.Replace, strings.Replacer, and fmt.Fprintf for placeholder substitution.
    • No Escaping: Unlike html/template, it performs no automatic escaping. You are responsible for escaping data (e.g., HTML or URL escaping) before substitution.
    • Use Case: Use it when you need maximum speed for simple string interpolation. If you need a powerful HTML template engine with automatic security features, use quicktemplate instead.
  2. How frozen vs constantly changing templates work

    master

    Choosing the right method depends on whether your template string is static or dynamic:

    1. Frozen Templates (High Performance):

      • Use the *Template type.
      • First, call fasttemplate.New() or fasttemplate.NewTemplate() to parse the template.
      • Then, call t.Execute* methods.
      • Why? The template is pre-parsed into segments and tags, so execution only involves writing those segments and looking up tags in the map.
    2. Constantly Changing Templates (Low Overhead for single use):

      • Use the package-level functions like fasttemplate.ExecuteString().
      • Why? Creating a Template object involves allocation and parsing. If you only use a template once before it changes, the overhead of creating a Template object is higher than just parsing it on the fly.
  3. Advanced usage with fasttemplate.NewTemplate() and ExecuteFuncString()

    master

    If you need more control over how values are injected, use fasttemplate.NewTemplate combined with ExecuteFuncString.

    1. NewTemplate(template, start, end): Parses the template and returns a template object or an error. This is useful if you want to validate the template structure upfront.
    2. ExecuteFuncString(func): Instead of a map, you provide a callback function. This function is called for every placeholder found in the template. The callback receives an io.Writer and the tag (the content inside the delimiters) and must write the replacement value to the writer.
    	template := "Hello, [user]! You won [prize]!!! [foobar]"
    	t, err := fasttemplate.NewTemplate(template, "[", "]")
    	if err != nil {
    		log.Fatalf("unexpected error when parsing template: %s", err)
    	}
    	s := t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) {
    		switch tag {
    		case "user":
    			return w.Write([]byte("John"))
    		case "prize":
    			return w.Write([]byte("$100500"))
    		default:
    			return w.Write([]byte(fmt.Sprintf("[unknown tag %q]", tag)))
    		}
    	})
    	fmt.Printf("%s", s)
    
    	// Output:
    	// Hello, John! You won $100500!!! [unknown tag "foobar"]
  4. Basic usage with fasttemplate.New()

    master

    For simple placeholder substitution using a map of values, use fasttemplate.New. You specify the template string and the start and end delimiters (e.g., {{ and }}). Use ExecuteString to perform the substitution using a map[string]interface{}.

    Important: fasttemplate does not perform any escaping (like html/template does). You must manually escape values (e.g., using url.QueryEscape) before passing them to the template engine to prevent injection or malformed output.

    	template := "http://{{host}}/?q={{query}}&foo={{bar}}{{bar}}"
    	t := fasttemplate.New(template, "{{", "}}")
    	s := t.ExecuteString(map[string]interface{}{
    		"host":  "google.com",
    		"query": url.QueryEscape("hello=world"),
    		"bar":   "foobar",
    	})
    	fmt.Printf("%s", s)
    
    	// Output:
    	// http://google.com/?q=hello%3Dworld&foo=foobarfoobar
  5. Parse a template with New or NewTemplate

    master

    To use fasttemplate efficiently, you should first parse your template string into a Template object. This pre-parses the template, making subsequent executions much faster.

    • New(template, startTag, endTag): Parses the template. Panics if the template cannot be parsed (e.g., if an end tag is missing).
    • NewTemplate(template, startTag, endTag): Parses the template and returns an error instead of panicking if parsing fails.

    Once created, a Template object can be executed concurrently by multiple goroutines.

    import "github.com/valyala/fasttemplate"
    
    template := "Hello {{name}}!"
    // Using New (panics on error)
    t, err := fasttemplate.NewTemplate(template, "{{", "}}")
    if err != nil {
        // handle error
    }
  6. Reuse a Template object with Reset

    master

    To avoid repeated allocations, you can reuse an existing Template object by calling Reset. This allows you to change the template string, the start tag, or the end tag.

    Warning: Reset may be called only if no other goroutines are currently calling methods on that Template instance.

    t, _ := fasttemplate.NewTemplate("{{a}}", "{{", "}}")
    
    // Use it
    t.ExecuteString(map[string]interface{}{"a": "first"})
    
    // Reuse it with a new template
    t.Reset("{{b}}", "{{", "}}")
    t.ExecuteString(map[string]interface{}{"b": "second"})
  7. Execute a constantly changing template with top-level functions

    master

    If your template string changes frequently, do not use the Template object. Instead, use the package-level functions which are optimized for templates that are not pre-parsed.

    Methods:

    • Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error)
    • ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error)
    • ExecuteString(template, startTag, endTag string, m map[string]interface{}) string
    • ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string
    // For templates that change every time
    template := "Hello {{name}}"
    m := map[string]interface{}{"name": "Alice"}
    
    result := fasttemplate.ExecuteString(template, "{{", "}}", m)
  8. Execute a frozen template with a map

    master

    If you have a pre-parsed Template object (a "frozen" template), use the Execute* methods for maximum performance. You can provide a map m where keys are the placeholder names and values are the replacement data.

    Supported value types in the map:

    • []byte: The fastest value type.
    • string: A convenient value type.
    • TagFunc: A function for flexible, custom substitution.

    Methods:

    • Execute(w io.Writer, m map[string]interface{}) (int64, error): Writes the result to the writer w. Unknown placeholders are omitted.
    • ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error): Writes the result to w, but keeps unknown placeholders (useful as a strings.Replacer replacement).
    • ExecuteString(m map[string]interface{}) string: Returns the result as a string. Uses Execute internally.
    • ExecuteStringStd(m map[string]interface{}) string: Returns the result as a string, keeping unknown placeholders.
    // Assuming 't' is a *fasttemplate.Template
    m := map[string]interface{}{
        "name": "World",
        "id":   []byte("123"),
    }
    
    // Write to a buffer or stdout
    result := t.ExecuteString(m)
    fmt.Println(result)
  9. Use TagFunc for custom substitution logic

    master

    A TagFunc allows you to define custom logic for how a placeholder should be processed. This is useful for complex transformations or dynamic data fetching.

    type TagFunc func(w io.Writer, tag string) (int, error)

    When using a Template object, use ExecuteFunc or ExecuteFuncString. When using package-level functions, use ExecuteFunc or ExecuteFuncString.

    Note: TagFunc must be safe to call from concurrently running goroutines.

    // Custom function to handle a tag
    myFunc := func(w io.Writer, tag string) (int, error) {
        if tag == "upper" {
            _, err := w.Write([]byte("UPPERCASE"))
            return 9, err
        }
        return 0, nil
    }
    
    // Using it with a frozen template
    t.ExecuteFunc(os.Stdout, myFunc)