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