Skip to content

Commit

Permalink
Implement Marshal porting Rack's code and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
derekstavis committed Nov 22, 2016
1 parent 9b947ca commit 8bdb561
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 0 deletions.
69 changes: 69 additions & 0 deletions marshal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package qs

import (
"fmt"
"net/url"
)

func Marshal(hash map[string]interface{}) (string, error) {
return buildNestedQuery(hash, "")
}

func buildNestedQuery(value interface{}, prefix string) (string, error) {
components := ""

switch vv := value.(type) {
case []interface{}:
for i, v := range vv {
component, err := buildNestedQuery(v, prefix+"[]")

if err != nil {
return "", err
}

components += component

if i < len(vv)-1 {
components += "&"
}
}

case map[string]interface{}:
length := len(vv)

for k, v := range vv {
childPrefix := ""

if prefix != "" {
childPrefix = prefix + "[" + url.QueryEscape(k) + "]"
} else {
childPrefix = url.QueryEscape(k)
}

component, err := buildNestedQuery(v, childPrefix)

if err != nil {
return "", err
}

components += component
length -= 1

if length > 0 {
components += "&"
}
}

case string:
if prefix == "" {
return "", fmt.Errorf("value must be a map[string]interface{}")
}

components += prefix + "=" + url.QueryEscape(vv)

default:
components += prefix
}

return components, nil
}
47 changes: 47 additions & 0 deletions marshal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package qs

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestMarshal(t *testing.T) {
testables := []map[string]interface{}{
map[string]interface{}{"foo": "bar"},
map[string]interface{}{"foo": "1", "bar": "2"},
map[string]interface{}{"my weird field": "q1!2\"'w$5&7/z8)?"},
map[string]interface{}{"foo": []interface{}{nil}},
map[string]interface{}{"foo": []interface{}{""}},
map[string]interface{}{"foo": []interface{}{"bar"}},
map[string]interface{}{"foo": nil, "bar": ""},
map[string]interface{}{"foo": "bar", "baz": ""},
map[string]interface{}{"foo": []interface{}{"1", "2"}},
map[string]interface{}{"foo": "bar", "baz": []interface{}{"1", "2", "3"}},
map[string]interface{}{"foo": []interface{}{"bar"}, "baz": []interface{}{"1", "2", "3"}},
map[string]interface{}{"foo": []interface{}{"1", "2"}},
map[string]interface{}{"foo": "bar", "baz": []interface{}{"1", "2", "3"}},
map[string]interface{}{"x": map[string]interface{}{"y": map[string]interface{}{"z": "1"}}},
map[string]interface{}{"x": map[string]interface{}{"y": map[string]interface{}{"z": []interface{}{"1"}}}},
map[string]interface{}{"x": map[string]interface{}{"y": map[string]interface{}{"z": []interface{}{"1", "2"}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": "1"}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": []interface{}{"1"}}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": "1", "w": "2"}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"v": map[string]interface{}{"w": "1"}}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": "1", "v": map[string]interface{}{"w": "2"}}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": "1"}, map[string]interface{}{"z": "2"}}}},
map[string]interface{}{"x": map[string]interface{}{"y": []interface{}{map[string]interface{}{"z": "1", "w": "a"}, map[string]interface{}{"z": "2", "w": "3"}}}},
}

for _, v := range testables {
querystring, err := Marshal(v)

if assert.NoError(t, err) {
hash, err := Unmarshal(querystring)

if assert.NoError(t, err) {
assert.Equal(t, hash, v)
}
}
}
}
File renamed without changes.
File renamed without changes.

0 comments on commit 8bdb561

Please sign in to comment.