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  }