nullish

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2025 License: MIT Imports: 8 Imported by: 0

README

# Nullish

Go Reference MIT License Go Version

A high-performance, minimal-dependency Go package for handling nullable types with PostgreSQL and JSON serialization support.

Features

  • High Performance - Sub-nanosecond operations for primitive types, zero allocations
  • Minimal Dependencies - Only 3 required packages (go-json, uuid, ulid)
  • Fully Tested - 77.5% code coverage, 87 test cases, 34 benchmarks
  • SQL Driver Compatible - Implements database/sql/driver.Valuer and sql.Scanner
  • JSON Support - Custom JSON marshaling/unmarshaling for all types
  • Production Ready - Battle-tested in production environments

Supported Types

Type Description Use Case
NullString Nullable string VARCHAR, TEXT columns
NullInt Nullable integer INT, BIGINT columns
NullFloat Nullable float64 FLOAT, DOUBLE columns
NullBool Nullable boolean BOOLEAN columns
NullTime Nullable time.Time TIMESTAMP columns
NullUUID Nullable UUID UUID columns
NullULID Nullable ULID Sortable unique identifiers
NullJSON Raw JSON JSONB columns
NullObj JSON object map[string]interface{}
NullArr JSON array []interface{}
NullArrObj Array of objects []map[string]interface{}

Installation

go get -u github.com/sutantodadang/nullish

Quick Start

Basic Usage

import "github.com/sutantodadang/nullish"

type User struct {
    ID        int                    `json:"id"`
    Name      nullish.NullString     `json:"name"`
    Age       nullish.NullInt        `json:"age"`
    Email     nullish.NullString     `json:"email"`
    Active    nullish.NullBool       `json:"active"`
    CreatedAt nullish.NullTime       `json:"created_at"`
    Metadata  nullish.NullObj        `json:"metadata"`
}

// Create with constructor
user := User{
    Name:  nullish.NewNullString("John Doe", true),
    Age:   nullish.NewNullInt(30, true),
    Email: nullish.NewNullString("", false), // null email
}

Database Operations

import (
    "database/sql"
    "github.com/sutantodadang/nullish"
)

// Scanning from database
var user User
err := db.QueryRow("SELECT name, age, email FROM users WHERE id = $1", 1).
    Scan(&user.Name, &user.Age, &user.Email)

// Inserting to database
_, err = db.Exec(
    "INSERT INTO users (name, age, email) VALUES ($1, $2, $3)",
    user.Name, user.Age, user.Email,
)

JSON Serialization

import "encoding/json"

// Marshal to JSON
user := User{
    Name:  nullish.NewNullString("Alice", true),
    Age:   nullish.NewNullInt(25, true),
    Email: nullish.NewNullString("", false),
}

jsonData, _ := json.Marshal(user)
// Output: {"id":0,"name":"Alice","age":25,"email":null,...}

// Unmarshal from JSON
var decoded User
json.Unmarshal(jsonData, &decoded)

Complex Types

// NullObj - for JSON objects
metadata := nullish.NewNullObj(map[string]interface{}{
    "role": "admin",
    "permissions": []string{"read", "write"},
}, true)

// NullArr - for arrays
tags := nullish.NewNullArr([]interface{}{"golang", "backend", "api"}, true)

// NullArrObj - for array of objects
addresses := nullish.NewNullArrObj([]map[string]interface{}{
    {"street": "123 Main St", "city": "NYC"},
    {"street": "456 Oak Ave", "city": "LA"},
}, true)

UUID & ULID

import (
    "github.com/google/uuid"
    "github.com/oklog/ulid/v2"
)

// UUID
id := uuid.New()
userID := nullish.NewNullUUID(id, true)

// ULID (sortable, timestamp-based)
entropy := ulid.DefaultEntropy()
ulidValue := ulid.MustNew(ulid.Timestamp(time.Now()), entropy)
trackingID := nullish.NewNullULID(ulidValue, true)

Performance

Benchmark results on AMD Ryzen 5 7500F:

Operation Time/op Allocations
NullString Value 0.21 ns 0
NullInt Scan 1.87 ns 0
NullBool Value 0.21 ns 0
NullFloat Scan 1.75 ns 0
NullTime MarshalJSON 124 ns 112 B
NullUUID Scan (bytes) 19 ns 0
NullULID Value 6.8 ns 0
NullObj UnmarshalJSON 203 ns 208 B
NullArr MarshalJSON 75 ns 32 B
NullArrObj Scan 0.42 ns 0

Architecture

All types implement:

  • database/sql/driver.Valuer - for database writes
  • database/sql.Scanner - for database reads
  • json.Marshaler - for JSON encoding
  • json.Unmarshaler - for JSON decoding

Each type has two fields:

  • The actual value (e.g., String, Int, Time)
  • Valid bool - indicates if the value is non-null

Testing

Run all tests:

go test -v

Run with coverage:

go test -cover

Run benchmarks:

go test -bench . -benchmem

API Reference

Constructors

All types provide constructor functions:

NewNullString(str string, valid bool) NullString
NewNullInt(integer int, valid bool) NullInt
NewNullFloat(float float64, valid bool) NullFloat
NewNullBool(boolean bool, valid bool) NullBool
NewNullTime(time time.Time, valid bool) NullTime
NewNullUUID(uuid uuid.UUID, valid bool) NullUUID
NewNullULID(ulid ulid.ULID, valid bool) NullULID
NewNullJSON(json json.RawMessage, valid bool) NullJSON
NewNullObj(object map[string]interface{}, valid bool) NullObj
NewNullArr(array []interface{}, valid bool) NullArr
NewNullArrObj(arrayObject []map[string]interface{}, valid bool) NullArrObj

Methods

