Skip to content

Commit

Permalink
Merge pull request #79 from lukasjarosch/78-cannot-replace-variables-…
Browse files Browse the repository at this point in the history
…with-values-of-type-map-from-another-class-sometimes

78 cannot replace variables with values of type map from another class sometimes
  • Loading branch information
lukasjarosch authored Feb 14, 2024
2 parents 06234ed + 60de4b2 commit 15bf3bb
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 82 deletions.
18 changes: 17 additions & 1 deletion data.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ func (d Data) GetPath(path ...interface{}) (tree interface{}, err error) {
return nil, fmt.Errorf("key not found: %v", el)
}

case map[string]interface{}:
key, ok := el.(string)
if !ok {
return nil, fmt.Errorf("unexpected string key in map[string]interface '%T' at index %d", el, i)
}
tree, ok = node[key]
if !ok {
return nil, fmt.Errorf("key not found: %v", el)
}

case map[interface{}]interface{}:
var ok bool
tree, ok = node[el]
Expand Down Expand Up @@ -154,6 +164,13 @@ func (d *Data) SetPath(value interface{}, path ...interface{}) (err error) {
}
node[key] = value

case map[string]interface{}:
key, ok := element.(string)
if !ok {
return fmt.Errorf("unexpected string key in map[string]interface '%T' at index %d", element, i)
}
node[key] = value

case []interface{}:
index, ok := element.(int)
if !ok {
Expand Down Expand Up @@ -228,7 +245,6 @@ func (d Data) FindValues(valueFunc FindValueFunc, target *[]interface{}) (err er

var walk func(reflect.Value, []interface{}) error
walk = func(v reflect.Value, path []interface{}) error {

// fix indirects through pointers and interfaces
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
v = v.Elem()
Expand Down
82 changes: 1 addition & 81 deletions variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,7 @@ func ReplaceVariables(data Data, classFiles []*Class, predefinedVariables map[st
}

// replace variable in Data
data.SetPath(sourceValue, variable.Identifier...)

return nil
return data.SetPath(sourceValue, variable.Identifier...)
}

// Replace variables in an undefined amount of iterations.
Expand Down Expand Up @@ -226,84 +224,6 @@ func ReplaceVariables(data Data, classFiles []*Class, predefinedVariables map[st
return nil
}

func replaceVariable(data Data, variable Variable, classFiles []*Class, predefinedVariables map[string]interface{}) (err error) {
isPredefinedVariable := func(variable Variable) bool {
for name := range predefinedVariables {
if strings.EqualFold(variable.Name, name) {
return true
}
}
return false
}

var targetValue interface{}
if isPredefinedVariable(variable) {
targetValue = predefinedVariables[variable.Name]
return nil
} else {
// targetValue is the value on which the variable points to.
// This is the value we need to replace the variable with
targetValue, err = data.GetPath(variable.NameAsIdentifier()...)
if err != nil {

// for any other error than a 'key not found' there is nothing we can do
if !strings.Contains(err.Error(), "key not found") {
return fmt.Errorf("reference to invalid variable '%s': %w", variable.FullName(), err)
}

// Local variable handling
//
// at this point we have failed to resolve the variable using 'absolute' paths
// but the variable may be only locally defined which means we need to change the lookup path.
// We iterate over all classes and attempt to resolve the variable within that limited scope.
for i, class := range classFiles {

// if the value to which the variable points is valid inside the class scope, we just need to add the class identifier
// if the combination works this means we have found ourselves a local variable and we can set the targetValue
fullPath := []interface{}{}
fullPath = append(fullPath, class.NameAsIdentifier()...)

// edge case: the class root key is 'foo', and the variable used references it like ${foo:bar:baz}
// this would result in the full path being 'foo foo bar baz', hence we need to strip the class name from the variable reference.
if strings.EqualFold(class.RootKey(), variable.NameAsIdentifier()[0].(string)) {
fullPath = append(fullPath, variable.NameAsIdentifier()[1:]...)
} else {
// default case: the class root key is not used in the variable, we can add the full variable identifier
fullPath = append(fullPath, variable.NameAsIdentifier()...)
}

targetValue, err = data.GetPath(fullPath...)

// as long as not all classes have been checked, we cannot be sure that the variable is undefined (aka. key not found error)
if targetValue == nil &&
i < len(classFiles) &&
strings.Contains(err.Error(), "key not found") {
continue
}

// the local variable is really not defined at this point
if err != nil {
return fmt.Errorf("reference to invalid variable '%s': %w", variable.FullName(), err)
}

break
}
}
}

// sourceValue is the value where the variable is located. It needs to be replaced with the 'targetValue'
sourceValue, err := data.GetPath(variable.Identifier...)
if err != nil {
return err
}

// Replace the full variable name (${variable}) with the targetValue
sourceValue = strings.ReplaceAll(fmt.Sprint(sourceValue), variable.FullName(), fmt.Sprint(targetValue))
data.SetPath(sourceValue, variable.Identifier...)

return nil
}

// variableFindValueFunc implements the [FindValueFunc] and searches for variables inside [Data].
// Variables are extracted by matching the values to the [variableRegex].
// All found variables are initialized and added to the output.
Expand Down

0 comments on commit 15bf3bb

Please sign in to comment.