From 0cf3d1e617bd78063263041267650d97e0f62b71 Mon Sep 17 00:00:00 2001 From: Charles Iliya Krempeaux Date: Sun, 24 Sep 2023 07:55:56 +0900 Subject: [PATCH] initial commits --- nullable.go | 82 ++++++++ nullable_get_test.go | 380 ++++++++++++++++++++++++++++++++++++++ nullable_gostring_test.go | 257 ++++++++++++++++++++++++++ 3 files changed, 719 insertions(+) create mode 100644 nullable.go create mode 100644 nullable_get_test.go create mode 100644 nullable_gostring_test.go diff --git a/nullable.go b/nullable.go new file mode 100644 index 0000000..541fc3a --- /dev/null +++ b/nullable.go @@ -0,0 +1,82 @@ +package nul + +import ( + "fmt" +) + +type Nullable[T any] struct { + value T + isnull bool + something bool +} + +func Nothing[T any]() Nullable[T] { + var nothing Nullable[T] + + return nothing +} + +func Null[T any]() Nullable[T] { + return Nullable[T]{ + isnull:true, + } +} + +func Something[T any](value T) Nullable[T] { + return Nullable[T]{ + something:true, + value:value, + } +} + +func (receiver Nullable[T]) isnothing() bool { + return !receiver.something && !receiver.isnull +} + +func (receiver Nullable[T]) Filter(fn func(T)bool) Nullable[T] { + if receiver.isnothing() { + return Nothing[T]() + } + if receiver.isnull { + return Nothing[T]() + } + + if !fn(receiver.value) { + return Nothing[T]() + } + + return receiver +} + +func (receiver Nullable[T]) Get() (T, bool) { + return receiver.value, receiver.something +} + +func (receiver Nullable[T]) GoString() string { + if receiver.isnothing() { + return fmt.Sprintf("nul.Nothing[%T]()", receiver.value) + } + if receiver.isnull { + return fmt.Sprintf("nul.Null[%T]()", receiver.value) + } + + return fmt.Sprintf("nul.Something[%T](%#v)", receiver.value, receiver.value) +} + +func (receiver Nullable[T]) WhenNothing(fn func()) { + if receiver.isnothing() { + fn() + } +} + +func (receiver Nullable[T]) WhenNull(fn func()) { + if receiver.isnull { + fn() + } +} + +func (receiver Nullable[T]) WhenSomething(fn func(T)) { + if receiver.something { + fn(receiver.value) + } +} diff --git a/nullable_get_test.go b/nullable_get_test.go new file mode 100644 index 0000000..d555a5d --- /dev/null +++ b/nullable_get_test.go @@ -0,0 +1,380 @@ +package nul_test + +import ( + "testing" + + "sourcecode.social/reiver/go-nul" +) + +func TestOptional_Get_string(t *testing.T) { + + tests := []struct{ + Nullable nul.Nullable[string] + ExpectedValue string + ExpectedSomething bool + }{ + { + Nullable: nul.Nothing[string](), + ExpectedValue: "", + ExpectedSomething: false, + }, + + + + { + Nullable: nul.Something(""), + ExpectedValue: "", + ExpectedSomething: true, + }, + { + Nullable: nul.Something("once twice thrice fource"), + ExpectedValue: "once twice thrice fource", + ExpectedSomething: true, + }, + { + Nullable: nul.Something("😈"), + ExpectedValue: "😈", + ExpectedSomething: true, + }, + { + Nullable: nul.Something("۰۱۲۳۴۵۶۷۸۹"), + ExpectedValue: "۰۱۲۳۴۵۶۷۸۹", + ExpectedSomething: true, + }, + } + + for testNumber, test := range tests { + + value, something := test.Nullable.Get() + + { + expected := test.ExpectedSomething + actual := something + + if expected != actual { + t.Errorf("For test #%d, the actual something-flag is not what was expected.", testNumber) + t.Logf("EXPECTED FLAG: %t", expected) + t.Logf("ACTUAL FLAG: %t", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + + { + expected := test.ExpectedValue + actual := value + + if expected != actual { + t.Errorf("For test #%d, the actual value is not what was expected.", testNumber) + t.Logf("EXPECTED VALUE: %q", expected) + t.Logf("ACTUAL VALUE: %q", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + } +} + +func TestOptional_Get_int8(t *testing.T) { + + tests := []struct{ + Nullable nul.Nullable[int8] + ExpectedValue int8 + ExpectedSomething bool + }{ + { + Nullable: nul.Nothing[int8](), + ExpectedValue: 0, + ExpectedSomething: false, + }, + + + + { + Nullable: nul.Something(int8(-127)), + ExpectedValue: int8(-127), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-126)), + ExpectedValue: int8(-126), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-125)), + ExpectedValue: int8(-125), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-124)), + ExpectedValue: int8(-124), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-123)), + ExpectedValue: int8(-123), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-122)), + ExpectedValue: int8(-122), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-121)), + ExpectedValue: int8(-121), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-120)), + ExpectedValue: int8(-120), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-9)), + ExpectedValue: int8(-9), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-8)), + ExpectedValue: int8(-8), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-7)), + ExpectedValue: int8(-7), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-6)), + ExpectedValue: int8(-6), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-5)), + ExpectedValue: int8(-5), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-4)), + ExpectedValue: int8(-4), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-3)), + ExpectedValue: int8(-3), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-2)), + ExpectedValue: int8(-2), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(-1)), + ExpectedValue: int8(-1), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(0)), + ExpectedValue: int8(0), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(1)), + ExpectedValue: int8(1), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(2)), + ExpectedValue: int8(2), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(3)), + ExpectedValue: int8(3), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(4)), + ExpectedValue: int8(4), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(5)), + ExpectedValue: int8(5), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(6)), + ExpectedValue: int8(6), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(7)), + ExpectedValue: int8(7), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(8)), + ExpectedValue: int8(8), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(9)), + ExpectedValue: int8(9), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(120)), + ExpectedValue: int8(120), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(121)), + ExpectedValue: int8(121), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(122)), + ExpectedValue: int8(122), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(123)), + ExpectedValue: int8(123), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(124)), + ExpectedValue: int8(124), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(125)), + ExpectedValue: int8(125), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(126)), + ExpectedValue: int8(126), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(int8(127)), + ExpectedValue: int8(127), + ExpectedSomething: true, + }, + } + + for testNumber, test := range tests { + + value, something := test.Nullable.Get() + + { + expected := test.ExpectedSomething + actual := something + + if expected != actual { + t.Errorf("For test #%d, the actual something-flag is not what was expected.", testNumber) + t.Logf("EXPECTED FLAG: %t", expected) + t.Logf("ACTUAL FLAG: %t", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + + { + expected := test.ExpectedValue + actual := value + + if expected != actual { + t.Errorf("For test #%d, the actual value is not what was expected.", testNumber) + t.Logf("EXPECTED VALUE: %q", expected) + t.Logf("ACTUAL VALUE: %q", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + } +} + +func TestOptional_Get_uint8(t *testing.T) { + + tests := []struct{ + Nullable nul.Nullable[uint8] + ExpectedValue uint8 + ExpectedSomething bool + }{ + { + Nullable: nul.Nothing[uint8](), + ExpectedValue: 0, + ExpectedSomething: false, + }, + + + + { + Nullable: nul.Something(uint8(0)), + ExpectedValue: uint8(0), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(uint8(1)), + ExpectedValue: uint8(1), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(uint8(2)), + ExpectedValue: uint8(2), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(uint8(254)), + ExpectedValue: uint8(254), + ExpectedSomething: true, + }, + { + Nullable: nul.Something(uint8(255)), + ExpectedValue: uint8(255), + ExpectedSomething: true, + }, + } + + for testNumber, test := range tests { + + value, something := test.Nullable.Get() + + { + expected := test.ExpectedSomething + actual := something + + if expected != actual { + t.Errorf("For test #%d, the actual something-flag is not what was expected.", testNumber) + t.Logf("EXPECTED FLAG: %t", expected) + t.Logf("ACTUAL FLAG: %t", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + + { + expected := test.ExpectedValue + actual := value + + if expected != actual { + t.Errorf("For test #%d, the actual value is not what was expected.", testNumber) + t.Logf("EXPECTED VALUE: %q", expected) + t.Logf("ACTUAL VALUE: %q", actual) + t.Logf("NULLABLE: %#v", test.Nullable) + /////////////////////// CONTINUE + continue + } + } + } +} diff --git a/nullable_gostring_test.go b/nullable_gostring_test.go new file mode 100644 index 0000000..fdd0134 --- /dev/null +++ b/nullable_gostring_test.go @@ -0,0 +1,257 @@ +package nul_test + +import ( + "testing" + + "fmt" + + "sourcecode.social/reiver/go-nul" +) + +func TestNullable_GoString(t *testing.T) { + + tests := []struct{ + Value any + Expected string + }{ + { + Value: "", + Expected: `nul.Something[string]("")`, + }, + { + Value: "once twice thrice fource", + Expected: `nul.Something[string]("once twice thrice fource")`, + }, + { + Value: "apple banana cherry", + Expected: `nul.Something[string]("apple banana cherry")`, + }, + + + + { + Value: uint8 (0x0), + Expected: `nul.Something[uint8](0x0)`, + }, + { + Value: uint8 (0x1), + Expected: `nul.Something[uint8](0x1)`, + }, + { + Value: uint8 (0x2), + Expected: `nul.Something[uint8](0x2)`, + }, + { + Value: uint8 (0xfe), + Expected: `nul.Something[uint8](0xfe)`, + }, + { + Value: uint8 (0xff), + Expected: `nul.Something[uint8](0xff)`, + }, + + + + { + Value: uint16 (0x0), + Expected: `nul.Something[uint16](0x0)`, + }, + { + Value: uint16 (0x1), + Expected: `nul.Something[uint16](0x1)`, + }, + { + Value: uint16 (0x2), + Expected: `nul.Something[uint16](0x2)`, + }, + { + Value: uint16 (0xfe), + Expected: `nul.Something[uint16](0xfe)`, + }, + { + Value: uint16 (0xff), + Expected: `nul.Something[uint16](0xff)`, + }, + { + Value: uint16 (0x100), + Expected: `nul.Something[uint16](0x100)`, + }, + { + Value: uint16 (0x101), + Expected: `nul.Something[uint16](0x101)`, + }, + { + Value: uint16 (0x102), + Expected: `nul.Something[uint16](0x102)`, + }, + { + Value: uint16 (0xfffe), + Expected: `nul.Something[uint16](0xfffe)`, + }, + { + Value: uint16 (0xffff), + Expected: `nul.Something[uint16](0xffff)`, + }, + + + + { + Value: struct { A string; B int }{A:"joeblow",B:7}, + Expected: `nul.Something[struct { A string; B int }](struct { A string; B int }{A:"joeblow", B:7})`, + }, + } + + for testNumber, test := range tests { + + op := nul.Something(test.Value) + gostring := op.GoString() + + + { + expected := test.Expected + actual := gostring + + if expected != actual { + t.Errorf("For test #%d, the actual go-string is not what was expected.", testNumber) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + t.Logf("VALUE-TYPE: %T", test.Value) + t.Logf("VALUE: %#v", test.Value) + //////////////////////// CONTINUE + continue + } + } + } +} + +func TestNullable_GoString_nothing(t *testing.T) { + + tests := []struct{ + Nullable fmt.GoStringer + Expected string + }{ + { + Nullable: nul.Nothing[string](), + Expected: `nul.Nothing[string]()`, + }, + + + + { + Nullable: nul.Nothing[int8](), + Expected: `nul.Nothing[int8]()`, + }, + { + Nullable: nul.Nothing[int16](), + Expected: `nul.Nothing[int16]()`, + }, + { + Nullable: nul.Nothing[int32](), + Expected: `nul.Nothing[int32]()`, + }, + { + Nullable: nul.Nothing[int64](), + Expected: `nul.Nothing[int64]()`, + }, + + + + { + Nullable: nul.Nothing[uint8](), + Expected: `nul.Nothing[uint8]()`, + }, + { + Nullable: nul.Nothing[uint16](), + Expected: `nul.Nothing[uint16]()`, + }, + { + Nullable: nul.Nothing[uint32](), + Expected: `nul.Nothing[uint32]()`, + }, + { + Nullable: nul.Nothing[uint64](), + Expected: `nul.Nothing[uint64]()`, + }, + } + + for testNumber, test := range tests { + + expected := test.Expected + actual := test.Nullable.GoString() + + if expected != actual { + t.Errorf("For test #%d, the actual go-string value is not what was expected.", testNumber) + t.Logf("EXPECTED GO-STRING: %q", expected) + t.Logf("ACTUAL GO-STRING: %q", actual) + t.Logf("TYPE: %T", test.Nullable) + /////////////// CONTINUE + continue + } + } +} + +func TestNullable_GoString_null(t *testing.T) { + + tests := []struct{ + Nullable fmt.GoStringer + Expected string + }{ + { + Nullable: nul.Null[string](), + Expected: `nul.Null[string]()`, + }, + + + + { + Nullable: nul.Null[int8](), + Expected: `nul.Null[int8]()`, + }, + { + Nullable: nul.Null[int16](), + Expected: `nul.Null[int16]()`, + }, + { + Nullable: nul.Null[int32](), + Expected: `nul.Null[int32]()`, + }, + { + Nullable: nul.Null[int64](), + Expected: `nul.Null[int64]()`, + }, + + + + { + Nullable: nul.Null[uint8](), + Expected: `nul.Null[uint8]()`, + }, + { + Nullable: nul.Null[uint16](), + Expected: `nul.Null[uint16]()`, + }, + { + Nullable: nul.Null[uint32](), + Expected: `nul.Null[uint32]()`, + }, + { + Nullable: nul.Null[uint64](), + Expected: `nul.Null[uint64]()`, + }, + } + + for testNumber, test := range tests { + + expected := test.Expected + actual := test.Nullable.GoString() + + if expected != actual { + t.Errorf("For test #%d, the actual go-string value is not what was expected.", testNumber) + t.Logf("EXPECTED GO-STRING: %q", expected) + t.Logf("ACTUAL GO-STRING: %q", actual) + t.Logf("TYPE: %T", test.Nullable) + /////////////// CONTINUE + continue + } + } +}