summaryrefslogtreecommitdiff
path: root/types/cookie/cookie.go
blob: e1cffca1e840348e2ecb5ba0d59ec9d7e74b5e3f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// package cookie implements a specialized version of a context
// - It is cancellable
// - It has a channel associated with it to reply to state callbacks
// - It can be marshalled by having a cgo Handle attached to it
package cookie

import (
	"context"
	"encoding/json"
	"runtime/cgo"

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

type Cookie struct {
	c         chan string
	ctx       context.Context
	ctxCancel context.CancelFunc
	H         cgo.Handle
}

// NewWithContext creates a new cookie with a context
// It stores the cancel and channel inside of the struct
func NewWithContext(ctx context.Context) Cookie {
	ctx, cancel := context.WithCancel(ctx)
	return Cookie{
		c:         make(chan string),
		ctx:       ctx,
		ctxCancel: cancel,
	}
}

// MarshalJSON marshals the cookie to JSON
func (c *Cookie) MarshalJSON() ([]byte, error) {
	if c.H == 0 {
		return nil, errors.New("no associated handle found")
	}
	return json.Marshal(c.H)
}

// Receive receives a value from the cookie up until the context is done or errchan gets an error
// This error chan is used for goroutines to signal errors that we have to exit early
func (c *Cookie) Receive(errchan chan error) (string, error) {
	select {
	case r := <-c.c:
		return r, nil
	case e := <-errchan:
		return "", e
	case <-c.ctx.Done():
		return "", errors.WrapPrefix(context.Canceled, "receive cookie", 0)
	}
}

// Cancel cancels the cookie by calling the context cancel function
// It returns an error if no such function exists
func (c *Cookie) Cancel() error {
	if c.ctxCancel == nil {
		return errors.New("no cancel function found")
	}
	c.ctxCancel()
	return nil
}

// Send sends data to the cookie channel if the context is not canceled
func (c *Cookie) Send(data string) error {
	select {
	case <-c.ctx.Done():
		return errors.WrapPrefix(context.Canceled, "send cookie", 0)
	default:
		if c.c == nil {
			return errors.New("channel is nil")
		}
		c.c <- data
		return nil
	}
}

// Context gets the underlying context of the cookie
func (c *Cookie) Context() context.Context {
	return c.ctx
}