Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nullable package #12

Merged
merged 3 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ Minimalist and zero-dependency errors library with stacktrace support for Go (fo

[See here](errors/README.md).

## nullable

A safe way to represent nullable primitive values in Go. Supports JSON serialization.

[See here](nullable/README.md).

## queue

Minimalist and zero-dependency low-level and simple queue library for thread-safe and unlimited-size generics in-memory message queue library for Go (async enqueue and blocking dequeue supports).\
Expand Down
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use (
./address
./errors
./fixedpoint
./nullable
./queue
./utils
)
12 changes: 12 additions & 0 deletions nullable/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/Cleverse/go-utilities/nullable.svg)](https://pkg.go.dev/github.com/Cleverse/go-utilities/nullable)
[![Report card](https://goreportcard.com/badge/github.com/Cleverse/go-utilities/nullable)](https://goreportcard.com/report/github.com/Cleverse/go-utilities/nullable)

# nullable

A safe way to represent nullable primitive values in Go. Supports JSON serialization.

## Installation

```shell
go get github.com/Cleverse/go-utilities/nullable
```
14 changes: 14 additions & 0 deletions nullable/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/Cleverse/go-utilities/nullable

go 1.21

require (
github.com/Cleverse/go-utilities/errors v0.0.0-20231113142714-2364608744a9
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 12 additions & 0 deletions nullable/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/Cleverse/go-utilities/errors v0.0.0-20231113142714-2364608744a9 h1:nHdki9biYL351wAyweRn7AOtEtOcIUN30DK7Xbx8dG0=
github.com/Cleverse/go-utilities/errors v0.0.0-20231113142714-2364608744a9/go.mod h1:1QK+h746G1DwellQ6KK2rBCJusZqIDTZ9QFVGnUX9+Q=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
137 changes: 137 additions & 0 deletions nullable/nullable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Package nullable provides a safe way to represent nullable primitive values in Go.
package nullable

import (
"bytes"
"encoding/json"

"github.com/Cleverse/go-utilities/errors"
)

type primitive interface {
bombnp marked this conversation as resolved.
Show resolved Hide resolved
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 | ~bool | ~string
}

var nullBytes = []byte("null")

// Nullable is a generic type that can be used to represent a nullable value. If valid is true, then data is considered non-null.
// If valid is false, then data is considered null. Nullable supports all primitive types, except pointers, slices, maps, channels and structs.
// Nullable supports
type Nullable[T primitive] struct {
valid bool
data T
}

// New returns a new null Nullable.
func New[T primitive]() Nullable[T] {
return Nullable[T]{}
}

// From returns a non-null Nullable with the given data.
func From[T primitive](data T) Nullable[T] {
return Nullable[T]{
valid: true,
data: data,
}
}

// Zero returns a non-null Nullable with the zero value of the given type.
func Zero[T primitive]() Nullable[T] {
return Nullable[T]{
valid: true,
}
}

// Get returns the data and a boolean indicating whether the Nullable is considered null or non-null.
// If boolean is false, then Nullable is considered null. If boolean is true, then Nullable is considered non-null.
func (n Nullable[T]) Get() (T, bool) {
return n.data, n.valid
}

// Data returns the without checking if Nullable is considered null. Only use this if you are sure that Nullable is non-null.
func (n Nullable[T]) Data() T {
return n.data
}

// Set sets the data and marks it as non-null.
func (n *Nullable[T]) Set(data T) {
n.valid = true
n.data = data
}

// SetNull marks the data as null.
func (n *Nullable[T]) SetNull() {
var zero T
n.valid = false
n.data = zero
}

// SetZero sets the data to the zero value of the given type and marks it as non-null.
func (n *Nullable[T]) SetZero() {
var zero T
n.valid = true
n.data = zero
}

// IsValid returns true if the Nullable is non-null.
func (n Nullable[T]) IsValid() bool {
return n.valid
}

// IsZero returns true if the Nullable is non-null and is the zero value of the given type.
func (n Nullable[T]) IsZero() bool {
if !n.valid {
return false
}
var zero T
return n.data == zero
}

// Ptr returns a pointer to the data. If the Nullable is null, then nil is returned.
func (n Nullable[T]) Ptr() *T {
if !n.valid {
return nil
}
return &n.data
}

// Equal returns true if both Nullable are null or if both Nullable are non-null and have the same data.
func (n Nullable[T]) Equal(other Nullable[T]) bool {
if !n.valid && !other.valid {
return true
}
if n.valid && other.valid && n.data == other.data {
return true
}
return false
}

// MarshalJSON implements json.Marshaler interface. If the Nullable is considered null, then "null" is returned.
func (n Nullable[T]) MarshalJSON() ([]byte, error) {
if !n.valid {
return json.Marshal(nil)
}
data, err := json.Marshal(n.data)
if err != nil {
return nil, errors.WithStack(err)
}
return data, nil
}

// UnmarshalJSON implements json.Unmarshaler interface. If "null" is passed, then the Nullable is marked as null.
// Otherwise, the data is marked as non-null and the data is unmarshalled.
func (n *Nullable[T]) UnmarshalJSON(data []byte) error {
if bytes.EqualFold(data, nullBytes) {
n.valid = false
return nil
}
if err := json.Unmarshal(data, &n.data); err != nil {
n.valid = false
return errors.WithStack(err)
}

n.valid = true
return nil
}
Loading