Skip to content

Commit

Permalink
Merge pull request #12 from Cleverse/feat/nullable
Browse files Browse the repository at this point in the history
  • Loading branch information
Planxnx authored Nov 16, 2023
2 parents 2364608 + 572095f commit 3e54ce2
Show file tree
Hide file tree
Showing 7 changed files with 653 additions and 0 deletions.
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=
138 changes: 138 additions & 0 deletions nullable/nullable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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"
)

// Primitive is a type constraint for all primitive types, except pointers, slices, maps, channels and structs.
type Primitive interface {
~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

0 comments on commit 3e54ce2

Please sign in to comment.