All types implement:

  • Value() (driver.Value, error) - Convert to database value
  • Scan(value interface{}) error - Read from database
  • MarshalJSON() ([]byte, error) - Convert to JSON
  • UnmarshalJSON(data []byte) error - Parse from JSON

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Acknowledgments

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NullType = []byte("null")

Functions

This section is empty.

Types

type NullArr

type NullArr struct {
	Arr   []interface{}
	Valid bool
}

func NewNullArr

func NewNullArr(array []interface{}, valid bool) NullArr

func (NullArr) MarshalJSON

func (na NullArr) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullArr) Scan

func (na *NullArr) Scan(value interface{}) error

Scan method

func (*NullArr) UnmarshalJSON

func (na *NullArr) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullArr) Value

func (na NullArr) Value() (driver.Value, error)

Value method

type NullArrObj

type NullArrObj struct {
	ArrObj []map[string]interface{}
	Valid  bool
}

func NewNullArrObj

func NewNullArrObj(arrayObject []map[string]interface{}, valid bool) NullArrObj

func (NullArrObj) MarshalJSON

func (na NullArrObj) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullArrObj) Scan

func (na *NullArrObj) Scan(value interface{}) error

Scan method

func (*NullArrObj) UnmarshalJSON

func (na *NullArrObj) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullArrObj) Value

func (na NullArrObj) Value() (driver.Value, error)

Value method

type NullBool

type NullBool struct {
	Bool  bool
	Valid bool
}

func NewNullBool

func NewNullBool(boolean bool, valid bool) NullBool

func (NullBool) MarshalJSON

func (nb NullBool) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullBool) Scan

func (nb *NullBool) Scan(value interface{}) error

Scan method

func (*NullBool) UnmarshalJSON

func (nb *NullBool) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullBool) Value

func (nb NullBool) Value() (driver.Value, error)

Value method

type NullFloat

type NullFloat struct {
	Float float64
	Valid bool
}

func NewNullFloat

func NewNullFloat(float float64, valid bool) NullFloat

func (NullFloat) MarshalJSON

func (nf NullFloat) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullFloat) Scan

func (nf *NullFloat) Scan(value interface{}) error

Scan method

func (*NullFloat) UnmarshalJSON

func (nf *NullFloat) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullFloat) Value

func (nf NullFloat) Value() (driver.Value, error)

Value method

type NullInt

type NullInt struct {
	Int   int
	Valid bool
}

func NewNullInt

func NewNullInt(integer int, valid bool) NullInt

func (NullInt) MarshalJSON

func (ni NullInt) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullInt) Scan

func (ni *NullInt) Scan(value interface{}) error

Scan method

func (*NullInt) UnmarshalJSON

func (ni *NullInt) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullInt) Value

func (ni NullInt) Value() (driver.Value, error)

Value method

type NullJSON

type NullJSON struct {
	Json  json.RawMessage
	Valid bool
}

func NewNullJSON

func NewNullJSON(json json.RawMessage, valid bool) NullJSON

func (NullJSON) MarshalJSON

func (nj NullJSON) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullJSON) Scan

func (nj *NullJSON) Scan(value interface{}) error

Scan method

func (*NullJSON) UnmarshalJSON

func (nj *NullJSON) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullJSON) Value

func (nj NullJSON) Value() (driver.Value, error)

Value method

type NullObj

type NullObj struct {
	Obj   map[string]interface{}
	Valid bool
}

func NewNullObj

func NewNullObj(object map[string]interface{}, valid bool) NullObj

func (NullObj) MarshalJSON

func (no NullObj) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullObj) Scan

func (no *NullObj) Scan(value interface{}) error

Scan method

func (*NullObj) UnmarshalJSON

func (no *NullObj) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullObj) Value

func (no NullObj) Value() (driver.Value, error)

Value method

type NullString

type NullString struct {
	String string
	Valid  bool
}

func NewNullString

func NewNullString(str string, valid bool) NullString

func (NullString) MarshalJSON

func (ns NullString) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullString) Scan

func (ns *NullString) Scan(value interface{}) error

Scan method

func (*NullString) UnmarshalJSON

func (ns *NullString) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullString) Value

func (ns NullString) Value() (driver.Value, error)

Value method

type NullTime

type NullTime struct {
	Time  time.Time
	Valid bool
}

func NewNullTime

func NewNullTime(time time.Time, valid bool) NullTime

func (NullTime) MarshalJSON

func (nt NullTime) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullTime) Scan

func (nt *NullTime) Scan(value interface{}) error

Scan method

func (*NullTime) UnmarshalJSON

func (nt *NullTime) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullTime) Value

func (nt NullTime) Value() (driver.Value, error)

Value method

type NullULID

type NullULID struct {
	ULID  ulid.ULID
	Valid bool
}

func NewNullULID

func NewNullULID(ulid ulid.ULID, valid bool) NullULID

func (NullULID) MarshalJSON

func (nl NullULID) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullULID) Scan

func (nl *NullULID) Scan(value interface{}) error

Scan method

func (*NullULID) UnmarshalJSON

func (nl *NullULID) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullULID) Value

func (nl NullULID) Value() (driver.Value, error)

Value method

type NullUUID

type NullUUID struct {
	UUID  uuid.UUID
	Valid bool
}

func NewNullUUID

func NewNullUUID(uuid uuid.UUID, valid bool) NullUUID

func (NullUUID) MarshalJSON

func (nu NullUUID) MarshalJSON() ([]byte, error)

MarshalJSON method

func (*NullUUID) Scan

func (nu *NullUUID) Scan(value interface{}) error

Scan method

func (*NullUUID) UnmarshalJSON

func (nu *NullUUID) UnmarshalJSON(data []byte) error

UnmarshalJSON method

func (NullUUID) Value

func (nu NullUUID) Value() (driver.Value, error)

Value method

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL