Unverified Commit 84b03696 authored by Hanzei's avatar Hanzei Committed by GitHub
Browse files

Refactor accessors (#53)

* Remove unused selecting via int

* Move test to package objx_test

* Add more tests

* Refactor access methode
parent 477a77ec
......@@ -46,103 +46,60 @@ func (m Map) Set(selector string, value interface{}) Map {
return m
}
// getIndex returns the index, which is hold in s by two braches.
// It also returns s withour the index part, e.g. name[1] will return (1, name).
// If no index is found, -1 is returned
func getIndex(s string) (int, string) {
arrayMatches := arrayAccesRegex.FindStringSubmatch(s)
if len(arrayMatches) > 0 {
// Get the key into the map
selector := arrayMatches[1]
// Get the index into the array at the key
// We know this cannt fail because arrayMatches[2] is an int for sure
index, _ := strconv.Atoi(arrayMatches[2])
return index, selector
}
return -1, s
}
// access accesses the object using the selector and performs the
// appropriate action.
func access(current, selector, value interface{}, isSet bool) interface{} {
switch selector.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
if array, ok := current.([]interface{}); ok {
index := intFromInterface(selector)
if index >= len(array) {
return nil
}
return array[index]
}
return nil
func access(current interface{}, selector string, value interface{}, isSet bool) interface{} {
selSegs := strings.SplitN(selector, PathSeparator, 2)
thisSel := selSegs[0]
index := -1
case string:
selStr := selector.(string)
selSegs := strings.SplitN(selStr, PathSeparator, 2)
thisSel := selSegs[0]
index := -1
var err error
if strings.Contains(thisSel, "[") {
arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
if len(arrayMatches) > 0 {
// Get the key into the map
thisSel = arrayMatches[1]
// Get the index into the array at the key
index, err = strconv.Atoi(arrayMatches[2])
if strings.Contains(thisSel, "[") {
index, thisSel = getIndex(thisSel)
}
if err != nil {
// This should never happen. If it does, something has gone
// seriously wrong. Panic.
panic("objx: Array index is not an integer. Must use array[int].")
}
}
}
if curMap, ok := current.(Map); ok {
current = map[string]interface{}(curMap)
}
// get the object in question
switch current.(type) {
case map[string]interface{}:
curMSI := current.(map[string]interface{})
if len(selSegs) <= 1 && isSet {
curMSI[thisSel] = value
return nil
}
current = curMSI[thisSel]
default:
current = nil
if curMap, ok := current.(Map); ok {
current = map[string]interface{}(curMap)
}
// get the object in question
switch current.(type) {
case map[string]interface{}:
curMSI := current.(map[string]interface{})
if len(selSegs) <= 1 && isSet {
curMSI[thisSel] = value
return nil
}
// do we need to access the item of an array?
if index > -1 {
if array, ok := current.([]interface{}); ok {
if index < len(array) {
current = array[index]
} else {
current = nil
}
current = curMSI[thisSel]
default:
current = nil
}
// do we need to access the item of an array?
if index > -1 {
if array, ok := current.([]interface{}); ok {
if index < len(array) {
current = array[index]
} else {
current = nil
}
}
if len(selSegs) > 1 {
current = access(current, selSegs[1], value, isSet)
}
}
return current
}
// intFromInterface converts an interface object to the largest
// representation of an unsigned integer using a type switch and
// assertions
func intFromInterface(selector interface{}) int {
var value int
switch selector.(type) {
case int:
value = selector.(int)
case int8:
value = int(selector.(int8))
case int16:
value = int(selector.(int16))
case int32:
value = int(selector.(int32))
case int64:
value = int(selector.(int64))
case uint:
value = int(selector.(uint))
case uint8:
value = int(selector.(uint8))
case uint16:
value = int(selector.(uint16))
case uint32:
value = int(selector.(uint32))
case uint64:
value = int(selector.(uint64))
default:
return 0
if len(selSegs) > 1 {
current = access(current, selSegs[1], value, isSet)
}
return value
return current
}
package objx
package objx_test
import (
"testing"
"github.com/stretchr/objx"
"github.com/stretchr/testify/assert"
)
func TestAccessorsAccessGetSingleField(t *testing.T) {
current := Map{"name": "Tyler"}
m := objx.Map{"name": "Tyler"}
assert.Equal(t, "Tyler", current.Get("name").Data())
assert.Equal(t, "Tyler", m.Get("name").Data())
}
func TestAccessorsAccessGetSingleFieldInt(t *testing.T) {
current := Map{"name": 10}
m := objx.Map{"name": 10}
assert.Equal(t, 10, current.Get("name").Data())
assert.Equal(t, 10, m.Get("name").Data())
}
func TestAccessorsAccessGetDeep(t *testing.T) {
current := Map{
"name": Map{
m := objx.Map{
"name": objx.Map{
"first": "Tyler",
"last": "Bunnell",
},
}
assert.Equal(t, "Tyler", current.Get("name.first").Data())
assert.Equal(t, "Bunnell", current.Get("name.last").Data())
assert.Equal(t, "Tyler", m.Get("name.first").Data())
assert.Equal(t, "Bunnell", m.Get("name.last").Data())
}
func TestAccessorsAccessGetDeepDeep(t *testing.T) {
current := Map{
"one": Map{
"two": Map{
"three": Map{
m := objx.Map{
"one": objx.Map{
"two": objx.Map{
"three": objx.Map{
"four": 4,
},
},
},
}
assert.Equal(t, 4, current.Get("one.two.three.four").Data())
assert.Equal(t, 4, m.Get("one.two.three.four").Data())
}
func TestAccessorsAccessGetInsideArray(t *testing.T) {
current := Map{
m := objx.Map{
"names": []interface{}{
Map{
objx.Map{
"first": "Tyler",
"last": "Bunnell",
},
Map{
objx.Map{
"first": "Capitol",
"last": "Bollocks",
},
},
}
assert.Equal(t, "Tyler", current.Get("names[0].first").Data())
assert.Equal(t, "Bunnell", current.Get("names[0].last").Data())
assert.Equal(t, "Capitol", current.Get("names[1].first").Data())
assert.Equal(t, "Bollocks", current.Get("names[1].last").Data())
assert.Equal(t, "Tyler", m.Get("names[0].first").Data())
assert.Equal(t, "Bunnell", m.Get("names[0].last").Data())
assert.Equal(t, "Capitol", m.Get("names[1].first").Data())
assert.Equal(t, "Bollocks", m.Get("names[1].last").Data())
assert.Nil(t, current.Get("names[2]").Data())
}
func TestAccessorsAccessGetFromArrayWithInt(t *testing.T) {
current := []interface{}{
map[string]interface{}{
"first": "Tyler",
"last": "Bunnell",
},
map[string]interface{}{
"first": "Capitol",
"last": "Bollocks",
},
}
one := access(current, 0, nil, false)
two := access(current, 1, nil, false)
three := access(current, 2, nil, false)
assert.Equal(t, "Tyler", one.(map[string]interface{})["first"])
assert.Equal(t, "Capitol", two.(map[string]interface{})["first"])
assert.Nil(t, three)
}
func TestAccessorsAccessGetFromArrayWithIntTypes(t *testing.T) {
current := []interface{}{
"abc",
"def",
}
assert.Equal(t, "abc", access(current, 0, nil, false))
assert.Equal(t, "def", access(current, 1, nil, false))
assert.Nil(t, access(current, 2, nil, false))
assert.Equal(t, "abc", access(current, int8(0), nil, false))
assert.Equal(t, "def", access(current, int8(1), nil, false))
assert.Nil(t, access(current, int8(2), nil, false))
assert.Equal(t, "abc", access(current, int16(0), nil, false))
assert.Equal(t, "def", access(current, int16(1), nil, false))
assert.Nil(t, access(current, int16(2), nil, false))
assert.Equal(t, "abc", access(current, int32(0), nil, false))
assert.Equal(t, "def", access(current, int32(1), nil, false))
assert.Nil(t, access(current, int32(2), nil, false))
assert.Equal(t, "abc", access(current, int64(0), nil, false))
assert.Equal(t, "def", access(current, int64(1), nil, false))
assert.Nil(t, access(current, int64(2), nil, false))
assert.Equal(t, "abc", access(current, uint(0), nil, false))
assert.Equal(t, "def", access(current, uint(1), nil, false))
assert.Nil(t, access(current, uint(2), nil, false))
assert.Equal(t, "abc", access(current, uint8(0), nil, false))
assert.Equal(t, "def", access(current, uint8(1), nil, false))
assert.Nil(t, access(current, uint8(2), nil, false))
assert.Equal(t, "abc", access(current, uint16(0), nil, false))
assert.Equal(t, "def", access(current, uint16(1), nil, false))
assert.Nil(t, access(current, uint16(2), nil, false))
assert.Equal(t, "abc", access(current, uint32(0), nil, false))
assert.Equal(t, "def", access(current, uint32(1), nil, false))
assert.Nil(t, access(current, uint32(2), nil, false))
assert.Equal(t, "abc", access(current, uint64(0), nil, false))
assert.Equal(t, "def", access(current, uint64(1), nil, false))
assert.Nil(t, access(current, uint64(2), nil, false))
}
func TestAccessorsAccessGetFromArrayWithIntError(t *testing.T) {
current := Map{"name": "Tyler"}
assert.Nil(t, access(current, 0, nil, false))
assert.Nil(t, m.Get("names[2]").Data())
assert.Nil(t, m.Get("names[]").Data())
assert.Nil(t, m.Get("names1]]").Data())
assert.Nil(t, m.Get("names[1]]").Data())
assert.Nil(t, m.Get("names[[1]]").Data())
assert.Nil(t, m.Get("names[[1]").Data())
assert.Nil(t, m.Get("names[[1").Data())
}
func TestAccessorsGet(t *testing.T) {
current := Map{"name": "Tyler"}
m := objx.Map{"name": "Tyler"}
assert.Equal(t, "Tyler", current.Get("name").Data())
assert.Equal(t, "Tyler", m.Get("name").Data())
}
func TestAccessorsAccessSetSingleField(t *testing.T) {
current := Map{"name": "Tyler"}
m := objx.Map{"name": "Tyler"}
current.Set("name", "Mat")
current.Set("age", 29)
m.Set("name", "Mat")
m.Set("age", 29)
assert.Equal(t, current["name"], "Mat")
assert.Equal(t, current["age"], 29)
assert.Equal(t, m.Get("name").Data(), "Mat")
assert.Equal(t, m.Get("age").Data(), 29)
}
func TestAccessorsAccessSetSingleFieldNotExisting(t *testing.T) {
current := Map{
m := objx.Map{
"first": "Tyler",
"last": "Bunnell",
}
current.Set("name", "Mat")
m.Set("name", "Mat")
assert.Equal(t, current["name"], "Mat")
assert.Equal(t, m.Get("name").Data(), "Mat")
}
func TestAccessorsAccessSetDeep(t *testing.T) {
current := Map{
"name": Map{
m := objx.Map{
"name": objx.Map{
"first": "Tyler",
"last": "Bunnell",
},
}
current.Set("name.first", "Mat")
current.Set("name.last", "Ryer")
m.Set("name.first", "Mat")
m.Set("name.last", "Ryer")
assert.Equal(t, "Mat", current.Get("name.first").Data())
assert.Equal(t, "Ryer", current.Get("name.last").Data())
assert.Equal(t, "Mat", m.Get("name.first").Data())
assert.Equal(t, "Ryer", m.Get("name.last").Data())
}
func TestAccessorsAccessSetDeepDeep(t *testing.T) {
current := Map{
"one": Map{
"two": Map{
"three": Map{
"four": 4},
m := objx.Map{
"one": objx.Map{
"two": objx.Map{
"three": objx.Map{
"four": 4,
},
},
},
}
current.Set("one.two.three.four", 5)
m.Set("one.two.three.four", 5)
assert.Equal(t, 5, current.Get("one.two.three.four").Data())
assert.Equal(t, 5, m.Get("one.two.three.four").Data())
}
func TestAccessorsAccessSetArray(t *testing.T) {
current := Map{
m := objx.Map{
"names": []interface{}{"Tyler"},
}
current.Set("names[0]", "Mat")
m.Set("names[0]", "Mat")
assert.Equal(t, "Mat", current.Get("names[0]").Data())
assert.Equal(t, "Mat", m.Get("names[0]").Data())
}
func TestAccessorsAccessSetInsideArray(t *testing.T) {
current := Map{
m := objx.Map{
"names": []interface{}{
Map{
objx.Map{
"first": "Tyler",
"last": "Bunnell",
},
Map{
objx.Map{
"first": "Capitol",
"last": "Bollocks",
},
},
}
current.Set("names[0].first", "Mat")
current.Set("names[0].last", "Ryer")
current.Set("names[1].first", "Captain")
current.Set("names[1].last", "Underpants")
m.Set("names[0].first", "Mat")
m.Set("names[0].last", "Ryer")
m.Set("names[1].first", "Captain")
m.Set("names[1].last", "Underpants")
assert.Equal(t, "Mat", current.Get("names[0].first").Data())
assert.Equal(t, "Ryer", current.Get("names[0].last").Data())
assert.Equal(t, "Captain", current.Get("names[1].first").Data())
assert.Equal(t, "Underpants", current.Get("names[1].last").Data())
assert.Equal(t, "Mat", m.Get("names[0].first").Data())
assert.Equal(t, "Ryer", m.Get("names[0].last").Data())
assert.Equal(t, "Captain", m.Get("names[1].first").Data())
assert.Equal(t, "Underpants", m.Get("names[1].last").Data())
}
func TestAccessorsSet(t *testing.T) {
current := Map{"name": "Tyler"}
m := objx.Map{"name": "Tyler"}
current.Set("name", "Mat")
m.Set("name", "Mat")
assert.Equal(t, "Mat", current.Get("name").data)
assert.Equal(t, "Mat", m.Get("name").Data())
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment