requirements parsing

this shit is really brittle
master
Jordan Orelli 10 years ago
parent 407c5225bc
commit 0a2edd9fc4

@ -81,16 +81,9 @@ func (d *Doc) Get(path string, dest interface{}) error {
dv := reflect.ValueOf(dest)
dve := dv.Elem()
return setValue(dve, reflect.ValueOf(v))
}
func setValue(dest, src reflect.Value) error {
if dest.Type() == src.Type() {
dest.Set(src)
dve.Set(reflect.ValueOf(v))
return nil
}
return fmt.Errorf("src and destination are not of same type. src type: %s, dest type: %s", src.Type().Name(), dest.Type().Name())
}
// Fill takes the raw values from the moon document and assigns them to the
// struct pointed at by dest. Dest must be a struct pointer; any other type
@ -101,45 +94,25 @@ func (d *Doc) Fill(dest interface{}) error {
return fmt.Errorf("destination is of type %v; a pointer type is required", dt)
}
dv := reflect.ValueOf(dest).Elem()
n := dv.NumField()
for i := 0; i < n; i++ {
field, fv := dv.Type().Field(i), dv.Field(i)
tag := field.Tag
fopts, err := ReadString(string(tag))
reqs, err := requirements(dest)
if err != nil {
return fmt.Errorf("unable to parse options for type %s, field %s: %s",
dv.Type().Name(), field.Name, err)
}
var fname string
if err := fopts.Get("name", &fname); err != nil {
if _, ok := err.(NoValue); !ok {
return err
}
}
var frequired bool
if err := fopts.Get("required", &frequired); err != nil {
if _, ok := err.(NoValue); !ok {
return err
}
return fmt.Errorf("unable to gather requirements: %s", err)
}
fdefault := fopts.items["default"]
v, ok := d.items[fname]
if !ok {
if frequired {
return fmt.Errorf("required field missing: %s", fname)
dv := reflect.ValueOf(dest).Elem()
for fname, req := range reqs {
fv := dv.FieldByName(fname)
v, ok := d.items[req.name]
if ok {
fv.Set(reflect.ValueOf(v))
} else {
fv.Set(reflect.ValueOf(fdefault))
continue
if req.required {
return fmt.Errorf("required field missing: %s", fname)
}
if req.d_fault != nil {
fv.Set(reflect.ValueOf(req.d_fault))
}
}
fv.Set(reflect.ValueOf(v))
}
return nil
}

@ -41,6 +41,7 @@ func TestFill(t *testing.T) {
if err := doc.Fill(&dest); err != nil {
t.Error(err)
return
}
if dest.FirstName != "jordan" {

@ -0,0 +1,92 @@
package moon
import (
"fmt"
"reflect"
)
type req struct {
name string // name as it appears in moon config file. Defaults to the field name.
cliName string // name as it appears on the command line
help string // text given in help documentation
required bool // whether or not the option must be configured
d_fault interface{} // default value for when the option is missing
short string // short flag on the command line
long string // long flag on the command line
flag bool // whether or not the variable is a flag.
t reflect.Type
}
func (r req) validate() error {
if r.name == "" {
return fmt.Errorf("invalid requirement: requirement must have a name")
}
if r.required && r.flag {
return fmt.Errorf("invalid requirement %s: a flag cannot be required", r.name)
}
if r.required && r.d_fault != nil {
return fmt.Errorf("invalid requirement %s: a required value cannot have a default", r.name)
}
return nil
}
func field2req(field reflect.StructField) (*req, error) {
doc, err := ReadString(string(field.Tag))
if err != nil {
return nil, fmt.Errorf("unable to parse requirements for field %s: %s", field.Name, err)
}
req := req{
name: field.Name,
cliName: field.Name,
t: field.Type,
}
// it's really easy to cause infinite recursion here, since this is used by
// some of the higher up functions, so we're going to hack the document directly
errors := map[string]error{
"name": doc.Get("name", &req.name),
"cli_name": doc.Get("cli_name", &req.cliName),
"help": doc.Get("help", &req.help),
"required": doc.Get("required", &req.required),
"default": doc.Get("default", &req.d_fault),
"short": doc.Get("short", &req.short),
"long": doc.Get("long", &req.long),
"flag": doc.Get("flag", &req.flag),
}
for fname, err := range errors {
if err == nil {
continue
}
if _, ok := err.(NoValue); !ok {
return nil, fmt.Errorf("unable to parse requirement %s: %s", fname, err)
}
}
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)
}
dv := reflect.ValueOf(dest).Elem()
n := dv.NumField()
out := make(map[string]req, dv.NumField())
for i := 0; i < n; i++ {
field := dv.Type().Field(i)
req, err := field2req(field)
if err != nil {
return nil, fmt.Errorf("unable to gather requireents for field %s: %s", field.Name, err)
}
out[field.Name] = *req
}
return out, nil
}
Loading…
Cancel
Save