smartconfig/interpolate.go
sneak 8a38afba5e passes tests, has cli filter now.
* still has not been *really* tested yet
2025-07-20 15:29:06 +02:00

105 lines
2.3 KiB
Go

package smartconfig
import (
"fmt"
"strings"
)
// findInterpolations finds all ${...} patterns in the string, handling nested cases
func findInterpolations(s string) []struct{ start, end int } {
var results []struct{ start, end int }
for i := 0; i < len(s); i++ {
if i+2 < len(s) && s[i:i+2] == "${" {
// Found start of interpolation
start := i
braceCount := 1
j := i + 2
// Find matching closing brace
for j < len(s) && braceCount > 0 {
if j+2 <= len(s) && s[j:j+2] == "${" {
braceCount++
j += 2
} else if s[j] == '}' {
braceCount--
j++
} else {
j++
}
}
if braceCount == 0 {
results = append(results, struct{ start, end int }{start, j})
i = j - 1 // Skip past this interpolation
}
}
}
return results
}
// interpolate handles nested interpolations properly
func (c *Config) interpolate(content string, depth int) (string, error) {
if depth >= maxRecursionDepth {
return content, nil
}
result := content
changed := true
for changed && depth < maxRecursionDepth {
changed = false
positions := findInterpolations(result)
// Process from end to beginning to maintain string positions
for i := len(positions) - 1; i >= 0; i-- {
pos := positions[i]
fullMatch := result[pos.start:pos.end]
// Extract the content inside ${}
inner := fullMatch[2 : len(fullMatch)-1]
// Find the resolver and value
colonIdx := strings.Index(inner, ":")
if colonIdx == -1 {
continue
}
resolverName := inner[:colonIdx]
value := inner[colonIdx+1:]
// Check if value contains nested interpolations
if strings.Contains(value, "${") {
// Recursively interpolate the value first
interpolatedValue, err := c.interpolate(value, depth+1)
if err != nil {
return "", err
}
value = interpolatedValue
}
// Resolve the value
resolver, ok := c.resolvers[resolverName]
if !ok {
return "", fmt.Errorf("unknown resolver: %s", resolverName)
}
resolved, err := resolver.Resolve(value)
if err != nil {
return "", fmt.Errorf("failed to resolve %s:%s: %w", resolverName, value, err)
}
// Replace the match
result = result[:pos.start] + resolved + result[pos.end:]
changed = true
}
if changed {
depth++
}
}
return result, nil
}