mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-10-19 15:55:08 +00:00
173 lines
4.2 KiB
Go
173 lines
4.2 KiB
Go
|
package types
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"compress/gzip"
|
||
|
"database/sql/driver"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
|
||
|
"io/ioutil"
|
||
|
)
|
||
|
|
||
|
// GzippedText is a []byte which transparently gzips data being submitted to
|
||
|
// a database and ungzips data being Scanned from a database.
|
||
|
type GzippedText []byte
|
||
|
|
||
|
// Value implements the driver.Valuer interface, gzipping the raw value of
|
||
|
// this GzippedText.
|
||
|
func (g GzippedText) Value() (driver.Value, error) {
|
||
|
b := make([]byte, 0, len(g))
|
||
|
buf := bytes.NewBuffer(b)
|
||
|
w := gzip.NewWriter(buf)
|
||
|
w.Write(g)
|
||
|
w.Close()
|
||
|
return buf.Bytes(), nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// Scan implements the sql.Scanner interface, ungzipping the value coming off
|
||
|
// the wire and storing the raw result in the GzippedText.
|
||
|
func (g *GzippedText) Scan(src interface{}) error {
|
||
|
var source []byte
|
||
|
switch src := src.(type) {
|
||
|
case string:
|
||
|
source = []byte(src)
|
||
|
case []byte:
|
||
|
source = src
|
||
|
default:
|
||
|
return errors.New("Incompatible type for GzippedText")
|
||
|
}
|
||
|
reader, err := gzip.NewReader(bytes.NewReader(source))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer reader.Close()
|
||
|
b, err := ioutil.ReadAll(reader)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
*g = GzippedText(b)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// JSONText is a json.RawMessage, which is a []byte underneath.
|
||
|
// Value() validates the json format in the source, and returns an error if
|
||
|
// the json is not valid. Scan does no validation. JSONText additionally
|
||
|
// implements `Unmarshal`, which unmarshals the json within to an interface{}
|
||
|
type JSONText json.RawMessage
|
||
|
|
||
|
var emptyJSON = JSONText("{}")
|
||
|
|
||
|
// MarshalJSON returns the *j as the JSON encoding of j.
|
||
|
func (j JSONText) MarshalJSON() ([]byte, error) {
|
||
|
if len(j) == 0 {
|
||
|
return emptyJSON, nil
|
||
|
}
|
||
|
return j, nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON sets *j to a copy of data
|
||
|
func (j *JSONText) UnmarshalJSON(data []byte) error {
|
||
|
if j == nil {
|
||
|
return errors.New("JSONText: UnmarshalJSON on nil pointer")
|
||
|
}
|
||
|
*j = append((*j)[0:0], data...)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Value returns j as a value. This does a validating unmarshal into another
|
||
|
// RawMessage. If j is invalid json, it returns an error.
|
||
|
func (j JSONText) Value() (driver.Value, error) {
|
||
|
var m json.RawMessage
|
||
|
var err = j.Unmarshal(&m)
|
||
|
if err != nil {
|
||
|
return []byte{}, err
|
||
|
}
|
||
|
return []byte(j), nil
|
||
|
}
|
||
|
|
||
|
// Scan stores the src in *j. No validation is done.
|
||
|
func (j *JSONText) Scan(src interface{}) error {
|
||
|
var source []byte
|
||
|
switch t := src.(type) {
|
||
|
case string:
|
||
|
source = []byte(t)
|
||
|
case []byte:
|
||
|
if len(t) == 0 {
|
||
|
source = emptyJSON
|
||
|
} else {
|
||
|
source = t
|
||
|
}
|
||
|
case nil:
|
||
|
*j = emptyJSON
|
||
|
default:
|
||
|
return errors.New("Incompatible type for JSONText")
|
||
|
}
|
||
|
*j = append((*j)[0:0], source...)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Unmarshal unmarshal's the json in j to v, as in json.Unmarshal.
|
||
|
func (j *JSONText) Unmarshal(v interface{}) error {
|
||
|
if len(*j) == 0 {
|
||
|
*j = emptyJSON
|
||
|
}
|
||
|
return json.Unmarshal([]byte(*j), v)
|
||
|
}
|
||
|
|
||
|
// String supports pretty printing for JSONText types.
|
||
|
func (j JSONText) String() string {
|
||
|
return string(j)
|
||
|
}
|
||
|
|
||
|
// NullJSONText represents a JSONText that may be null.
|
||
|
// NullJSONText implements the scanner interface so
|
||
|
// it can be used as a scan destination, similar to NullString.
|
||
|
type NullJSONText struct {
|
||
|
JSONText
|
||
|
Valid bool // Valid is true if JSONText is not NULL
|
||
|
}
|
||
|
|
||
|
// Scan implements the Scanner interface.
|
||
|
func (n *NullJSONText) Scan(value interface{}) error {
|
||
|
if value == nil {
|
||
|
n.JSONText, n.Valid = emptyJSON, false
|
||
|
return nil
|
||
|
}
|
||
|
n.Valid = true
|
||
|
return n.JSONText.Scan(value)
|
||
|
}
|
||
|
|
||
|
// Value implements the driver Valuer interface.
|
||
|
func (n NullJSONText) Value() (driver.Value, error) {
|
||
|
if !n.Valid {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return n.JSONText.Value()
|
||
|
}
|
||
|
|
||
|
// BitBool is an implementation of a bool for the MySQL type BIT(1).
|
||
|
// This type allows you to avoid wasting an entire byte for MySQL's boolean type TINYINT.
|
||
|
type BitBool bool
|
||
|
|
||
|
// Value implements the driver.Valuer interface,
|
||
|
// and turns the BitBool into a bitfield (BIT(1)) for MySQL storage.
|
||
|
func (b BitBool) Value() (driver.Value, error) {
|
||
|
if b {
|
||
|
return []byte{1}, nil
|
||
|
}
|
||
|
return []byte{0}, nil
|
||
|
}
|
||
|
|
||
|
// Scan implements the sql.Scanner interface,
|
||
|
// and turns the bitfield incoming from MySQL into a BitBool
|
||
|
func (b *BitBool) Scan(src interface{}) error {
|
||
|
v, ok := src.([]byte)
|
||
|
if !ok {
|
||
|
return errors.New("bad []byte type assertion")
|
||
|
}
|
||
|
*b = v[0] == 1
|
||
|
return nil
|
||
|
}
|