1// Package zero contains SQL types that consider zero input and null input to be equivalent
2// with convenient support for JSON and text marshaling.
3// Types in this package will JSON marshal to their zero value, even if null.
4// Use the null parent package if you don't want this.
5package zero
6
7import (
8	"database/sql"
9	"encoding/json"
10	"fmt"
11	"reflect"
12)
13
14// String is a nullable string.
15// JSON marshals to a blank string if null.
16// Considered null to SQL if zero.
17type String struct {
18	sql.NullString
19}
20
21// NewString creates a new String
22func NewString(s string, valid bool) String {
23	return String{
24		NullString: sql.NullString{
25			String: s,
26			Valid:  valid,
27		},
28	}
29}
30
31// StringFrom creates a new String that will be null if s is blank.
32func StringFrom(s string) String {
33	return NewString(s, s != "")
34}
35
36// StringFromPtr creates a new String that be null if s is nil or blank.
37// It will make s point to the String's value.
38func StringFromPtr(s *string) String {
39	if s == nil {
40		return NewString("", false)
41	}
42	return NewString(*s, *s != "")
43}
44
45// ValueOrZero returns the inner value if valid, otherwise zero.
46func (s String) ValueOrZero() string {
47	if !s.Valid {
48		return ""
49	}
50	return s.String
51}
52
53// UnmarshalJSON implements json.Unmarshaler.
54// It supports string and null input. Blank string input produces a null String.
55// It also supports unmarshalling a sql.NullString.
56func (s *String) UnmarshalJSON(data []byte) error {
57	var err error
58	var v interface{}
59	if err = json.Unmarshal(data, &v); err != nil {
60		return err
61	}
62	switch x := v.(type) {
63	case string:
64		s.String = x
65	case map[string]interface{}:
66		err = json.Unmarshal(data, &s.NullString)
67	case nil:
68		s.Valid = false
69		return nil
70	default:
71		err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.String", reflect.TypeOf(v).Name())
72	}
73	s.Valid = (err == nil) && (s.String != "")
74	return err
75}
76
77// MarshalText implements encoding.TextMarshaler.
78// It will encode a blank string when this String is null.
79func (s String) MarshalText() ([]byte, error) {
80	if !s.Valid {
81		return []byte{}, nil
82	}
83	return []byte(s.String), nil
84}
85
86// UnmarshalText implements encoding.TextUnmarshaler.
87// It will unmarshal to a null String if the input is a blank string.
88func (s *String) UnmarshalText(text []byte) error {
89	s.String = string(text)
90	s.Valid = s.String != ""
91	return nil
92}
93
94// SetValid changes this String's value and also sets it to be non-null.
95func (s *String) SetValid(v string) {
96	s.String = v
97	s.Valid = true
98}
99
100// Ptr returns a pointer to this String's value, or a nil pointer if this String is null.
101func (s String) Ptr() *string {
102	if !s.Valid {
103		return nil
104	}
105	return &s.String
106}
107
108// IsZero returns true for null or empty strings, for potential future omitempty support.
109func (s String) IsZero() bool {
110	return !s.Valid || s.String == ""
111}
112
113// Equal returns true if both strings have the same value or are both either null or empty.
114func (s String) Equal(other String) bool {
115	return s.ValueOrZero() == other.ValueOrZero()
116}
117