aboutsummaryrefslogtreecommitdiffhomepage
path: root/identity/identity.go
blob: 9236f08769e37adba0264a2bc2119f53a49f0cbb (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package identity

import (
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
)

// NewIdentityManager creates a new Manager starting at id.
func NewManager(id Provider) Manager {
	return &identityManager{
		Provider: id,
		ids:      Identities{id.GetIdentity(): id},
	}
}

// NewPathIdentity creates a new Identity with the two identifiers
// type and path.
func NewPathIdentity(typ, pat string) PathIdentity {
	pat = strings.ToLower(strings.TrimPrefix(filepath.ToSlash(pat), "/"))
	return PathIdentity{Type: typ, Path: pat}
}

// Identities stores identity providers.
type Identities map[Identity]Provider

func (ids Identities) search(depth int, id Identity) Provider {
	if v, found := ids[id.GetIdentity()]; found {
		return v
	}

	depth++

	// There may be infinite recursion in templates.
	if depth > 100 {
		// Bail out.
		return nil
	}

	for _, v := range ids {
		switch t := v.(type) {
		case IdentitiesProvider:
			if nested := t.GetIdentities().search(depth, id); nested != nil {
				return nested
			}
		}
	}
	return nil
}

// IdentitiesProvider provides all Identities.
type IdentitiesProvider interface {
	GetIdentities() Identities
}

// Identity represents an thing that can provide an identify. This can be
// any Go type, but the Identity returned by GetIdentify must be hashable.
type Identity interface {
	Provider
	Name() string
}

// Manager manages identities, and is itself a Provider of Identity.
type Manager interface {
	SearchProvider
	Add(ids ...Provider)
	Reset()
}

// SearchProvider provides access to the chained set of identities.
type SearchProvider interface {
	Provider
	IdentitiesProvider
	Search(id Identity) Provider
}

// A PathIdentity is a common identity identified by a type and a path, e.g. "layouts" and "_default/single.html".
type PathIdentity struct {
	Type string
	Path string
}

// GetIdentity returns itself.
func (id PathIdentity) GetIdentity() Identity {
	return id
}

// Name returns the Path.
func (id PathIdentity) Name() string {
	return id.Path
}

// A KeyValueIdentity a general purpose identity.
type KeyValueIdentity struct {
	Key   string
	Value string
}

// GetIdentity returns itself.
func (id KeyValueIdentity) GetIdentity() Identity {
	return id
}

// Name returns the Key.
func (id KeyValueIdentity) Name() string {
	return id.Key
}

// Provider provides the hashable Identity.
type Provider interface {
	// GetIdentity is for internal use.
	GetIdentity() Identity
}

type identityManager struct {
	sync.Mutex
	Provider
	ids Identities
}

func (im *identityManager) Add(ids ...Provider) {
	im.Lock()
	for _, id := range ids {
		im.ids[id.GetIdentity()] = id
	}
	im.Unlock()
}

func (im *identityManager) Reset() {
	im.Lock()
	id := im.GetIdentity()
	im.ids = Identities{id.GetIdentity(): id}
	im.Unlock()
}

// TODO(bep) these identities are currently only read on server reloads
// so there should be no concurrency issues, but that may change.
func (im *identityManager) GetIdentities() Identities {
	im.Lock()
	defer im.Unlock()
	return im.ids
}

func (im *identityManager) Search(id Identity) Provider {
	im.Lock()
	defer im.Unlock()
	return im.ids.search(0, id.GetIdentity())
}

// Incrementer increments and returns the value.
// Typically used for IDs.
type Incrementer interface {
	Incr() int
}

// IncrementByOne implements Incrementer adding 1 every time Incr is called.
type IncrementByOne struct {
	counter uint64
}

func (c *IncrementByOne) Incr() int {
	return int(atomic.AddUint64(&c.counter, uint64(1)))
}