filling values is now recursive

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

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

@ -122,32 +122,69 @@ func (o *Object) Fill(dest interface{}) error {
// dt = destination type // dt = destination type
dt := reflect.TypeOf(dest) dt := reflect.TypeOf(dest)
if dt.Kind() != reflect.Ptr { 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 { 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 { for fname, req := range reqs {
// fv = field value // field value
fv := dv.FieldByName(fname) fv := dv.FieldByName(fname)
v, ok := o.items[req.name] // object value
if ok { ov, ok := o.items[req.name]
if !fv.Type().AssignableTo(reflect.TypeOf(v)) { if !ok {
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)) // moon data is missing expected field
}
fv.Set(reflect.ValueOf(v))
} else {
if req.required { if req.required {
// if the field is required, that's an error
return fmt.Errorf("required field missing: %s", fname) return fmt.Errorf("required field missing: %s", fname)
} }
if req.d_fault != nil { if req.d_fault != nil {
// otherwise, we look for a user-defined default value
fv.Set(reflect.ValueOf(req.d_fault)) 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 return nil

@ -141,23 +141,32 @@ func ExampleDoc_Get_two() {
// Output: sean // Output: sean
} }
// func TestFillEmbeds(t *testing.T) { func TestFillEmbeds(t *testing.T) {
// in := `top: {val: some_data}` in := `top: {val: some_data}`
//
// var dest struct { var dest struct {
// Top *struct { Top *struct {
// Val string `name: val` Val string `name: val`
// } `name: top` } `name: top`
// } }
//
// doc, err := ReadString(in) doc, err := ReadString(in)
// if err != nil { if err != nil {
// t.Error(err) t.Error(err)
// return return
// } }
//
// if err := doc.Fill(&dest); err != nil { if err := doc.Fill(&dest); err != nil {
// t.Error(err) t.Error(err)
// return 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 return &req, nil
} }
func requirements(dest interface{}) (map[string]req, error) { // requirements gathers the moon requirements for a given struct type. the
dt := reflect.TypeOf(dest) // output is a mapping of field names to requirements.
if dt.Kind() != reflect.Ptr { func requirements(t reflect.Type) (map[string]req, error) {
return nil, fmt.Errorf("destination is of type %v; a pointer type is required", dt) 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 := t.NumField()
n := dv.NumField() out := make(map[string]req, t.NumField())
out := make(map[string]req, dv.NumField())
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
field := dv.Type().Field(i) field := t.Field(i)
req, err := field2req(field) req, err := field2req(field)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to gather requirements for field %s: %s", field.Name, err) return nil, fmt.Errorf("unable to gather requirements for field %s: %s", field.Name, err)

Loading…
Cancel
Save