Skip to content

heedy/duck

Repository files navigation

MIT licensed Go Report Card Build Status Coverage Status GoDoc

Duck

Sometimes in golang a function returns an interface (or perhaps something was marshalled into an interface). Duck allows you to manipulate and convert from interfaces in a very simple manner. Duck is very eager to fit the inputs to the wanted format.

Type Conversions

The most basic actions are conversions between the standard built-in types.

//true, true
b,ok := duck.Bool(1.0)
//false, true
b,ok = duck.Bool(0.0)

//"1337", true
s, ok := duck.String(1337)
//"true", true
s, ok := duck.String(true)

//56, true
i, ok := duck.Int("56.0")

//1.0, true
f, ok := duck.Float(true)

var vptr interface{}

v := 6.0
vptr := &v	//duck follows pointers

//"6.0",true
s, ok = duck.String(vptr)
//6,true
i,ok = duck.Int(vptr)
//6.0,true
f,ok = duck.Float(vptr)
//true,true
b,ok = duck.Bool(vptr)

Comparisons

Duck allows comparing interfaces with similar conversions as the type functions.

//true, true ("34.5" < 35)
out, ok := duck.Lt("34.5",35)
//false, true
out, ok = duck.Lt("34.5",34)

//true,true
out, ok = duck.Eq("1.0", true)

//false,true - this one is unusual, true is defined as 1 (as in c)
out, ok = duck.Eq("2.0", true)

//false,true - false is defined as 0
out, ok = duck.Eq("2.0", false)

//true, true (34.6 >= 34)
out,ok = duck.Gte(34.6, 34)

if duck.Cmp(45,45.32)==duck.LessThan {
	//true!
}

if duck.Cmp(45," LOL ")==duck.CantCompare {
	//true!
}

if duck.Cmp(45,45.0)==duck.Equals {
	//true!
}

Objects

Duck gets very liberal with its duck-typing for objects. The main function here is Get which gets an element from the object. Since duck just loves duck-typing, as long as the requested element can be found in the given object, it is extracted - no matter if the object is a map, struct or array/slice.

floatarray := []float32{0.0,1.0,2.0,3.0,4.0}

// 1.0, true
e,ok := duck.Get(floatarray,1)
e,ok = duck.Get(floatarray,1.0)
e,ok = duck.Get(floatarray,true)
e,ok = duck.Get(floatarray,"1.0")

//Python-style indexing!
//4.0,true
e, ok = duck.Get(floatarray,-1.0)

//Currently, only map[string] is supported - panics on other map types!
smap := map[string]string{"foo": "bar", "true": "wow", "1": "one"}

//"bar", true
s, ok := duck.Get(smap, "foo")
//"wow", true
s, ok = duck.Get(smap,true)
// "one", true
s, ok = duck.Get(smap,1)	//Note that string conversions ALWAYS convert 1.000 -> 1

st := struct {
	MyElement string
	SecondElement int
}{"woo",1337}

//"woo", true
sv, ok := duck.Get(st,"MyElement")
//nil, false
sv, ok = duck.Get(st,"Nonexisting")

Duck-Tags

Sometimes you want structs to be recognized by a special tag in Get. The duck tag allows you to do that.

val := struct{
	A1 string `duck:"lol"`
	A2 string `duck:"-"`
}{"foo","bar"}

//nil,false
v,ok := duck.Get(val,"A1")
v,ok = duck.Get(val,"A2")

//"foo", true
v, ok = duck.Get(val,"lol")

Set

Initial support for setting values is also available.

var integer int
//true
ok := duck.Set(&integer,"54")
if (intger==54) {
	//true!
}

var mystring string
//true
ok = duck.Set(&mystring,13.0)
if (mystring=="13") {
	//true!
}

var iface interface{}

//true
ok = duck.Set(&iface, true)
//true!
_,ok = iface.(bool)

//Currently, only map[string]interface{} is supported for setting values
// reflect makes it very difficult to set map values. Structs and arrays work fine.
mymap := map[string]interface{}{"foo":"bar"}
ok = duck.Set(&mymap,1337,"foo")
//mymap["foo"]=1337 now

//Arbitrary object depth is supported
mysuperobject := struct{
	A1 []interface{}
	A2 string
}{
	A1: []interface{}{"hello","world"},
}
//world
duck.Get(mysuperobject,"A1",1)

//sets "world" to "not world anymore"
ok = duck.Set(&m,"not world anymore","A1",1)

If you need speed

Duck itself is very general, but not particularly fast. To work in restricted environments where speed is critical, a limited version of duck was created: quack. Quack is located in the quack subfolder, with the same API. It is much quicker.