diff --git a/temporal.go b/temporal.go new file mode 100644 index 0000000..687e35d --- /dev/null +++ b/temporal.go @@ -0,0 +1,114 @@ +package tmp + +import ( + "fmt" + "time" +) + +type Temporal[T any] struct { + value T + until int64 // unit-time + istemporary bool + ispermanent bool +} + +func Nothing[T any]() Temporal[T] { + var nothing Temporal[T] + + return nothing +} + +func Permanent[T any](value T) Temporal[T] { + return Temporal[T]{ + ispermanent:true, + value:value, + } +} + +func Temporary[T any](value T, until int64) Temporal[T] { + return Temporal[T]{ + istemporary:true, + value:value, + until:until, + } +} + +func (receiver Temporal[T]) isnothing() bool { + return !receiver.istemporary && !receiver.ispermanent +} + +func (receiver Temporal[T]) Filter(fn func(T)bool) Temporal[T] { + if receiver.isnothing() { + return Nothing[T]() + } + if receiver.IsDefunct() { + return Nothing[T]() + } + + if !fn(receiver.value) { + return Nothing[T]() + } + + return receiver +} + +func (receiver Temporal[T]) Get() (T, bool) { + return receiver.value, receiver.IsExtant() +} + +func (receiver Temporal[T]) GoString() string { + if receiver.isnothing() { + return fmt.Sprintf("tmp.Nothing[%T]()", receiver.value) + } + if receiver.ispermanent { + return fmt.Sprintf("tmp.Permanent[%T](%#v)", receiver.value, receiver.value) + } + + return fmt.Sprintf("tmp.Temporary[%T](%#v, %d)", receiver.value, receiver.value, receiver.until) +} + +func (receiver Temporal[T]) IsDefunct() bool { + if receiver.ispermanent { + return false + } + + if receiver.istemporary { + var now int64 = time.Now().Unix() + + return receiver.until < now + } + + return true +} + +func (receiver Temporal[T]) IsExtant() bool { + if receiver.ispermanent { + return true + } + + if receiver.istemporary { + var now int64 = time.Now().Unix() + + return now <= receiver.until + } + + return false +} + +func (receiver Temporal[T]) WhenNothing(fn func()) { + if receiver.isnothing() { + fn() + } +} + +func (receiver Temporal[T]) WhenDefunct(fn func()) { + if receiver.IsDefunct() { + fn() + } +} + +func (receiver Temporal[T]) WhenExtant(fn func(T)) { + if receiver.IsExtant() { + fn(receiver.value) + } +} diff --git a/temporal_filter_test.go b/temporal_filter_test.go new file mode 100644 index 0000000..5c3378a --- /dev/null +++ b/temporal_filter_test.go @@ -0,0 +1,107 @@ +package tmp_test + +import ( + "testing" + + "sourcecode.social/reiver/go-tmp" +) + +func TestTemporal_Filter_int(t *testing.T) { + + tests := []struct{ + Temporal tmp.Temporal[int] + Expected tmp.Temporal[int] + }{ + { + Temporal: tmp.Nothing[int](), + Expected: tmp.Nothing[int](), + }, + + + + { + Temporal: tmp.Permanent[int](-2), + Expected: tmp.Permanent[int](-2), + }, + { + Temporal: tmp.Permanent[int](-1), + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Permanent[int](0), + Expected: tmp.Permanent[int](0), + }, + { + Temporal: tmp.Permanent[int](1), + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Permanent[int](2), + Expected: tmp.Permanent[int](2), + }, + + + + { + Temporal: tmp.Temporary[int](-2, 9_223_372_036_854_775_807), // int64 problem + Expected: tmp.Temporary[int](-2, 9_223_372_036_854_775_807), + }, + { + Temporal: tmp.Temporary[int](-1, 9_223_372_036_854_775_806), // int64 problem + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](0, 9_223_372_036_854_775_805), // int64 problem + Expected: tmp.Temporary[int](0, 9_223_372_036_854_775_805), + }, + { + Temporal: tmp.Temporary[int](1, 9_223_372_036_854_775_804), // int64 problem + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](2, 9_223_372_036_854_775_803), + Expected: tmp.Temporary[int](2, 9_223_372_036_854_775_803), // int64 problem + }, + + + + { + Temporal: tmp.Temporary[int](-2, 111), // supposed to be in the past + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](-1, 111), // supposed to be in the past + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](0, 111), // supposed to be in the past + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](1, 111), // supposed to be in the past + Expected: tmp.Nothing[int](), + }, + { + Temporal: tmp.Temporary[int](2, 111), // supposed to be in the past + Expected: tmp.Nothing[int](), + }, + } + + for testNumber, test := range tests { + + fn := func(value int) bool { + return 0 == (value % 2) + } + + actual := test.Temporal.Filter(fn) + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual value is not what was expected.", testNumber) + t.Logf("EXPECTED: %#v", expected) + t.Logf("ACTUAL: %#v", actual) + /////////////// CONTINUE + continue + } + } +} diff --git a/temporal_gostring_test.go b/temporal_gostring_test.go new file mode 100644 index 0000000..da258d8 --- /dev/null +++ b/temporal_gostring_test.go @@ -0,0 +1,325 @@ +package tmp_test + +import ( + "testing" + + "fmt" + + "sourcecode.social/reiver/go-tmp" +) + +func TestTemporal_GoString_permanent(t *testing.T) { + + tests := []struct{ + Value any + Expected string + }{ + { + Value: "", + Expected: `tmp.Permanent[string]("")`, + }, + { + Value: "once twice thrice fource", + Expected: `tmp.Permanent[string]("once twice thrice fource")`, + }, + { + Value: "apple banana cherry", + Expected: `tmp.Permanent[string]("apple banana cherry")`, + }, + + + + { + Value: uint8 (0x0), + Expected: `tmp.Permanent[uint8](0x0)`, + }, + { + Value: uint8 (0x1), + Expected: `tmp.Permanent[uint8](0x1)`, + }, + { + Value: uint8 (0x2), + Expected: `tmp.Permanent[uint8](0x2)`, + }, + { + Value: uint8 (0xfe), + Expected: `tmp.Permanent[uint8](0xfe)`, + }, + { + Value: uint8 (0xff), + Expected: `tmp.Permanent[uint8](0xff)`, + }, + + + + { + Value: uint16 (0x0), + Expected: `tmp.Permanent[uint16](0x0)`, + }, + { + Value: uint16 (0x1), + Expected: `tmp.Permanent[uint16](0x1)`, + }, + { + Value: uint16 (0x2), + Expected: `tmp.Permanent[uint16](0x2)`, + }, + { + Value: uint16 (0xfe), + Expected: `tmp.Permanent[uint16](0xfe)`, + }, + { + Value: uint16 (0xff), + Expected: `tmp.Permanent[uint16](0xff)`, + }, + { + Value: uint16 (0x100), + Expected: `tmp.Permanent[uint16](0x100)`, + }, + { + Value: uint16 (0x101), + Expected: `tmp.Permanent[uint16](0x101)`, + }, + { + Value: uint16 (0x102), + Expected: `tmp.Permanent[uint16](0x102)`, + }, + { + Value: uint16 (0xfffe), + Expected: `tmp.Permanent[uint16](0xfffe)`, + }, + { + Value: uint16 (0xffff), + Expected: `tmp.Permanent[uint16](0xffff)`, + }, + + + + { + Value: struct { A string; B int }{A:"joeblow",B:7}, + Expected: `tmp.Permanent[struct { A string; B int }](struct { A string; B int }{A:"joeblow", B:7})`, + }, + } + + for testNumber, test := range tests { + + op := tmp.Permanent(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 TestTemporal_GoString_nothing(t *testing.T) { + + tests := []struct{ + Temporal fmt.GoStringer + Expected string + }{ + { + Temporal: tmp.Nothing[string](), + Expected: `tmp.Nothing[string]()`, + }, + + + + { + Temporal: tmp.Nothing[int8](), + Expected: `tmp.Nothing[int8]()`, + }, + { + Temporal: tmp.Nothing[int16](), + Expected: `tmp.Nothing[int16]()`, + }, + { + Temporal: tmp.Nothing[int32](), + Expected: `tmp.Nothing[int32]()`, + }, + { + Temporal: tmp.Nothing[int64](), + Expected: `tmp.Nothing[int64]()`, + }, + + + + { + Temporal: tmp.Nothing[uint8](), + Expected: `tmp.Nothing[uint8]()`, + }, + { + Temporal: tmp.Nothing[uint16](), + Expected: `tmp.Nothing[uint16]()`, + }, + { + Temporal: tmp.Nothing[uint32](), + Expected: `tmp.Nothing[uint32]()`, + }, + { + Temporal: tmp.Nothing[uint64](), + Expected: `tmp.Nothing[uint64]()`, + }, + } + + for testNumber, test := range tests { + + expected := test.Expected + actual := test.Temporal.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.Temporal) + /////////////// CONTINUE + continue + } + } +} + +func TestTemporal_GoString_temporary(t *testing.T) { + + tests := []struct{ + Value any + Until int64 + Expected string + }{ + { + Value: "", + Until: 0, + Expected: `tmp.Temporary[string]("", 0)`, + }, + { + Value: "once twice thrice fource", + Until: 1234, + Expected: `tmp.Temporary[string]("once twice thrice fource", 1234)`, + }, + { + Value: "apple banana cherry", + Until: 979899, + Expected: `tmp.Temporary[string]("apple banana cherry", 979899)`, + }, + + + + { + Value: uint8 (0x0), + Until: 101, + Expected: `tmp.Temporary[uint8](0x0, 101)`, + }, + { + Value: uint8 (0x1), + Until: 111, + Expected: `tmp.Temporary[uint8](0x1, 111)`, + }, + { + Value: uint8 (0x2), + Until: 121, + Expected: `tmp.Temporary[uint8](0x2, 121)`, + }, + { + Value: uint8 (0xfe), + Until: 989, + Expected: `tmp.Temporary[uint8](0xfe, 989)`, + }, + { + Value: uint8 (0xff), + Until: 999, + Expected: `tmp.Temporary[uint8](0xff, 999)`, + }, + + + + { + Value: uint16 (0x0), + Until: 303, + Expected: `tmp.Temporary[uint16](0x0, 303)`, + }, + { + Value: uint16 (0x1), + Until: 313, + Expected: `tmp.Temporary[uint16](0x1, 313)`, + }, + { + Value: uint16 (0x2), + Until: 323, + Expected: `tmp.Temporary[uint16](0x2, 323)`, + }, + { + Value: uint16 (0xfe), + Until: 383, + Expected: `tmp.Temporary[uint16](0xfe, 383)`, + }, + { + Value: uint16 (0xff), + Until: 393, + Expected: `tmp.Temporary[uint16](0xff, 393)`, + }, + { + Value: uint16 (0x100), + Until: 3003, + Expected: `tmp.Temporary[uint16](0x100, 3003)`, + }, + { + Value: uint16 (0x101), + Until: 3113, + Expected: `tmp.Temporary[uint16](0x101, 3113)`, + }, + { + Value: uint16 (0x102), + Until: 3223, + Expected: `tmp.Temporary[uint16](0x102, 3223)`, + }, + { + Value: uint16 (0xfffe), + Until: 3883, + Expected: `tmp.Temporary[uint16](0xfffe, 3883)`, + }, + { + Value: uint16 (0xffff), + Until: 3993, + Expected: `tmp.Temporary[uint16](0xffff, 3993)`, + }, + + + + { + Value: struct { A string; B int }{A:"joeblow",B:7}, + Until: 9876543210, + Expected: `tmp.Temporary[struct { A string; B int }](struct { A string; B int }{A:"joeblow", B:7}, 9876543210)`, + }, + } + + for testNumber, test := range tests { + + op := tmp.Temporary(test.Value, test.Until) + 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 + } + } + } +} diff --git a/temporal_is_defunct_test.go b/temporal_is_defunct_test.go new file mode 100644 index 0000000..ccf37e1 --- /dev/null +++ b/temporal_is_defunct_test.go @@ -0,0 +1,48 @@ +package tmp_test + +import ( + "testing" + + "time" + + "sourcecode.social/reiver/go-tmp" +) + +func TestTemporal_IsDefunct(t *testing.T) { + + tests := []struct{ + Temporal tmp.Temporal[string] + Expected bool + }{ + { + Temporal: tmp.Nothing[string](), + Expected: true, + }, + { + Temporal: tmp.Permanent[string]("forever"), + Expected: false, + }, + { + Temporal: tmp.Temporary[string]("expired", time.Now().Unix()-999), // supposed to be in the pastasx + Expected: true, + }, + { + Temporal: tmp.Temporary[string]("not-expired", time.Now().Unix()+99999), + Expected: false, + }, + } + + for testNumber, test := range tests { + + actual := test.Temporal.IsDefunct() + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual value for is-defunct is not what was expected.", testNumber) + t.Logf("EXPECTED: %t", expected) + t.Logf("ACTUAL: %t", actual) + t.Logf("TEMPORAL: %#v", test.Temporal) + continue + } + } +} diff --git a/temporal_is_nothing_test.go b/temporal_is_nothing_test.go new file mode 100644 index 0000000..31ceda3 --- /dev/null +++ b/temporal_is_nothing_test.go @@ -0,0 +1,46 @@ +package tmp + +import ( + "testing" + + "time" +) + +func TestTemporal_IsNothing(t *testing.T) { + + tests := []struct{ + Temporal Temporal[string] + Expected bool + }{ + { + Temporal: Nothing[string](), + Expected: true, + }, + { + Temporal: Permanent[string]("forever"), + Expected: false, + }, + { + Temporal: Temporary[string]("expired", time.Now().Unix()-999), + Expected: false, + }, + { + Temporal: Temporary[string]("not-expired", time.Now().Unix()+99999), + Expected: false, + }, + } + + for testNumber, test := range tests { + + actual := test.Temporal.isnothing() + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual value for is-defunct is not what was expected.", testNumber) + t.Logf("EXPECTED: %t", expected) + t.Logf("ACTUAL: %t", actual) + t.Logf("TEMPORAL: %#v", test.Temporal) + continue + } + } +}