Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

dynreplacer.gno

3.58 Kb · 111 lines
  1// Package dynreplacer provides a simple template engine for handling dynamic
  2// content replacement. It is similar to strings.Replacer but with lazy
  3// execution of replacements, making it more optimization-friendly in several
  4// cases. While strings.Replacer requires all replacement values to be computed
  5// upfront, dynreplacer only executes the callback functions for placeholders
  6// that actually exist in the template, avoiding unnecessary computations.
  7//
  8// The package ensures efficient, non-recursive replacement of placeholders in a
  9// single pass. This lazy evaluation approach is particularly beneficial when:
 10// - Some replacement values are expensive to compute
 11// - Not all placeholders are guaranteed to be present in the template
 12// - Templates are reused with different content
 13//
 14// Example usage:
 15//
 16//	r := dynreplacer.New(
 17//	    dynreplacer.Pair{":name:", func() string { return "World" }},
 18//	    dynreplacer.Pair{":greeting:", func() string { return "Hello" }},
 19//	)
 20//	result := r.Replace("Hello :name:!") // Returns "Hello World!"
 21//
 22// The replacer caches computed values, so subsequent calls with the same
 23// placeholder will reuse the cached value instead of executing the callback
 24// again:
 25//
 26//	r := dynreplacer.New()
 27//	r.RegisterCallback(":expensive:", func() string { return "computed" })
 28//	r.Replace("Value1: :expensive:") // Computes the value
 29//	r.Replace("Value2: :expensive:") // Uses cached value
 30//	r.ClearCache()                   // Force re-computation on next use
 31package dynreplacer
 32
 33import (
 34	"strings"
 35)
 36
 37// Replacer manages dynamic placeholders, their associated functions, and cached
 38// values.
 39type Replacer struct {
 40	callbacks    map[string]func() string
 41	cachedValues map[string]string
 42}
 43
 44// Pair represents a placeholder and its callback function
 45type Pair struct {
 46	Placeholder string
 47	Callback    func() string
 48}
 49
 50// New creates a new Replacer instance with optional initial replacements.
 51// It accepts pairs where each pair consists of a placeholder string and
 52// its corresponding callback function.
 53//
 54// Example:
 55//
 56//	New(
 57//	    Pair{":name:", func() string { return "World" }},
 58//	    Pair{":greeting:", func() string { return "Hello" }},
 59//	)
 60func New(pairs ...Pair) *Replacer {
 61	r := &Replacer{
 62		callbacks:    make(map[string]func() string),
 63		cachedValues: make(map[string]string),
 64	}
 65
 66	for _, pair := range pairs {
 67		r.RegisterCallback(pair.Placeholder, pair.Callback)
 68	}
 69
 70	return r
 71}
 72
 73// RegisterCallback associates a placeholder with a function to generate its
 74// content.
 75func (r *Replacer) RegisterCallback(placeholder string, callback func() string) {
 76	r.callbacks[placeholder] = callback
 77}
 78
 79// Replace processes the given layout, replacing placeholders with cached or
 80// newly computed values.
 81func (r *Replacer) Replace(layout string) string {
 82	replacements := []string{}
 83
 84	// Check for placeholders and compute/retrieve values
 85	hasReplacements := false
 86	for placeholder, callback := range r.callbacks {
 87		if strings.Contains(layout, placeholder) {
 88			value, exists := r.cachedValues[placeholder]
 89			if !exists {
 90				value = callback()
 91				r.cachedValues[placeholder] = value
 92			}
 93			replacements = append(replacements, placeholder, value)
 94			hasReplacements = true
 95		}
 96	}
 97
 98	// If no replacements were found, return the original layout
 99	if !hasReplacements {
100		return layout
101	}
102
103	// Create a strings.Replacer with all computed replacements
104	replacer := strings.NewReplacer(replacements...)
105	return replacer.Replace(layout)
106}
107
108// ClearCache clears all cached values, forcing re-computation on next Replace.
109func (r *Replacer) ClearCache() {
110	r.cachedValues = make(map[string]string)
111}