102 lines
2.1 KiB
Go
102 lines
2.1 KiB
Go
package smartconfig
|
|
|
|
import "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 {
|
|
continue
|
|
}
|
|
value = interpolatedValue
|
|
}
|
|
|
|
// Resolve the value
|
|
resolver, ok := c.resolvers[resolverName]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
resolved, err := resolver.Resolve(value)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
// Replace the match
|
|
result = result[:pos.start] + resolved + result[pos.end:]
|
|
changed = true
|
|
}
|
|
|
|
if changed {
|
|
depth++
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|