github.com/angryronald/go-kit@v0.0.0-20240505173814-ff2bd9c79dbf/types/types.go (about) 1 package types 2 3 import ( 4 "database/sql/driver" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "regexp" 9 "strconv" 10 "strings" 11 ) 12 13 // Metadata is an ADT to overcome the generic repo problem with JSONB Value 14 type Metadata map[string]interface{} 15 16 // Value override value's function for metadata (ADT) type 17 func (p Metadata) Value() (driver.Value, error) { 18 j, err := json.Marshal(p) 19 return j, err 20 } 21 22 // Scan override scan's function for metadata (ADT) type 23 func (p *Metadata) Scan(src interface{}) error { 24 source, ok := src.([]byte) 25 if !ok { 26 return errors.New("Type assertion .([]byte) failed") 27 } 28 29 var i interface{} 30 err := json.Unmarshal(source, &i) 31 if err != nil { 32 return err 33 } 34 35 if string(source) == "{}" || string(source) == "null" { 36 *p = map[string]interface{}{} 37 return nil 38 } 39 40 *p, ok = i.(map[string]interface{}) 41 if !ok { 42 return errors.New("Type assertion .(map[string]interface{}) failed") 43 } 44 45 return nil 46 } 47 48 // IntArray is an ADT to overcome the generic repo problem with pq.StringArray Value 49 type IntArray []int 50 51 // Value override value's function for IntArray (ADT) type 52 func (a IntArray) Value() (driver.Value, error) { 53 var strs []string 54 for _, i := range a { 55 strs = append(strs, strconv.FormatInt(int64(i), 10)) 56 } 57 return "{" + strings.Join(strs, ",") + "}", nil 58 } 59 60 // Scan override scan's function for IntArray (ADT) type 61 func (a *IntArray) Scan(src interface{}) error { 62 asBytes, ok := src.([]byte) 63 if !ok { 64 return error(errors.New("Scan source was not []bytes")) 65 } 66 67 asString := string(asBytes) 68 parsed, err := parseArrayInt(asString) 69 if err != nil { 70 return err 71 } 72 (*a) = IntArray(parsed) 73 74 return nil 75 } 76 77 // StringArray is an ADT to overcome the generic repo problem with pq.StringArray Value 78 type StringArray []string 79 80 // Value override value's function for StringArray (ADT) type 81 func (s StringArray) Value() (driver.Value, error) { 82 for i, elem := range s { 83 s[i] = `"` + strings.Replace(strings.Replace(elem, `\`, `\\\`, -1), `"`, `\"`, -1) + `"` 84 } 85 return "{" + strings.Join(s, ",") + "}", nil 86 } 87 88 // Scan override scan's function for StringArray (ADT) type 89 func (s *StringArray) Scan(src interface{}) error { 90 asBytes, ok := src.([]byte) 91 if !ok { 92 return error(errors.New("Scan source was not []bytes")) 93 } 94 95 asString := string(asBytes) 96 parsed := parseArrayString(asString) 97 (*s) = StringArray(parsed) 98 99 return nil 100 } 101 102 // construct a regexp to extract values: 103 var ( 104 // unquoted array values must not contain: (" , \ { } whitespace NULL) 105 // and must be at least one char 106 unquotedChar = `[^",\\{}\s(NULL)]` 107 unquotedValue = fmt.Sprintf("(%s)+", unquotedChar) 108 109 // quoted array values are surrounded by double quotes, can be any 110 // character except " or \, which must be backslash escaped: 111 quotedChar = `[^"\\]|\\"|\\\\` 112 quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar) 113 114 // an array value may be either quoted or unquoted: 115 arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue) 116 117 // Array values are separated with a comma IF there is more than one value: 118 arrayExp = regexp.MustCompile(fmt.Sprintf("%s", arrayValue)) 119 120 valueIndex int 121 ) 122 123 // Parse the output string from the array type. 124 // Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?) 125 func parseArrayString(array string) []string { 126 results := make([]string, 0) 127 matches := arrayExp.FindAllStringSubmatch(array, -1) 128 for _, match := range matches { 129 s := match[valueIndex] 130 // the string _might_ be wrapped in quotes, so trim them: 131 s = strings.Trim(s, "\"") 132 results = append(results, s) 133 } 134 return results 135 } 136 137 // Parse the output int from the array type. 138 // Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?) 139 func parseArrayInt(array string) ([]int, error) { 140 results := make([]int, 0) 141 matches := arrayExp.FindAllStringSubmatch(array, -1) 142 for _, match := range matches { 143 s := match[valueIndex] 144 // the string _might_ be wrapped in quotes, so trim them: 145 s = strings.Trim(s, "\"") 146 sInt, err := strconv.Atoi(s) 147 if err != nil { 148 return nil, err 149 } 150 results = append(results, sInt) 151 } 152 return results, nil 153 }