diff --git a/pkg/environment/apply.go b/pkg/environment/apply.go deleted file mode 100644 index d15115f4..00000000 --- a/pkg/environment/apply.go +++ /dev/null @@ -1,115 +0,0 @@ -package config - -import ( - "fmt" - "reflect" - "regexp" - - "github.com/xiatechs/jsonata-go" -) - -var placeholder = regexp.MustCompile(`\$\{\{\s*([^\}]*)\s*\}\}`) - -// Apply applies the environment represented by 'environment' to the input value 'val'. -// It replaces placeholders in the input value with corresponding values from the environment. -func Apply(val any, environment *Environment) (any, error) { - environment.mu.RLock() - defer environment.mu.RUnlock() - - v := reflect.ValueOf(val) - result := deepUnSpecify(v) - - if err := applyRecursive(result, environment.data); err != nil { - return nil, err - } - - return result.Interface(), nil -} - -func applyRecursive(node reflect.Value, environment map[string]any) error { - switch node.Kind() { - case reflect.Map: - for _, key := range node.MapKeys() { - value := reflect.ValueOf(node.MapIndex(key).Interface()) - - switch value.Kind() { - case reflect.String: - strValue := value.String() - newValue, err := replacePlaceholders(strValue, environment) - if err != nil { - return err - } - - node.SetMapIndex(key, reflect.ValueOf(newValue)) - case reflect.Map, reflect.Slice: - if err := applyRecursive(value, environment); err != nil { - return err - } - } - } - case reflect.Slice: - for i := 0; i < node.Len(); i++ { - elem := reflect.ValueOf(node.Index(i).Interface()) - err := applyRecursive(elem, environment) - if err != nil { - return err - } - } - } - return nil -} - -func replacePlaceholders(input string, environment map[string]any) (any, error) { - matches := placeholder.FindAllStringSubmatch(input, -1) - - if len(matches) == 0 { - return input, nil - } - if len(matches) == 1 { - return evaluateExpression(matches[0][1], environment) - } - - return placeholder.ReplaceAllStringFunc(input, func(match string) string { - matches := placeholder.FindStringSubmatch(match) - if len(matches) != 2 { - return match - } - expression := matches[1] - - value, err := evaluateExpression(expression, environment) - if err != nil { - return match - } - - return fmt.Sprintf("%v", value) - }), nil -} - -func evaluateExpression(expression string, environment map[string]any) (any, error) { - exp, err := jsonata.Compile(expression) - if err != nil { - return nil, err - } - exp.RegisterVars(environment) - return exp.Eval(nil) -} - -func deepUnSpecify(original reflect.Value) reflect.Value { - switch original.Kind() { - case reflect.Map: - result := reflect.MakeMap(reflect.MapOf(original.Type().Key(), typeAny)) - for _, key := range original.MapKeys() { - value := original.MapIndex(key) - result.SetMapIndex(key, deepUnSpecify(value)) - } - return result - case reflect.Slice: - result := reflect.MakeSlice(reflect.SliceOf(typeAny), original.Len(), original.Len()) - for i := 0; i < original.Len(); i++ { - result.Index(i).Set(deepUnSpecify(original.Index(i))) - } - return result - default: - return original - } -} diff --git a/pkg/environment/appy_test.go b/pkg/environment/appy_test.go deleted file mode 100644 index d103445b..00000000 --- a/pkg/environment/appy_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package config - -import ( - "fmt" - "testing" - - "github.com/go-faker/faker/v4" - "github.com/stretchr/testify/assert" -) - -func TestApply(t *testing.T) { - t.Run("Match Single Placeholder", func(t *testing.T) { - c := New() - - key1 := faker.Word() - key2 := faker.Word() - - key := fmt.Sprintf("%s.%s", key1, key2) - value := 1 - - c.Set(key, value) - - origin := map[string]string{key: fmt.Sprintf("${{ $%s }}", key)} - - result, err := Apply(origin, c) - assert.NoError(t, err) - assert.Equal(t, map[string]any{key: value}, result) - }) - - t.Run("Match Multiple Placeholders", func(t *testing.T) { - c := New() - - key1 := faker.Word() - key2 := faker.Word() - - key := fmt.Sprintf("%s.%s", key1, key2) - value := 1 - - c.Set(key, value) - - origin := map[string]string{key: fmt.Sprintf("${{ $%s }} ${{ $%s }}", key, key)} - - result, err := Apply(origin, c) - assert.NoError(t, err) - assert.Equal(t, map[string]any{key: fmt.Sprintf("%d %d", value, value)}, result) - }) -} diff --git a/pkg/environment/environment.go b/pkg/template/values.go similarity index 70% rename from pkg/environment/environment.go rename to pkg/template/values.go index 5624c2ad..4fc7dcb5 100644 --- a/pkg/environment/environment.go +++ b/pkg/template/values.go @@ -1,4 +1,4 @@ -package config +package template import ( "fmt" @@ -7,30 +7,30 @@ import ( "sync" ) -// Environment represents the environment of your application. -type Environment struct { +// Values represents the environment of your application. +type Values struct { data map[string]any mu sync.RWMutex } var typeAny = reflect.TypeOf((*any)(nil)).Elem() -// New creates a new environment instance. -func New() *Environment { - return &Environment{ +// NewValues creates a new environment instance. +func NewValues() *Values { + return &Values{ data: make(map[string]any), } } // Set sets the value for the specified key in the environment. -func (c *Environment) Set(key string, val any) error { - c.mu.Lock() - defer c.mu.Unlock() +func (vl *Values) Set(key string, val any) error { + vl.mu.Lock() + defer vl.mu.Unlock() tokens := strings.Split(key, ".") v := reflect.ValueOf(val) - node := reflect.ValueOf(c.data) + node := reflect.ValueOf(vl.data) for i, token := range tokens { t := reflect.ValueOf(token) @@ -55,13 +55,13 @@ func (c *Environment) Set(key string, val any) error { // Get retrieves the value for the specified key from the environment. // If the key is not present, it returns a default value and a boolean indicating the key's existence. -func (c *Environment) Get(key string) (any, bool) { - c.mu.RLock() - defer c.mu.RUnlock() +func (vl *Values) Get(key string) (any, bool) { + vl.mu.RLock() + defer vl.mu.RUnlock() tokens := strings.Split(key, ".") - node := reflect.ValueOf(c.data) + node := reflect.ValueOf(vl.data) for _, token := range tokens { t := reflect.ValueOf(token) @@ -80,27 +80,27 @@ func (c *Environment) Get(key string) (any, bool) { } // Delete removes the specified key from the environment. -func (c *Environment) Delete(key string) { - c.mu.Lock() - defer c.mu.Unlock() +func (vl *Values) Delete(key string) { + vl.mu.Lock() + defer vl.mu.Unlock() tokens := strings.Split(key, ".") - c.deleteRecursive(reflect.ValueOf(c.data), tokens) + vl.deleteRecursive(reflect.ValueOf(vl.data), tokens) } // GetData returns a read-only copy of the environment data. -func (c *Environment) GetData() map[string]any { - c.mu.RLock() - defer c.mu.RUnlock() +func (vl *Values) GetData() map[string]any { + vl.mu.RLock() + defer vl.mu.RUnlock() - result := make(map[string]any, len(c.data)) - for k, v := range c.data { + result := make(map[string]any, len(vl.data)) + for k, v := range vl.data { result[k] = v } return result } -func (c *Environment) deleteRecursive(node reflect.Value, tokens []string) bool { +func (vl *Values) deleteRecursive(node reflect.Value, tokens []string) bool { t := reflect.ValueOf(tokens[0]) if node.Kind() != reflect.Map || node.Type().Key().Kind() != reflect.String { @@ -121,7 +121,7 @@ func (c *Environment) deleteRecursive(node reflect.Value, tokens []string) bool return false } child = reflect.ValueOf(child.Interface()) - if c.deleteRecursive(child, tokens[1:]) { + if vl.deleteRecursive(child, tokens[1:]) { if child.Len() == 0 { node.SetMapIndex(t, reflect.Value{}) } diff --git a/pkg/environment/environment_test.go b/pkg/template/values_test.go similarity index 61% rename from pkg/environment/environment_test.go rename to pkg/template/values_test.go index 13ab0a16..5815a76d 100644 --- a/pkg/environment/environment_test.go +++ b/pkg/template/values_test.go @@ -1,4 +1,4 @@ -package config +package template import ( "fmt" @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" ) -func TestEnvironment_GetAndSetAndDelete(t *testing.T) { - env := New() +func TestValues_GetAndSetAndDelete(t *testing.T) { + vl := NewValues() key1 := faker.Word() key2 := faker.Word() @@ -17,21 +17,21 @@ func TestEnvironment_GetAndSetAndDelete(t *testing.T) { key := fmt.Sprintf("%s.%s", key1, key2) value := faker.Word() - env.Set(key, value) + vl.Set(key, value) - r1, ok := env.Get(key) + r1, ok := vl.Get(key) assert.True(t, ok) assert.Equal(t, value, r1) - _, ok = env.Get(key1) + _, ok = vl.Get(key1) assert.True(t, ok) - env.Delete(key) + vl.Delete(key) - r2, ok := env.Get(key) + r2, ok := vl.Get(key) assert.False(t, ok) assert.Nil(t, r2) - _, ok = env.Get(key1) + _, ok = vl.Get(key1) assert.False(t, ok) }