options.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. // Package options provides a way to pass unstructured sets of options to a
  2. // component expecting a strongly-typed configuration structure.
  3. package options
  4. import (
  5. "fmt"
  6. "reflect"
  7. )
  8. // NoSuchFieldError is the error returned when the generic parameters hold a
  9. // value for a field absent from the destination structure.
  10. type NoSuchFieldError struct {
  11. Field string
  12. Type string
  13. }
  14. func (e NoSuchFieldError) Error() string {
  15. return fmt.Sprintf("no field %q in type %q", e.Field, e.Type)
  16. }
  17. // CannotSetFieldError is the error returned when the generic parameters hold a
  18. // value for a field that cannot be set in the destination structure.
  19. type CannotSetFieldError struct {
  20. Field string
  21. Type string
  22. }
  23. func (e CannotSetFieldError) Error() string {
  24. return fmt.Sprintf("cannot set field %q of type %q", e.Field, e.Type)
  25. }
  26. // Generic is an basic type to store arbitrary settings.
  27. type Generic map[string]interface{}
  28. // NewGeneric returns a new Generic instance.
  29. func NewGeneric() Generic {
  30. return make(Generic)
  31. }
  32. // GenerateFromModel takes the generic options, and tries to build a new
  33. // instance of the model's type by matching keys from the generic options to
  34. // fields in the model.
  35. //
  36. // The return value is of the same type than the model (including a potential
  37. // pointer qualifier).
  38. func GenerateFromModel(options Generic, model interface{}) (interface{}, error) {
  39. modType := reflect.TypeOf(model)
  40. // If the model is of pointer type, we need to dereference for New.
  41. resType := reflect.TypeOf(model)
  42. if modType.Kind() == reflect.Ptr {
  43. resType = resType.Elem()
  44. }
  45. // Populate the result structure with the generic layout content.
  46. res := reflect.New(resType)
  47. for name, value := range options {
  48. field := res.Elem().FieldByName(name)
  49. if !field.IsValid() {
  50. return nil, NoSuchFieldError{name, resType.String()}
  51. }
  52. if !field.CanSet() {
  53. return nil, CannotSetFieldError{name, resType.String()}
  54. }
  55. field.Set(reflect.ValueOf(value))
  56. }
  57. // If the model is not of pointer type, return content of the result.
  58. if modType.Kind() == reflect.Ptr {
  59. return res.Interface(), nil
  60. }
  61. return res.Elem().Interface(), nil
  62. }