tulip/database/db-scanner.go
2023-09-06 22:20:09 +01:00

112 lines
2.9 KiB
Go

package database
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/MrMelon54/pronouns"
"golang.org/x/text/language"
"time"
)
var (
_, _, _, _, _ sql.Scanner = &NullStringScanner{}, &NullDateScanner{}, &LocationScanner{}, &LocaleScanner{}, &PronounScanner{}
_, _, _, _, _ json.Marshaler = &NullStringScanner{}, &NullDateScanner{}, &LocationScanner{}, &LocaleScanner{}, &PronounScanner{}
_, _, _, _, _ json.Unmarshaler = &NullStringScanner{}, &NullDateScanner{}, &LocationScanner{}, &LocaleScanner{}, &PronounScanner{}
)
func marshalValueOrNull(null bool, data any) ([]byte, error) {
if null {
return json.Marshal(nil)
}
return json.Marshal(data)
}
type NullStringScanner struct{ sql.NullString }
func (s *NullStringScanner) Null() bool { return !s.Valid }
func (s *NullStringScanner) Scan(src any) error { return s.Scan(src) }
func (s NullStringScanner) MarshalJSON() ([]byte, error) {
return marshalValueOrNull(s.Null(), s.String)
}
func (s *NullStringScanner) UnmarshalJSON(bytes []byte) error {
if string(bytes) == "null" {
return s.Scan(nil)
}
var a string
err := json.Unmarshal(bytes, &a)
if err != nil {
return err
}
return s.Scan(&a)
}
type NullDateScanner struct{ sql.NullTime }
func (t *NullDateScanner) Null() bool { return !t.Valid }
func (t *NullDateScanner) Scan(src any) error { return t.NullTime.Scan(src) }
func (t NullDateScanner) MarshalJSON() ([]byte, error) {
return marshalValueOrNull(t.Null(), t.Time.UTC().Format(time.DateOnly))
}
func (t *NullDateScanner) UnmarshalJSON(bytes []byte) error {
if string(bytes) == "null" {
return t.Scan(nil)
}
var a string
err := json.Unmarshal(bytes, &a)
if err != nil {
return err
}
return t.Scan(&a)
}
type LocationScanner struct{ *time.Location }
func (l *LocationScanner) Scan(src any) error {
s, ok := src.(string)
if !ok {
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, l)
}
loc, err := time.LoadLocation(s)
if err != nil {
return err
}
l.Location = loc
return nil
}
func (l LocationScanner) MarshalJSON() ([]byte, error) { return json.Marshal(l.Location.String()) }
type LocaleScanner struct{ language.Tag }
func (l *LocaleScanner) Scan(src any) error {
s, ok := src.(string)
if !ok {
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, l)
}
lang, err := language.Parse(s)
if err != nil {
return err
}
l.Tag = lang
return nil
}
func (l LocaleScanner) MarshalJSON() ([]byte, error) {
return json.Marshal(l.Tag.String())
}
type PronounScanner struct{ pronouns.Pronoun }
func (p *PronounScanner) Scan(src any) error {
s, ok := src.(string)
if !ok {
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, p)
}
pro, err := pronouns.FindPronoun(s)
if err != nil {
return err
}
p.Pronoun = pro
return nil
}
func (p PronounScanner) MarshalJSON() ([]byte, error) { return json.Marshal(p.Pronoun.String()) }