github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/helper/fields/data.go (about) 1 package fields 2 3 import ( 4 "fmt" 5 6 multierror "github.com/hashicorp/go-multierror" 7 "github.com/mitchellh/mapstructure" 8 ) 9 10 // FieldData contains the raw data and the schema that the data should adhere to 11 type FieldData struct { 12 Raw map[string]interface{} 13 Schema map[string]*FieldSchema 14 } 15 16 // Validate cycles through the raw data and validates conversions in the schema. 17 // It also checks for the existence and value of required fields. 18 func (d *FieldData) Validate() error { 19 var result *multierror.Error 20 21 // Scan for missing required fields 22 for field, schema := range d.Schema { 23 if schema.Required { 24 _, ok := d.Raw[field] 25 if !ok { 26 result = multierror.Append(result, fmt.Errorf( 27 "field %q is required", field)) 28 } 29 } 30 } 31 32 // Validate field type and value 33 for field, value := range d.Raw { 34 schema, ok := d.Schema[field] 35 if !ok { 36 result = multierror.Append(result, fmt.Errorf( 37 "%q is an invalid field", field)) 38 continue 39 } 40 41 switch schema.Type { 42 case TypeBool, TypeInt, TypeMap, TypeArray, TypeString: 43 val, _, err := d.getPrimitive(field, schema) 44 if err != nil { 45 result = multierror.Append(result, fmt.Errorf( 46 "field %q with input %q doesn't seem to be of type %s", 47 field, value, schema.Type)) 48 } 49 // Check that we don't have an empty value for required fields 50 if schema.Required && val == schema.Type.Zero() { 51 result = multierror.Append(result, fmt.Errorf( 52 "field %q is required, but no value was found", field)) 53 } 54 default: 55 result = multierror.Append(result, fmt.Errorf( 56 "unknown field type %s for field %s", schema.Type, field)) 57 } 58 } 59 60 return result.ErrorOrNil() 61 } 62 63 // Get gets the value for the given field. If the key is an invalid field, 64 // FieldData will panic. If you want a safer version of this method, use 65 // GetOk. If the field k is not set, the default value (if set) will be 66 // returned, otherwise the zero value will be returned. 67 func (d *FieldData) Get(k string) interface{} { 68 schema, ok := d.Schema[k] 69 if !ok { 70 panic(fmt.Sprintf("field %s not in the schema", k)) 71 } 72 73 value, ok := d.GetOk(k) 74 if !ok { 75 value = schema.DefaultOrZero() 76 } 77 78 return value 79 } 80 81 // GetOk gets the value for the given field. The second return value 82 // will be false if the key is invalid or the key is not set at all. 83 func (d *FieldData) GetOk(k string) (interface{}, bool) { 84 schema, ok := d.Schema[k] 85 if !ok { 86 return nil, false 87 } 88 89 result, ok, err := d.GetOkErr(k) 90 if err != nil { 91 panic(fmt.Sprintf("error reading %s: %s", k, err)) 92 } 93 94 if ok && result == nil { 95 result = schema.DefaultOrZero() 96 } 97 98 return result, ok 99 } 100 101 // GetOkErr is the most conservative of all the Get methods. It returns 102 // whether key is set or not, but also an error value. The error value is 103 // non-nil if the field doesn't exist or there was an error parsing the 104 // field value. 105 func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) { 106 schema, ok := d.Schema[k] 107 if !ok { 108 return nil, false, fmt.Errorf("unknown field: %s", k) 109 } 110 111 switch schema.Type { 112 case TypeBool, TypeInt, TypeMap, TypeArray, TypeString: 113 return d.getPrimitive(k, schema) 114 default: 115 return nil, false, 116 fmt.Errorf("unknown field type %s for field %s", schema.Type, k) 117 } 118 } 119 120 // getPrimitive tries to convert the raw value of a field to its data type as 121 // defined in the schema. It does strict type checking, so the value will need 122 // to be able to convert to the appropriate type directly. 123 func (d *FieldData) getPrimitive( 124 k string, schema *FieldSchema) (interface{}, bool, error) { 125 raw, ok := d.Raw[k] 126 if !ok { 127 return nil, false, nil 128 } 129 130 switch schema.Type { 131 case TypeBool: 132 var result bool 133 if err := mapstructure.Decode(raw, &result); err != nil { 134 return nil, true, err 135 } 136 return result, true, nil 137 138 case TypeInt: 139 var result int 140 if err := mapstructure.Decode(raw, &result); err != nil { 141 return nil, true, err 142 } 143 return result, true, nil 144 145 case TypeString: 146 var result string 147 if err := mapstructure.Decode(raw, &result); err != nil { 148 return nil, true, err 149 } 150 return result, true, nil 151 152 case TypeMap: 153 var result map[string]interface{} 154 if err := mapstructure.Decode(raw, &result); err != nil { 155 return nil, true, err 156 } 157 return result, true, nil 158 159 case TypeArray: 160 var result []interface{} 161 if err := mapstructure.Decode(raw, &result); err != nil { 162 return nil, true, err 163 } 164 return result, true, nil 165 166 default: 167 panic(fmt.Sprintf("Unknown type: %s", schema.Type)) 168 } 169 }