package types import ( "bytes" "database/sql" "database/sql/driver" "encoding" "encoding/hex" "encoding/json" "github.com/icinga/icingadb/internal" "github.com/icinga/icingadb/pkg/contracts" "github.com/pkg/errors" ) // Binary nullable byte string. Hex as JSON. type Binary []byte // nullBinary for validating whether a Binary is valid. var nullBinary Binary // Equal returns whether the binaries are the same length and // contain the same bytes. func (binary Binary) Equal(equaler contracts.Equaler) bool { b, ok := equaler.(Binary) if !ok { panic("bad Binary type assertion") } return bytes.Equal(binary, b) } // Valid returns whether the Binary is valid. func (binary Binary) Valid() bool { return !bytes.Equal(binary, nullBinary) } // String returns the hex string representation form of the Binary. func (binary Binary) String() string { return hex.EncodeToString(binary) } // MarshalText implements a custom marhsal function to encode // the Binary as hex. MarshalText implements the // encoding.TextMarshaler interface. func (binary Binary) MarshalText() ([]byte, error) { return []byte(binary.String()), nil } // UnmarshalText implements a custom unmarshal function to decode // hex into a Binary. UnmarshalText implements the // encoding.TextUnmarshaler interface. func (binary *Binary) UnmarshalText(text []byte) error { b := make([]byte, hex.DecodedLen(len(text))) _, err := hex.Decode(b, text) if err != nil { return internal.CantDecodeHex(err, string(text)) } *binary = b return nil } // MarshalJSON implements a custom marshal function to encode the Binary // as a hex string. MarshalJSON implements the json.Marshaler interface. // Supports JSON null. func (binary Binary) MarshalJSON() ([]byte, error) { if !binary.Valid() { return nil, nil } return internal.MarshalJSON(binary.String()) } // UnmarshalJSON implements a custom unmarshal function to decode // a JSON hex string into a Binary. UnmarshalJSON implements the // json.Unmarshaler interface. Supports JSON null. func (binary *Binary) UnmarshalJSON(data []byte) error { if string(data) == "null" || len(data) == 0 { return nil } var s string if err := internal.UnmarshalJSON(data, &s); err != nil { return err } b, err := hex.DecodeString(s) if err != nil { return internal.CantDecodeHex(err, s) } *binary = b return nil } // Scan implements the sql.Scanner interface. // Supports SQL NULL. func (binary *Binary) Scan(src interface{}) error { switch src := src.(type) { case nil: return nil case []byte: if len(src) == 0 { return nil } b := make([]byte, len(src)) copy(b, src) *binary = b default: return errors.Errorf("unable to scan type %T into Binary", src) } return nil } // Value implements the driver.Valuer interface. // Supports SQL NULL. func (binary Binary) Value() (driver.Value, error) { if !binary.Valid() { return nil, nil } return []byte(binary), nil } // Assert interface compliance. var ( _ contracts.ID = (*Binary)(nil) _ encoding.TextMarshaler = (*Binary)(nil) _ encoding.TextUnmarshaler = (*Binary)(nil) _ json.Marshaler = (*Binary)(nil) _ json.Unmarshaler = (*Binary)(nil) _ sql.Scanner = (*Binary)(nil) _ driver.Valuer = (*Binary)(nil) )