/// <summary> /// Gets the value associated with the specified <paramref name="key"/>, /// recursively resolving placeholders. /// </summary> /// <param name="key"> /// The key whose value to resolve. /// </param> public string GetValue(string key) { if (key is null) { throw new ArgumentNullException(nameof(key)); } string resolveExpression(string input, ImmutableHashSet <string> path) { if (path.Contains(input)) { throw new ValueUnresolvableException( $"Encountered loop while trying to resolve expression: '{input}'." + $"Path: ['{String.Join("' -> '", path)}']" ); } var nextPath = path.Add(input); return(options .ToRegexPatterns() .Aggregate(input, (input, pattern) => Regex.Replace( input: input, pattern: pattern, evaluator: m => resolveOrMap(m, nextPath), options: RegexOptions.IgnoreCase ) )); } string resolveOrMap(Match match, ImmutableHashSet <string> path) => provider.TryGetValue(match.Groups[1].Value) switch { (true, string val) => resolveExpression(val, path), _ => mapUnresolvable(match.Value) ?? "", };