first commit
This commit is contained in:
commit
2147470e13
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# package apicontext
|
||||||
|
apicontext simply combines the context.Context with its corresponding context.CancelFunc,
|
||||||
|
making it simple to manage objects with context. It comes with some helper functiosn that
|
||||||
|
can replace infinite `for` loops (see `ContextIsNotDone`)
|
76
apicontext.go
Normal file
76
apicontext.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package apicontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextIsDone(ctx context.Context) bool {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextIsNotDone(ctx context.Context) bool { return !ContextIsDone(ctx) }
|
||||||
|
|
||||||
|
func Background() context.Context { return context.Background() }
|
||||||
|
|
||||||
|
func (c *Context) Cancel() {
|
||||||
|
if c.cancel != nil {
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
return c.ctx.Deadline()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Done() <-chan struct{} {
|
||||||
|
return c.ctx.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Err() error {
|
||||||
|
return c.ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Value(key any) any {
|
||||||
|
return c.ctx.Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) IsDone() bool {
|
||||||
|
return ContextIsDone(c.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) IsNotDone() bool {
|
||||||
|
return ContextIsNotDone(c.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
func WithCancel(parent context.Context) *Context {
|
||||||
|
ctx, cancel := context.WithCancel(parent)
|
||||||
|
return &Context{ctx: ctx, cancel: cancel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTimeout(parent context.Context, timeout time.Duration) *Context {
|
||||||
|
ctx, cancel := context.WithTimeout(parent, timeout)
|
||||||
|
return &Context{ctx: ctx, cancel: cancel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDeadline(parent context.Context, deadline time.Time) *Context {
|
||||||
|
ctx, cancel := context.WithDeadline(parent, deadline)
|
||||||
|
return &Context{ctx: ctx, cancel: cancel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithValue(parent context.Context, key, val any) *Context {
|
||||||
|
ctx := context.WithValue(parent, key, val)
|
||||||
|
return &Context{ctx: ctx}
|
||||||
|
}
|
88
apicontext_test.go
Normal file
88
apicontext_test.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package apicontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContextIsDone(t *testing.T) {
|
||||||
|
ctx := WithCancel(Background())
|
||||||
|
defer ctx.Cancel()
|
||||||
|
if ContextIsDone(ctx) {
|
||||||
|
t.Fatalf("error: context was done before cancel()\n")
|
||||||
|
} else if err := ctx.Err(); err != nil {
|
||||||
|
t.Fatalf("error: context reporting error %v; expected nil\n", err)
|
||||||
|
}
|
||||||
|
ctx.Cancel()
|
||||||
|
if !ContextIsDone(ctx) {
|
||||||
|
t.Fatalf("error: context was not done after cancel()\n")
|
||||||
|
} else if want, got := context.Canceled, ctx.Err(); !errors.Is(got, want) {
|
||||||
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextWithTimeout(t *testing.T) {
|
||||||
|
timeout := time.Second
|
||||||
|
controlCtx, cancelControlCtx := context.WithTimeout(Background(), timeout*2)
|
||||||
|
defer cancelControlCtx()
|
||||||
|
ctx := WithTimeout(Background(), timeout)
|
||||||
|
defer ctx.Cancel()
|
||||||
|
if want, got := true, ctx.IsNotDone(); want != got {
|
||||||
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
||||||
|
} else if want, got := error(nil), ctx.Err(); want != got {
|
||||||
|
t.Fatalf("error: wanted err == %v; got %v\n", want, got)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-controlCtx.Done():
|
||||||
|
t.Fatalf("error: control timeout reached - apicontext.WithTimeout failed to fire\n")
|
||||||
|
}
|
||||||
|
if want, got := true, ctx.IsDone(); want != got {
|
||||||
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
||||||
|
} else if want, got := context.DeadlineExceeded, ctx.Err(); !errors.Is(got, want) {
|
||||||
|
t.Fatalf("error: wanted %v; got %v\n", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextWithDeadline(t *testing.T) {
|
||||||
|
deadline := time.Now().Add(time.Second)
|
||||||
|
controlCtx, cancelControlCtx := context.WithDeadline(Background(), deadline.Add(time.Second))
|
||||||
|
defer cancelControlCtx()
|
||||||
|
ctx := WithDeadline(Background(), deadline)
|
||||||
|
defer ctx.Cancel()
|
||||||
|
if gotDeadline, ok := ctx.Deadline(); !ok {
|
||||||
|
t.Fatalf("error: deadline !ok\n")
|
||||||
|
} else if want, got := deadline, gotDeadline; want != got {
|
||||||
|
t.Fatalf("error: wanted deadline %s; got %s\n", want, got)
|
||||||
|
} else if err := ctx.Err(); err != nil {
|
||||||
|
t.Fatalf("error: wanted err == nil; got %v\n", err)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-controlCtx.Done():
|
||||||
|
t.Fatalf("error: control timeout reached - apicontext.WithDeadline failed to fire\n")
|
||||||
|
}
|
||||||
|
if want, got := context.DeadlineExceeded, ctx.Err(); !errors.Is(got, want) {
|
||||||
|
t.Fatalf("error: wanted err == %v; got %v\n", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextWithValue(t *testing.T) {
|
||||||
|
type ValueKey string
|
||||||
|
key, value := ValueKey("TheTestKey"), "TheTestValue"
|
||||||
|
ctx := WithValue(Background(), key, value)
|
||||||
|
// expect no error from cancel
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
t.Fatalf("error: expected no panic; got %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ctx.Cancel()
|
||||||
|
if want, got := value, ctx.Value(key).(string); want != got {
|
||||||
|
t.Fatalf("error: wanted %s; got %s\n", want, got)
|
||||||
|
} else if err := ctx.Err(); err != nil {
|
||||||
|
t.Fatalf("error: got error from context...\n")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user