func propertyEncoder(v reflect.Value) (string, error) { vi := v.Interface() if p, err := properties.PropertyFromInterface(vi); err != nil { // return early if interface fails its own validation return "", err } else if p.HasNameAndValue() { // if an interface encodes its own name and value, it's a property return properties.MarshalProperty(p), nil } return "", nil }
func marshalStruct(v reflect.Value) (string, error) { var out []string // iterate over all fields vtype := v.Type() n := vtype.NumField() for i := 0; i < n; i++ { // keep a reference to the field value and definition fv := v.Field(i) fs := vtype.Field(i) // use the field definition to extract out property defaults p := properties.PropertyFromStructField(fs) if p == nil { continue // skip explicitly ignored fields and private members } fi := fv.Interface() // some fields are not properties, but actually nested objects. // detect those early using the property and object encoder... if _, ok := fi.(properties.CanEncodeValue); !ok && !isInvalidOrEmptyValue(fv) { if encoded, err := encode(fv, objectEncoder); err != nil { msg := fmt.Sprintf("unable to encode field %s", fs.Name) return "", utils.NewError(marshalStruct, msg, v.Interface(), err) } else if encoded != "" { // encoding worked! no need to process as a property out = append(out, encoded) continue } } // now check to see if the field value overrides the defaults... if !isInvalidOrEmptyValue(fv) { // first, check the field value interface for overrides... if overrides, err := properties.PropertyFromInterface(fi); err != nil { msg := fmt.Sprintf("field %s failed validation", fs.Name) return "", utils.NewError(marshalStruct, msg, v.Interface(), err) } else if p.Merge(overrides); p.Value == "" { // then, if we couldn't find an override from the interface, // try the simple string encoder... if p.Value, err = stringEncoder(fv); err != nil { msg := fmt.Sprintf("unable to encode field %s", fs.Name) return "", utils.NewError(marshalStruct, msg, v.Interface(), err) } } } // make sure we have a value by this point if !p.HasNameAndValue() { if p.OmitEmpty { continue } else if p.DefaultValue != "" { p.Value = p.DefaultValue } else if p.Required { msg := fmt.Sprintf("missing value for required field %s", fs.Name) return "", utils.NewError(Marshal, msg, v.Interface(), nil) } } // encode in the property out = append(out, properties.MarshalProperty(p)) } // wrap the fields in the enclosing struct tags return tagAndJoinValue(v, out) }