Skip to content

Instantly share code, notes, and snippets.

@simon-engledew
Created May 27, 2022 18:12
Show Gist options
  • Save simon-engledew/8ddb3e7a3c52cc86e35d7567a525e298 to your computer and use it in GitHub Desktop.
Save simon-engledew/8ddb3e7a3c52cc86e35d7567a525e298 to your computer and use it in GitHub Desktop.
package server
import (
"fmt"
"reflect"
"go.starlark.net/starlark"
)
func toValue(v interface{}) starlark.Value {
switch v := v.(type) {
case uint8:
return starlark.MakeUint64(uint64(v))
case uint16:
return starlark.MakeUint64(uint64(v))
case uint32:
return starlark.MakeUint64(uint64(v))
case uint64:
return starlark.MakeUint64(v)
case uintptr:
return starlark.MakeUint64(uint64(v))
case uint:
return starlark.MakeUint64(uint64(v))
case int8:
return starlark.MakeInt64(int64(v))
case int16:
return starlark.MakeInt64(int64(v))
case int32:
return starlark.MakeInt64(int64(v))
case int64:
return starlark.MakeInt64(v)
case int:
return starlark.MakeInt64(int64(v))
case string:
return starlark.String(v)
case map[string]uint64:
// this is the only map type that we use in the api, if we ever want to
// add more maps to the api a more general approach will be necessary.
var r starlark.Dict
for k, v := range v {
r.SetKey(starlark.String(k), starlark.MakeUint64(v))
}
return &r
case nil:
return starlark.None
case error:
return starlark.String(v.Error())
default:
vval := reflect.ValueOf(v)
switch vval.Type().Kind() {
case reflect.Ptr:
if vval.IsNil() {
return starlark.None
}
vval = vval.Elem()
if vval.Type().Kind() == reflect.Struct {
return structAsStarlarkValue{vval}
}
case reflect.Struct:
return structAsStarlarkValue{vval}
case reflect.Slice:
return sliceAsStarlarkValue{vval}
}
return starlark.String(fmt.Sprintf("%v", v))
}
}
// sliceAsStarlarkValue converts a reflect.Value containing a slice
// into a starlark value.
// The public methods of sliceAsStarlarkValue implement the Indexable and
// Sequence starlark interfaces.
type sliceAsStarlarkValue struct {
v reflect.Value
}
var _ starlark.Indexable = sliceAsStarlarkValue{}
var _ starlark.Sequence = sliceAsStarlarkValue{}
func (v sliceAsStarlarkValue) Freeze() {
}
func (v sliceAsStarlarkValue) Hash() (uint32, error) {
return 0, fmt.Errorf("not hashable")
}
func (v sliceAsStarlarkValue) String() string {
return fmt.Sprintf("%#v", v.v)
}
func (v sliceAsStarlarkValue) Truth() starlark.Bool {
return v.v.Len() != 0
}
func (v sliceAsStarlarkValue) Type() string {
return v.v.Type().String()
}
func (v sliceAsStarlarkValue) Index(i int) starlark.Value {
if i >= v.v.Len() {
return nil
}
return toValue(v.v.Index(i).Interface())
}
func (v sliceAsStarlarkValue) Len() int {
return v.v.Len()
}
func (v sliceAsStarlarkValue) Iterate() starlark.Iterator {
return &sliceAsStarlarkValueIterator{0, v.v}
}
type sliceAsStarlarkValueIterator struct {
cur int
v reflect.Value
}
func (it *sliceAsStarlarkValueIterator) Done() {
}
func (it *sliceAsStarlarkValueIterator) Next(p *starlark.Value) bool {
if it.cur >= it.v.Len() {
return false
}
*p = toValue(it.v.Index(it.cur).Interface())
it.cur++
return true
}
// structAsStarlarkValue converts any Go struct into a starlark.Value.
// The public methods of structAsStarlarkValue implement the
// starlark.HasAttrs interface.
type structAsStarlarkValue struct {
v reflect.Value
}
func (v structAsStarlarkValue) AttrNames() []string {
var out []string
for i := 0; i < v.v.NumField(); i++ {
out = append(out, v.v.Type().Field(i).Name)
}
return out
}
var _ starlark.HasAttrs = structAsStarlarkValue{}
func (v structAsStarlarkValue) Freeze() {
}
func (v structAsStarlarkValue) Hash() (uint32, error) {
return 0, fmt.Errorf("not hashable")
}
func (v structAsStarlarkValue) String() string {
return fmt.Sprintf("%#v", v.v)
}
func (v structAsStarlarkValue) Truth() starlark.Bool {
return true
}
func (v structAsStarlarkValue) Type() string {
return v.v.Type().String()
}
func (v structAsStarlarkValue) Attr(name string) (starlark.Value, error) {
r := v.v.FieldByName(name)
if r == (reflect.Value{}) {
return starlark.None, fmt.Errorf("no field named %q in %T", name, v.v.Interface())
}
return toValue(r.Interface()), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment