github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/internal/build/tags.go (about) 1 package build 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/opentelekomcloud/gophertelekomcloud/internal/multierr" 8 ) 9 10 type fieldValue struct { 11 // Name of the field in the type. 12 Name string 13 // Value of the field in the structure. 14 Value reflect.Value 15 // `required` tag value. 16 TagRequired bool 17 // `xor` tag value. 18 TagXOR string 19 // `or` tag value. 20 TagOR string 21 } 22 23 // ValidateTags validating structure by tags. 24 // 25 // Supported validations: 26 // 27 // required (`required:"true"`) - mark field required, returns error if it is empty. 28 // or: (`or:"OtherField"`) - requires at least one field to be not empty. 29 // xor: (`xor:"OtherField"`) - requires exactly of this and the other field to be set. 30 func ValidateTags(opts interface{}) error { 31 if opts == nil { 32 return nil // nil is an ideal value 33 } 34 35 optsValue := reflect.ValueOf(opts) 36 if optsValue.Kind() == reflect.Ptr { 37 optsValue = optsValue.Elem() 38 } 39 40 optsType := reflect.TypeOf(opts) 41 if optsType.Kind() == reflect.Ptr { 42 optsType = optsType.Elem() 43 } 44 45 fields := make(map[string]fieldValue) 46 47 if optsValue.Kind() != reflect.Struct { 48 return nil // no need to go deep 49 } 50 51 // fill the structure fields map 52 for i := 0; i < optsValue.NumField(); i++ { 53 value := optsValue.Field(i) 54 field := optsType.Field(i) 55 56 fields[field.Name] = fieldValue{ 57 Name: field.Name, 58 Value: value, 59 TagRequired: structFieldRequired(field), 60 TagXOR: field.Tag.Get("xor"), 61 TagOR: field.Tag.Get("or"), 62 } 63 } 64 65 errors := multierr.MultiError{} 66 67 for name, field := range fields { 68 fieldErrors := make([]error, 0) 69 70 if field.TagRequired && field.Value.IsZero() { 71 fieldErrors = append(fieldErrors, 72 fmt.Errorf("missing input for argument [%s]", name), 73 ) 74 } 75 76 orField := field.TagOR 77 if orField != "" && field.Value.IsZero() && fields[orField].Value.IsZero() { 78 fieldErrors = append(fieldErrors, 79 fmt.Errorf("at least one of %s and %s must be provided", name, orField), 80 ) 81 } 82 83 xorField := field.TagXOR 84 if xorField != "" && (field.Value.IsZero() == fields[xorField].Value.IsZero()) { 85 fieldErrors = append(fieldErrors, 86 fmt.Errorf("exactly one of %s and %s must be provided", name, xorField), 87 ) 88 } 89 90 errors = append(errors, fieldErrors...) 91 } 92 93 return errors.ErrorOrNil() 94 } 95 96 func structFieldRequired(field reflect.StructField) bool { 97 return field.Tag.Get("required") == "true" 98 }