Skip to content

Commit

Permalink
Allow map keys with brackets
Browse files Browse the repository at this point in the history
fixed #44
  • Loading branch information
darigaaz committed Jan 30, 2021
1 parent 6f187e4 commit 9527da5
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 52 deletions.
110 changes: 58 additions & 52 deletions formam.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,65 +210,71 @@ func (dec Decoder) init() error {
// analyzePath analyzes the current path to walk through it.
// For example: users[0].name
func (dec *Decoder) analyzePath() (err error) {
inBracket := false
bracketClosed := false
traversedByBracket := false
nesting := 0
lastPos := 0
endPos := 0

// parse path
for i, char := range []byte(dec.path) {
if char == '[' && inBracket == false {
// found an opening bracket
bracketClosed = false
inBracket = true
dec.field = dec.path[lastPos:i]
lastPos = i + 1
continue
} else if inBracket {
// it is inside of bracket, so get its value
if char == ']' {
// found an closing bracket, so it will be recently close, so put as true the bracketClosed
// and put as false inBracket and pass the value of bracket to dec.key
inBracket = false
bracketClosed = true
if endPos == 0 { // foo[] without number.
dec.index = dec.path[lastPos:i]
} else {
dec.index = dec.path[lastPos:endPos]
}
lastPos = i + 1
// traverse the path
err = dec.traverse()
// flush the index already used by traverse
dec.index = ""
// check if the "traverse" failed
if err != nil {
return
}
} else {
// still inside the bracket, so to save the end position
endPos = i + 1
}
continue
} else if !inBracket {
// not found any bracket, so try found a field
if char == '.' {
// found a field, we need to know if the field is next of a closing bracket,
// for example: [0].Field
if bracketClosed {
bracketClosed = false
lastPos = i + 1
continue
}
// found a field, but is not next of a closing bracket, for example: Field1.Field2
switch char {
case '[':
// save current access field
if nesting == 0 {
traversedByBracket = false
dec.field = dec.path[lastPos:i]
//dec.field = tmp[:i]
lastPos = i + 1
if err = dec.traverse(); err != nil {
return
}
}
continue
nesting += 1

case ']':
// no matching open bracket - regular character
if nesting == 0 {
continue
}

// decrease nesting
nesting -= 1
// if still inside outer brackets - regular character
// for example: [nested[brackets]]
if nesting > 0 {
continue
}

traversedByBracket = true
dec.index = dec.path[lastPos:i]
lastPos = i + 1
// traverse the path
err = dec.traverse()
// flush the index already used by traverse
dec.index = ""
// check if the "traverse" failed
if err != nil {
return
}

case '.':
// inside brackets - regular character
// for example: [key.with.dots]
if nesting > 0 {
continue
}

// found a field, we need to know if the field is next to a closing bracket,
// if it is then no need to traverse again
// for example: [0].Field
if traversedByBracket {
traversedByBracket = false
lastPos = i + 1
continue
}

// found a field, but is not next to a closing bracket,
// for example: Field1.Field2
dec.field = dec.path[lastPos:i]
lastPos = i + 1
if err = dec.traverse(); err != nil {
return
}
}
}

Expand Down
49 changes: 49 additions & 0 deletions formam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1026,3 +1026,52 @@ func errorContains(out error, want string) bool {
}
return strings.Contains(out.Error(), want)
}

func TestBracketsAndNestedPointers(t *testing.T) {
var s struct {
MapStringString map[string]string
MapStringPtrStruct map[string]struct {
ID string
}
MapStringMapStringString map[string]map[string]string
}

vals := url.Values{
"MapStringString[a[b][c]d]": []string{"MapStringString[a[b][c]d]"},
"MapStringString[name.with.dots]": []string{"MapStringString[name.with.dots]"},
"MapStringPtrStruct[k2]ID": []string{"MapStringPtrStruct[k2]ID"},
"MapStringMapStringString[a[b[c]d]]q]w": []string{"MapStringMapStringString[a[b[c]d]]q]w"},
}

dec := formam.NewDecoder(nil)

if err := dec.Decode(vals, &s); err != nil {
t.Fatalf("error when decode %s", err)
}

if v, ok := s.MapStringString["a[b][c]d"]; !ok {
t.Error("The key \"a[b][c]d\" in MapStringString does not exists")
} else if v != "MapStringString[a[b][c]d]" {
t.Error("The value in key \"a[b][c]d\" of MapStringString is incorrect")
}

if v, ok := s.MapStringString["name.with.dots"]; !ok {
t.Error("The key \"name.with.dots\" in MapStringString does not exists")
} else if v != "MapStringString[name.with.dots]" {
t.Error("The value in key \"name.with.dots\" of MapStringString is incorrect")
}

if v, ok := s.MapStringPtrStruct["k2"]; !ok {
t.Error("The key \"k2\" in MapStringPtrStruct does not exists")
} else if v.ID != "MapStringPtrStruct[k2]ID" {
t.Error("The value in key \"k2\" of MapStringPtrStruct is incorrect")
}

if v, ok := s.MapStringMapStringString["a[b[c]d]"]; !ok {
t.Error("The key \"a[b[c]d]\" in MapStringMapStringString does not exists")
} else if vv, ok := v["q]w"]; !ok {
t.Error("The key \"q]w\" in MapStringMapStringString[a[b[c]d]] does not exists")
} else if vv != "MapStringMapStringString[a[b[c]d]]q]w" {
t.Error("The value in key \"q]w\" of MapStringMapStringString[a[b[c]d]] is incorrect")
}
}

0 comments on commit 9527da5

Please sign in to comment.