errors.go raw

   1  // Package edgegriderr is used for parsing validation errors to make them more readable.
   2  // It formats error(s) in a way, that the return value is one formatted error type, consisting of all the errors that occurred
   3  // in human-readable form. It is important to provide all the validation errors to the function.
   4  // Usage example:
   5  //
   6  //	error := edgegriderr.ParseValidationErrors(validation.Errors{
   7  //		"Validation1": validation.Validate(...),
   8  //		"Validation2": validation.Validate(...),
   9  //	})
  10  package edgegriderr
  11  
  12  import (
  13  	"fmt"
  14  	"sort"
  15  	"strconv"
  16  	"strings"
  17  
  18  	validation "github.com/go-ozzo/ozzo-validation/v4"
  19  )
  20  
  21  // ParseValidationErrors parses validation errors into easily readable form
  22  // The output error is formatted with indentations and struct field indexing for collections
  23  func ParseValidationErrors(e validation.Errors) error {
  24  	if e.Filter() == nil {
  25  		return nil
  26  	}
  27  
  28  	parser := validationErrorsParser()
  29  	return fmt.Errorf("%s", strings.TrimSuffix(parser(e, "", 0), "\n"))
  30  }
  31  
  32  // validationErrorsParser returns a function that parses validation errors
  33  // Returned function takes validation.Errors, field to be indexed (empty at the beginning) and index size at start as parameters
  34  func validationErrorsParser() func(validation.Errors, string, int) string {
  35  	var parser func(validation.Errors, string, int) string
  36  
  37  	parser = func(validationErrors validation.Errors, indexedFieldName string, indentSize int) string {
  38  		keys := getSortedKeys(validationErrors)
  39  
  40  		var s strings.Builder
  41  		for _, key := range keys {
  42  			if validationErrors[key] == nil {
  43  				continue
  44  			}
  45  			errs, ok := validationErrors[key].(validation.Errors)
  46  			if !ok {
  47  				fmt.Fprintf(&s, "%s%s: %s\n", indent(indentSize), key, validationErrors[key].Error())
  48  				continue
  49  			}
  50  
  51  			if _, err := strconv.Atoi(key); err != nil {
  52  				if hasNumericKeys(errs) {
  53  					fmt.Fprintf(&s, "%s", parser(errs, key, indentSize))
  54  				} else {
  55  					fmt.Fprintf(&s, "%s%s: {\n%s%s}\n", indent(indentSize), key, parser(errs, key, indentSize+1), indent(indentSize))
  56  				}
  57  				continue
  58  			}
  59  
  60  			fmt.Fprintf(&s, "%s%s[%s]: {\n%s%s}\n", indent(indentSize), indexedFieldName, key, parser(errs, "", indentSize+1), indent(indentSize))
  61  		}
  62  
  63  		return s.String()
  64  	}
  65  
  66  	return parser
  67  }
  68  
  69  func getSortedKeys(errs validation.Errors) []string {
  70  	keys := make([]string, 0, len(errs))
  71  	for k := range errs {
  72  		keys = append(keys, k)
  73  	}
  74  	sort.Strings(keys)
  75  	return keys
  76  }
  77  
  78  func indent(size int) string {
  79  	return strings.Repeat("\t", size)
  80  }
  81  
  82  func hasNumericKeys(errs validation.Errors) bool {
  83  	for key := range errs {
  84  		if _, err := strconv.Atoi(key); err != nil {
  85  			return false
  86  		}
  87  	}
  88  	return true
  89  }
  90