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