filling values is now recursive

master
Jordan Orelli 10 years ago
parent 844a6e2eeb
commit 981d7978f5

@ -8,7 +8,7 @@ import (
)
func parseArgs(args []string, dest interface{}) (*Object, error) {
reqs, err := requirements(dest)
reqs, err := requirements(reflect.TypeOf(dest))
if err != nil {
return nil, fmt.Errorf("unable to parse args: bad requirements: %s", err)
}
@ -125,7 +125,7 @@ func parseArgs(args []string, dest interface{}) (*Object, error) {
}
func showHelp(dest interface{}) {
reqs, err := requirements(dest)
reqs, err := requirements(reflect.TypeOf(dest))
if err != nil {
panic(err)
}

@ -122,32 +122,69 @@ func (o *Object) Fill(dest interface{}) error {
// dt = destination type
dt := reflect.TypeOf(dest)
if dt.Kind() != reflect.Ptr {
return fmt.Errorf("destination is of type %v; a pointer type is required", dt)
return fmt.Errorf("destination is of type %v (%v); a pointer type is required", dt, dt.Kind())
}
// ensure the pointer points to a struct type
if dt.Elem().Kind() != reflect.Struct {
return fmt.Errorf("destination is a pointer to a non-struct type: %v (pointer to struct required)", dt.Elem())
}
reqs, err := requirements(dest)
// value of our struct pointer
pv := reflect.ValueOf(dest)
// value of the struct being pointed to
v := pv.Elem()
return o.fillValue(v)
}
func (o *Object) fillValue(dv reflect.Value) error {
switch dv.Kind() {
case reflect.Struct:
// this is fine
case reflect.Ptr:
if dv.IsNil() {
dv.Set(reflect.New(dv.Type().Elem()))
}
dv = reflect.Indirect(dv)
default:
return fmt.Errorf("moon object can only fillValue to a struct value, saw %v (%v)", dv.Type(), dv.Kind())
}
// the destination defines the requirements (i.e., the method of unpacking
// our moon data)
reqs, err := requirements(dv.Type())
if err != nil {
return fmt.Errorf("unable to gather requirements: %s", err)
return fmt.Errorf("unable to gather requirements: %v", err)
}
// dv = destination value
dv := reflect.ValueOf(dest).Elem()
for fname, req := range reqs {
// fv = field value
// field value
fv := dv.FieldByName(fname)
v, ok := o.items[req.name]
if ok {
if !fv.Type().AssignableTo(reflect.TypeOf(v)) {
return fmt.Errorf("unable to assign field %s: source type %v is not assignable to destination type %v", req.name, fv.Type(), reflect.TypeOf(v))
}
fv.Set(reflect.ValueOf(v))
} else {
// object value
ov, ok := o.items[req.name]
if !ok {
// moon data is missing expected field
if req.required {
// if the field is required, that's an error
return fmt.Errorf("required field missing: %s", fname)
}
if req.d_fault != nil {
// otherwise, we look for a user-defined default value
fv.Set(reflect.ValueOf(req.d_fault))
}
continue
}
switch t_ov := ov.(type) {
case *Object:
return t_ov.fillValue(fv)
default:
if !fv.Type().AssignableTo(reflect.TypeOf(ov)) {
return fmt.Errorf("unable to assign field %s: source type %v is not assignable to destination type %v", req.name, reflect.TypeOf(ov), fv.Type())
}
fv.Set(reflect.ValueOf(ov))
}
}
return nil

@ -141,23 +141,32 @@ func ExampleDoc_Get_two() {
// Output: sean
}
// func TestFillEmbeds(t *testing.T) {
// in := `top: {val: some_data}`
//
// var dest struct {
// Top *struct {
// Val string `name: val`
// } `name: top`
// }
//
// doc, err := ReadString(in)
// if err != nil {
// t.Error(err)
// return
// }
//
// if err := doc.Fill(&dest); err != nil {
// t.Error(err)
// return
// }
// }
func TestFillEmbeds(t *testing.T) {
in := `top: {val: some_data}`
var dest struct {
Top *struct {
Val string `name: val`
} `name: top`
}
doc, err := ReadString(in)
if err != nil {
t.Error(err)
return
}
if err := doc.Fill(&dest); err != nil {
t.Error(err)
return
}
if dest.Top == nil {
t.Error("didn't actually set a value")
return
}
if dest.Top.Val != "some_data" {
t.Errorf("expected some_data, got %v", dest.Top.Val)
}
}

@ -87,18 +87,22 @@ func field2req(field reflect.StructField) (*req, error) {
return &req, nil
}
func requirements(dest interface{}) (map[string]req, error) {
dt := reflect.TypeOf(dest)
if dt.Kind() != reflect.Ptr {
return nil, fmt.Errorf("destination is of type %v; a pointer type is required", dt)
// requirements gathers the moon requirements for a given struct type. the
// output is a mapping of field names to requirements.
func requirements(t reflect.Type) (map[string]req, error) {
switch t.Kind() {
case reflect.Ptr:
t = t.Elem()
case reflect.Struct:
default:
return nil, fmt.Errorf("destination is of type %v; a pointer or struct type is required", t)
}
dv := reflect.ValueOf(dest).Elem()
n := dv.NumField()
out := make(map[string]req, dv.NumField())
n := t.NumField()
out := make(map[string]req, t.NumField())
for i := 0; i < n; i++ {
field := dv.Type().Field(i)
field := t.Field(i)
req, err := field2req(field)
if err != nil {
return nil, fmt.Errorf("unable to gather requirements for field %s: %s", field.Name, err)

Loading…
Cancel
Save