func (r *Runtime) Eval(template map[string]interface{}) (map[string]interface{}, error) {
	var err error
	uriToProviderHash := func(uri *url.URL) string {
		bs := []byte{}
		bs = append(bs, []byte(uri.Scheme)...)
		query := uri.Query().Encode()
		bs = append(bs, []byte(query)...)
		return fmt.Sprintf("%x", md5.Sum(bs))
	}
	createProvider := func(scheme string, uri *url.URL) (api.Provider, error) {
		query := uri.Query()
		m := map[string]interface{}{}
		for key, params := range query {
			if len(params) > 0 {
				m[key] = params[0]
			}
		}
		conf := config.MapConfig{M: m}
		switch scheme {
		case ProviderVault:
			p := vault.New(conf)
			return p, nil
		case ProviderS3:
			p := s3.New(conf)
			return p, nil
		case ProviderSSM:
			p := ssm.New(conf)
			return p, nil
		case ProviderSecretsManager:
			p := awssecrets.New(conf)
			return p, nil
		case ProviderSOPS:
			p := sops.New(conf)
			return p, nil
		case ProviderEcho:
			p := echo.New(conf)
			return p, nil
		case ProviderFile:
			p := file.New(conf)
			return p, nil
		case ProviderGCPSecretManager:
			p := gcpsecrets.New(conf)
			return p, nil
		case ProviderTFState:
			p := tfstate.New(conf, "")
			return p, nil
		case ProviderTFStateGS:
			p := tfstate.New(conf, "gs")
			return p, nil
		case ProviderTFStateS3:
			p := tfstate.New(conf, "s3")
			return p, nil
		case ProviderAzureKeyVault:
			p := azurekeyvault.New(conf)
			return p, nil
		}
		return nil, fmt.Errorf("no provider registered for scheme %q", scheme)
	}
	var only []string
	if r.Options.ExcludeSecret {
		only = []string{"ref"}
	}
	expand := expansion.ExpandRegexMatch{
		Only:   only,
		Target: expansion.DefaultRefRegexp,
		Lookup: func(key string) (string, error) {
			if val, ok := r.docCache.Get(key); ok {
				valStr, ok := val.(string)
				if !ok {
					return "", fmt.Errorf("error reading string from cache: unsupported value type %T", val)
				}
				return valStr, nil
			}
			uri, err := url.Parse(key)
			if err != nil {
				return "", err
			}
			hash := uriToProviderHash(uri)
			p, ok := r.providers[hash]
			if !ok {
				var scheme string
				scheme = uri.Scheme
				scheme = strings.Split(scheme, "://")[0]
				p, err = createProvider(scheme, uri)
				if err != nil {
					return "", err
				}
				r.providers[hash] = p
			}
			var frag string
			frag = uri.Fragment
			frag = strings.TrimPrefix(frag, "#")
			frag = strings.TrimPrefix(frag, "/")
			var components []string
			var host string
			{
				host = uri.Host
				if host != "" {
					components = append(components, host)
				}
			}
			{
				path2 := uri.Path
				path2 = strings.TrimPrefix(path2, "#")
				if host != "" {
					path2 = strings.TrimPrefix(path2, "/")
				}
				if path2 != "" {
					components = append(components, path2)
				}
			}
			path := strings.Join(components, "/")
			if len(frag) == 0 {
				var str string
				cacheKey := key
				if cachedStr, ok := r.strCache.Get(cacheKey); ok {
					str, ok = cachedStr.(string)
					if !ok {
						return "", fmt.Errorf("error reading str from cache: unsupported value type %T", cachedStr)
					}
				} else {
					str, err = p.GetString(path)
					if err != nil {
						return "", err
					}
					r.strCache.Add(cacheKey, str)
				}
				return str, nil
			} else {
				mapRequestURI := key[:strings.LastIndex(key, uri.Fragment)-1]
				var obj map[string]interface{}
				if cachedMap, ok := r.docCache.Get(mapRequestURI); ok {
					obj, ok = cachedMap.(map[string]interface{})
					if !ok {
						return "", fmt.Errorf("error reading map from cache: unsupported value type %T", cachedMap)
					}
				} else {
					obj, err = p.GetStringMap(path)
					if err != nil {
						return "", err
					}
					r.docCache.Add(mapRequestURI, obj)
				}
				keys := strings.Split(frag, "/")
				for i, k := range keys {
					newobj := map[string]interface{}{}
					switch t := obj[k].(type) {
					case string:
						if i != len(keys)-1 {
							return "", fmt.Errorf("unexpected type of value for key at %d=%s in %v: expected map[string]interface{}, got %v(%T)", i, k, keys, t, t)
						}
						r.docCache.Add(key, t)
						return t, nil
					case map[string]interface{}:
						newobj = t
					case map[interface{}]interface{}:
						for k, v := range t {
							newobj[fmt.Sprintf("%v", k)] = v
						}
					}
					obj = newobj
				}
				return "", fmt.Errorf("no value found for key %s", frag)
			}
		},
	}
	ret, err := expand.InMap(template)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
Eval replaces 'ref+<provider>://xxxxx' entries by their actual values
package vals
import (
	"crypto/md5"
	"errors"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"strings"
	"github.com/variantdev/vals/pkg/config"
	"github.com/variantdev/vals/pkg/providers/s3"
	lru "github.com/hashicorp/golang-lru"
	"github.com/variantdev/vals/pkg/api"
	"github.com/variantdev/vals/pkg/expansion"
	"github.com/variantdev/vals/pkg/providers/awssecrets"
	"github.com/variantdev/vals/pkg/providers/azurekeyvault"
	"github.com/variantdev/vals/pkg/providers/echo"
	"github.com/variantdev/vals/pkg/providers/file"
	"github.com/variantdev/vals/pkg/providers/gcpsecrets"
	"github.com/variantdev/vals/pkg/providers/sops"
	"github.com/variantdev/vals/pkg/providers/ssm"
	"github.com/variantdev/vals/pkg/providers/tfstate"
	"github.com/variantdev/vals/pkg/providers/vault"
	"github.com/variantdev/vals/pkg/stringmapprovider"
	"github.com/variantdev/vals/pkg/stringprovider"
	"gopkg.in/yaml.v3"
)
const (
	TypeMap    = "map"
	TypeString = "string"
	FormatRaw  = "raw"
	FormatYAML = "yaml"
	KeyProvider   = "provider"
	KeyName       = "name"
	KeyKeys       = "keys"
	KeyPaths      = "paths"
	KeyType       = "type"
	KeyFormat     = "format"
	KeyInline     = "inline"
	KeyPrefix     = "prefix"
	KeyPath       = "path"
	KeySetForKey  = "setForKeys"
	KeySet        = "set"
	KeyValuesFrom = "valuesFrom"
	defaultCacheSize = 512
	ProviderVault            = "vault"
	ProviderS3               = "s3"
	ProviderSSM              = "awsssm"
	ProviderSecretsManager   = "awssecrets"
	ProviderSOPS             = "sops"
	ProviderEcho             = "echo"
	ProviderFile             = "file"
	ProviderGCPSecretManager = "gcpsecrets"
	ProviderTFState          = "tfstate"
	ProviderTFStateGS        = "tfstategs"
	ProviderTFStateS3        = "tfstates3"
	ProviderAzureKeyVault    = "azurekeyvault"
)
type Evaluator interface {
	Eval(map[string]interface{}) (map[string]interface{}, error)
}
type Runtime struct {
	providers map[string]api.Provider
	docCache  *lru.Cache  
	strCache  *lru.Cache  
	Options Options
}
func New(opts Options) (*Runtime, error) {
	cacheSize := opts.CacheSize
	if cacheSize == 0 {
		cacheSize = defaultCacheSize
	}
	r := &Runtime{
		providers: map[string]api.Provider{},
		Options:   opts,
	}
	var err error
	r.docCache, err = lru.New(cacheSize)
	if err != nil {
		return nil, err
	}
	r.strCache, err = lru.New(cacheSize)
	if err != nil {
		return nil, err
	}
	return r, nil
}