diff options
Diffstat (limited to '')
-rw-r--r-- | pkg/types/acknowledgement_state.go | 61 | ||||
-rw-r--r-- | pkg/types/binary.go | 137 | ||||
-rw-r--r-- | pkg/types/bool.go | 105 | ||||
-rw-r--r-- | pkg/types/comment_type.go | 79 | ||||
-rw-r--r-- | pkg/types/float.go | 68 | ||||
-rw-r--r-- | pkg/types/int.go | 68 | ||||
-rw-r--r-- | pkg/types/notification_states.go | 78 | ||||
-rw-r--r-- | pkg/types/notification_type.go | 68 | ||||
-rw-r--r-- | pkg/types/notification_types.go | 81 | ||||
-rw-r--r-- | pkg/types/state_type.go | 65 | ||||
-rw-r--r-- | pkg/types/string.go | 74 | ||||
-rw-r--r-- | pkg/types/unix_milli.go | 95 | ||||
-rw-r--r-- | pkg/types/uuid.go | 24 |
13 files changed, 1003 insertions, 0 deletions
diff --git a/pkg/types/acknowledgement_state.go b/pkg/types/acknowledgement_state.go new file mode 100644 index 0000000..5bff613 --- /dev/null +++ b/pkg/types/acknowledgement_state.go @@ -0,0 +1,61 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" +) + +// AcknowledgementState specifies an acknowledgement state (yes, no, sticky). +type AcknowledgementState uint8 + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (as *AcknowledgementState) UnmarshalText(text []byte) error { + return as.UnmarshalJSON(text) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (as *AcknowledgementState) UnmarshalJSON(data []byte) error { + var i uint8 + if err := internal.UnmarshalJSON(data, &i); err != nil { + return err + } + + a := AcknowledgementState(i) + if _, ok := acknowledgementStates[a]; !ok { + return badAcknowledgementState(data) + } + + *as = a + return nil +} + +// Value implements the driver.Valuer interface. +func (as AcknowledgementState) Value() (driver.Value, error) { + if v, ok := acknowledgementStates[as]; ok { + return v, nil + } else { + return nil, badAcknowledgementState(as) + } +} + +// badAcknowledgementState returns an error about a syntactically, but not semantically valid AcknowledgementState. +func badAcknowledgementState(s interface{}) error { + return errors.Errorf("bad acknowledgement state: %#v", s) +} + +// acknowledgementStates maps all valid AcknowledgementState values to their SQL representation. +var acknowledgementStates = map[AcknowledgementState]string{ + 0: "n", + 1: "y", + 2: "sticky", +} + +// Assert interface compliance. +var ( + _ encoding.TextUnmarshaler = (*AcknowledgementState)(nil) + _ json.Unmarshaler = (*AcknowledgementState)(nil) + _ driver.Valuer = AcknowledgementState(0) +) diff --git a/pkg/types/binary.go b/pkg/types/binary.go new file mode 100644 index 0000000..00a5417 --- /dev/null +++ b/pkg/types/binary.go @@ -0,0 +1,137 @@ +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) +) diff --git a/pkg/types/bool.go b/pkg/types/bool.go new file mode 100644 index 0000000..4330264 --- /dev/null +++ b/pkg/types/bool.go @@ -0,0 +1,105 @@ +package types + +import ( + "database/sql" + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" + "strconv" +) + +var ( + enum = map[bool]string{ + true: "y", + false: "n", + } +) + +// Bool represents a bool for ENUM ('y', 'n'), which can be NULL. +type Bool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL +} + +// MarshalJSON implements the json.Marshaler interface. +func (b Bool) MarshalJSON() ([]byte, error) { + if !b.Valid { + return nil, nil + } + + return internal.MarshalJSON(b.Bool) +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (b *Bool) UnmarshalText(text []byte) error { + parsed, err := strconv.ParseUint(string(text), 10, 64) + if err != nil { + return internal.CantParseUint64(err, string(text)) + } + + *b = Bool{parsed != 0, true} + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (b *Bool) UnmarshalJSON(data []byte) error { + if string(data) == "null" || len(data) == 0 { + return nil + } + + if err := internal.UnmarshalJSON(data, &b.Bool); err != nil { + return err + } + + b.Valid = true + + return nil +} + +// Scan implements the sql.Scanner interface. +// Supports SQL NULL. +func (b *Bool) Scan(src interface{}) error { + if src == nil { + b.Bool, b.Valid = false, false + return nil + } + + v, ok := src.([]byte) + if !ok { + return errors.Errorf("bad []byte type assertion from %#v", src) + } + + switch string(v) { + case "y": + b.Bool = true + case "n": + b.Bool = false + default: + return errors.Errorf("bad bool %#v", v) + } + + b.Valid = true + + return nil +} + +// Value implements the driver.Valuer interface. +// Supports SQL NULL. +func (b Bool) Value() (driver.Value, error) { + if !b.Valid { + return nil, nil + } + + return enum[b.Bool], nil +} + +// Assert interface compliance. +var ( + _ json.Marshaler = (*Bool)(nil) + _ encoding.TextUnmarshaler = (*Bool)(nil) + _ json.Unmarshaler = (*Bool)(nil) + _ sql.Scanner = (*Bool)(nil) + _ driver.Valuer = (*Bool)(nil) +) diff --git a/pkg/types/comment_type.go b/pkg/types/comment_type.go new file mode 100644 index 0000000..8aed475 --- /dev/null +++ b/pkg/types/comment_type.go @@ -0,0 +1,79 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" + "strconv" +) + +// CommentType specifies a comment's origin's kind. +type CommentType uint8 + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (ct *CommentType) UnmarshalJSON(data []byte) error { + var i uint8 + if err := internal.UnmarshalJSON(data, &i); err != nil { + return err + } + + c := CommentType(i) + if _, ok := commentTypes[c]; !ok { + return badCommentType(data) + } + + *ct = c + return nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (ct *CommentType) UnmarshalText(text []byte) error { + s := string(text) + + i, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return internal.CantParseUint64(err, s) + } + + c := CommentType(i) + if uint64(c) != i { + // Truncated due to above cast, obviously too high + return badCommentType(s) + } + + if _, ok := commentTypes[c]; !ok { + return badCommentType(s) + } + + *ct = c + return nil +} + +// Value implements the driver.Valuer interface. +func (ct CommentType) Value() (driver.Value, error) { + if v, ok := commentTypes[ct]; ok { + return v, nil + } else { + return nil, badCommentType(ct) + } +} + +// badCommentType returns an error about a syntactically, but not semantically valid CommentType. +func badCommentType(t interface{}) error { + return errors.Errorf("bad comment type: %#v", t) +} + +// commentTypes maps all valid CommentType values to their SQL representation. +var commentTypes = map[CommentType]string{ + 1: "comment", + 4: "ack", +} + +// Assert interface compliance. +var ( + _ json.Unmarshaler = (*CommentType)(nil) + _ encoding.TextUnmarshaler = (*CommentType)(nil) + _ driver.Valuer = CommentType(0) +) diff --git a/pkg/types/float.go b/pkg/types/float.go new file mode 100644 index 0000000..a4aedd6 --- /dev/null +++ b/pkg/types/float.go @@ -0,0 +1,68 @@ +package types + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "strconv" +) + +// Float adds JSON support to sql.NullFloat64. +type Float struct { + sql.NullFloat64 +} + +// MarshalJSON implements the json.Marshaler interface. +// Supports JSON null. +func (f Float) MarshalJSON() ([]byte, error) { + var v interface{} + if f.Valid { + v = f.Float64 + } + + return internal.MarshalJSON(v) +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (f *Float) UnmarshalText(text []byte) error { + parsed, err := strconv.ParseFloat(string(text), 64) + if err != nil { + return internal.CantParseFloat64(err, string(text)) + } + + *f = Float{sql.NullFloat64{ + Float64: parsed, + Valid: true, + }} + + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Supports JSON null. +func (f *Float) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if bytes.HasPrefix(data, []byte{'n'}) { + return nil + } + + if err := internal.UnmarshalJSON(data, &f.Float64); err != nil { + return err + } + + f.Valid = true + + return nil +} + +// Assert interface compliance. +var ( + _ json.Marshaler = Float{} + _ encoding.TextUnmarshaler = (*Float)(nil) + _ json.Unmarshaler = (*Float)(nil) + _ driver.Valuer = Float{} + _ sql.Scanner = (*Float)(nil) +) diff --git a/pkg/types/int.go b/pkg/types/int.go new file mode 100644 index 0000000..0e51f21 --- /dev/null +++ b/pkg/types/int.go @@ -0,0 +1,68 @@ +package types + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "strconv" +) + +// Int adds JSON support to sql.NullInt64. +type Int struct { + sql.NullInt64 +} + +// MarshalJSON implements the json.Marshaler interface. +// Supports JSON null. +func (i Int) MarshalJSON() ([]byte, error) { + var v interface{} + if i.Valid { + v = i.Int64 + } + + return internal.MarshalJSON(v) +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (i *Int) UnmarshalText(text []byte) error { + parsed, err := strconv.ParseInt(string(text), 10, 64) + if err != nil { + return internal.CantParseInt64(err, string(text)) + } + + *i = Int{sql.NullInt64{ + Int64: parsed, + Valid: true, + }} + + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Supports JSON null. +func (i *Int) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if bytes.HasPrefix(data, []byte{'n'}) { + return nil + } + + if err := internal.UnmarshalJSON(data, &i.Int64); err != nil { + return err + } + + i.Valid = true + + return nil +} + +// Assert interface compliance. +var ( + _ json.Marshaler = Int{} + _ json.Unmarshaler = (*Int)(nil) + _ encoding.TextUnmarshaler = (*Int)(nil) + _ driver.Valuer = Int{} + _ sql.Scanner = (*Int)(nil) +) diff --git a/pkg/types/notification_states.go b/pkg/types/notification_states.go new file mode 100644 index 0000000..ff5760a --- /dev/null +++ b/pkg/types/notification_states.go @@ -0,0 +1,78 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" +) + +// NotificationStates specifies the set of states a notification may be sent for. +type NotificationStates uint8 + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (nst *NotificationStates) UnmarshalJSON(data []byte) error { + var states []string + if err := internal.UnmarshalJSON(data, &states); err != nil { + return err + } + + var n NotificationStates + for _, state := range states { + if v, ok := notificationStateNames[state]; ok { + n |= v + } else { + return badNotificationStates(states) + } + } + + *nst = n + return nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (nst *NotificationStates) UnmarshalText(text []byte) error { + return nst.UnmarshalJSON(text) +} + +// Value implements the driver.Valuer interface. +func (nst NotificationStates) Value() (driver.Value, error) { + if nst&^allNotificationStates == 0 { + return int64(nst), nil + } else { + return nil, badNotificationStates(nst) + } +} + +// badNotificationStates returns an error about syntactically, but not semantically valid NotificationStates. +func badNotificationStates(s interface{}) error { + return errors.Errorf("bad notification states: %#v", s) +} + +// notificationStateNames maps all valid NotificationStates values to their SQL representation. +var notificationStateNames = map[string]NotificationStates{ + "OK": 1, + "Warning": 2, + "Critical": 4, + "Unknown": 8, + "Up": 16, + "Down": 32, +} + +// allNotificationStates is the largest valid NotificationStates value. +var allNotificationStates = func() NotificationStates { + var nt NotificationStates + for _, v := range notificationStateNames { + nt |= v + } + + return nt +}() + +// Assert interface compliance. +var ( + _ json.Unmarshaler = (*NotificationStates)(nil) + _ encoding.TextUnmarshaler = (*NotificationStates)(nil) + _ driver.Valuer = NotificationStates(0) +) diff --git a/pkg/types/notification_type.go b/pkg/types/notification_type.go new file mode 100644 index 0000000..f2980f4 --- /dev/null +++ b/pkg/types/notification_type.go @@ -0,0 +1,68 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" + "strconv" +) + +// NotificationType specifies the reason of a sent notification. +type NotificationType uint16 + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (nt *NotificationType) UnmarshalText(text []byte) error { + s := string(text) + + i, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return internal.CantParseUint64(err, s) + } + + n := NotificationType(i) + if uint64(n) != i { + // Truncated due to above cast, obviously too high + return badNotificationType(s) + } + + if _, ok := notificationTypes[n]; !ok { + return badNotificationType(s) + } + + *nt = n + return nil +} + +// Value implements the driver.Valuer interface. +func (nt NotificationType) Value() (driver.Value, error) { + if v, ok := notificationTypes[nt]; ok { + return v, nil + } else { + return nil, badNotificationType(nt) + } +} + +// badNotificationType returns an error about a syntactically, but not semantically valid NotificationType. +func badNotificationType(t interface{}) error { + return errors.Errorf("bad notification type: %#v", t) +} + +// notificationTypes maps all valid NotificationType values to their SQL representation. +var notificationTypes = map[NotificationType]string{ + 1: "downtime_start", + 2: "downtime_end", + 4: "downtime_removed", + 8: "custom", + 16: "acknowledgement", + 32: "problem", + 64: "recovery", + 128: "flapping_start", + 256: "flapping_end", +} + +// Assert interface compliance. +var ( + _ encoding.TextUnmarshaler = (*NotificationType)(nil) + _ driver.Valuer = NotificationType(0) +) diff --git a/pkg/types/notification_types.go b/pkg/types/notification_types.go new file mode 100644 index 0000000..832a515 --- /dev/null +++ b/pkg/types/notification_types.go @@ -0,0 +1,81 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" +) + +// NotificationTypes specifies the set of reasons a notification may be sent for. +type NotificationTypes uint16 + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (nt *NotificationTypes) UnmarshalJSON(data []byte) error { + var types []string + if err := internal.UnmarshalJSON(data, &types); err != nil { + return err + } + + var n NotificationTypes + for _, typ := range types { + if v, ok := notificationTypeNames[typ]; ok { + n |= v + } else { + return badNotificationTypes(nt) + } + } + + *nt = n + return nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (nt *NotificationTypes) UnmarshalText(text []byte) error { + return nt.UnmarshalJSON(text) +} + +// Value implements the driver.Valuer interface. +func (nt NotificationTypes) Value() (driver.Value, error) { + if nt&^allNotificationTypes == 0 { + return int64(nt), nil + } else { + return nil, badNotificationTypes(nt) + } +} + +// badNotificationTypes returns an error about syntactically, but not semantically valid NotificationTypes. +func badNotificationTypes(t interface{}) error { + return errors.Errorf("bad notification types: %#v", t) +} + +// notificationTypeNames maps all valid NotificationTypes values to their SQL representation. +var notificationTypeNames = map[string]NotificationTypes{ + "DowntimeStart": 1, + "DowntimeEnd": 2, + "DowntimeRemoved": 4, + "Custom": 8, + "Acknowledgement": 16, + "Problem": 32, + "Recovery": 64, + "FlappingStart": 128, + "FlappingEnd": 256, +} + +// allNotificationTypes is the largest valid NotificationTypes value. +var allNotificationTypes = func() NotificationTypes { + var nt NotificationTypes + for _, v := range notificationTypeNames { + nt |= v + } + + return nt +}() + +// Assert interface compliance. +var ( + _ json.Unmarshaler = (*NotificationTypes)(nil) + _ encoding.TextUnmarshaler = (*NotificationTypes)(nil) + _ driver.Valuer = NotificationTypes(0) +) diff --git a/pkg/types/state_type.go b/pkg/types/state_type.go new file mode 100644 index 0000000..f0cc69a --- /dev/null +++ b/pkg/types/state_type.go @@ -0,0 +1,65 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/pkg/errors" +) + +// StateType specifies a state's hardness. +type StateType uint8 + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (st *StateType) UnmarshalText(text []byte) error { + return st.UnmarshalJSON(text) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (st *StateType) UnmarshalJSON(data []byte) error { + var i uint8 + if err := internal.UnmarshalJSON(data, &i); err != nil { + return err + } + + s := StateType(i) + if _, ok := stateTypes[s]; !ok { + return badStateType(data) + } + + *st = s + return nil +} + +// Value implements the driver.Valuer interface. +func (st StateType) Value() (driver.Value, error) { + if v, ok := stateTypes[st]; ok { + return v, nil + } else { + return nil, badStateType(st) + } +} + +// badStateType returns and error about a syntactically, but not semantically valid StateType. +func badStateType(t interface{}) error { + return errors.Errorf("bad state type: %#v", t) +} + +const ( + StateSoft = StateType(0) + StateHard = StateType(1) +) + +// stateTypes maps all valid StateType values to their SQL representation. +var stateTypes = map[StateType]string{ + StateSoft: "soft", + StateHard: "hard", +} + +// Assert interface compliance. +var ( + _ encoding.TextUnmarshaler = (*StateType)(nil) + _ json.Unmarshaler = (*StateType)(nil) + _ driver.Valuer = StateType(0) +) diff --git a/pkg/types/string.go b/pkg/types/string.go new file mode 100644 index 0000000..f8ead45 --- /dev/null +++ b/pkg/types/string.go @@ -0,0 +1,74 @@ +package types + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "strings" +) + +// String adds JSON support to sql.NullString. +type String struct { + sql.NullString +} + +// MarshalJSON implements the json.Marshaler interface. +// Supports JSON null. +func (s String) MarshalJSON() ([]byte, error) { + var v interface{} + if s.Valid { + v = s.String + } + + return internal.MarshalJSON(v) +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (s *String) UnmarshalText(text []byte) error { + *s = String{sql.NullString{ + String: string(text), + Valid: true, + }} + + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Supports JSON null. +func (s *String) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if bytes.HasPrefix(data, []byte{'n'}) { + return nil + } + + if err := internal.UnmarshalJSON(data, &s.String); err != nil { + return err + } + + s.Valid = true + + return nil +} + +// Value implements the driver.Valuer interface. +// Supports SQL NULL. +func (s String) Value() (driver.Value, error) { + if !s.Valid { + return nil, nil + } + + // PostgreSQL does not allow null bytes in varchar, char and text fields. + return strings.ReplaceAll(s.String, "\x00", ""), nil +} + +// Assert interface compliance. +var ( + _ json.Marshaler = String{} + _ encoding.TextUnmarshaler = (*String)(nil) + _ json.Unmarshaler = (*String)(nil) + _ driver.Valuer = String{} + _ sql.Scanner = (*String)(nil) +) diff --git a/pkg/types/unix_milli.go b/pkg/types/unix_milli.go new file mode 100644 index 0000000..203cdf6 --- /dev/null +++ b/pkg/types/unix_milli.go @@ -0,0 +1,95 @@ +package types + +import ( + "database/sql" + "database/sql/driver" + "encoding" + "encoding/json" + "github.com/icinga/icingadb/internal" + "github.com/icinga/icingadb/pkg/utils" + "github.com/pkg/errors" + "strconv" + "time" +) + +// UnixMilli is a nullable millisecond UNIX timestamp in databases and JSON. +type UnixMilli time.Time + +// Time returns the time.Time conversion of UnixMilli. +func (t UnixMilli) Time() time.Time { + return time.Time(t) +} + +// MarshalJSON implements the json.Marshaler interface. +// Marshals to milliseconds. Supports JSON null. +func (t UnixMilli) MarshalJSON() ([]byte, error) { + if time.Time(t).IsZero() { + return nil, nil + } + + return []byte(strconv.FormatInt(time.Time(t).UnixMilli(), 10)), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (t *UnixMilli) UnmarshalText(text []byte) error { + parsed, err := strconv.ParseFloat(string(text), 64) + if err != nil { + return internal.CantParseFloat64(err, string(text)) + } + + *t = UnixMilli(utils.FromUnixMilli(int64(parsed))) + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// Unmarshals from milliseconds. Supports JSON null. +func (t *UnixMilli) UnmarshalJSON(data []byte) error { + if string(data) == "null" || len(data) == 0 { + return nil + } + + ms, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return internal.CantParseFloat64(err, string(data)) + } + tt := utils.FromUnixMilli(int64(ms)) + *t = UnixMilli(tt) + + return nil +} + +// Scan implements the sql.Scanner interface. +// Scans from milliseconds. Supports SQL NULL. +func (t *UnixMilli) Scan(src interface{}) error { + if src == nil { + return nil + } + + v, ok := src.(int64) + if !ok { + return errors.Errorf("bad int64 type assertion from %#v", src) + } + tt := utils.FromUnixMilli(v) + *t = UnixMilli(tt) + + return nil +} + +// Value implements the driver.Valuer interface. +// Returns milliseconds. Supports SQL NULL. +func (t UnixMilli) Value() (driver.Value, error) { + if t.Time().IsZero() { + return nil, nil + } + + return t.Time().UnixMilli(), nil +} + +// Assert interface compliance. +var ( + _ json.Marshaler = (*UnixMilli)(nil) + _ encoding.TextUnmarshaler = (*UnixMilli)(nil) + _ json.Unmarshaler = (*UnixMilli)(nil) + _ sql.Scanner = (*UnixMilli)(nil) + _ driver.Valuer = (*UnixMilli)(nil) +) diff --git a/pkg/types/uuid.go b/pkg/types/uuid.go new file mode 100644 index 0000000..02acbcd --- /dev/null +++ b/pkg/types/uuid.go @@ -0,0 +1,24 @@ +package types + +import ( + "database/sql/driver" + "encoding" + "github.com/google/uuid" +) + +// UUID is like uuid.UUID, but marshals itself binarily (not like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) in SQL context. +type UUID struct { + uuid.UUID +} + +// Value implements driver.Valuer. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.UUID[:], nil +} + +// Assert interface compliance. +var ( + _ encoding.TextUnmarshaler = (*UUID)(nil) + _ driver.Valuer = UUID{} + _ driver.Valuer = (*UUID)(nil) +) |