key.go 5.22 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package datastore

import (
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
4 5
	"path"
	"strings"
Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
6 7

	"github.com/jbenet/datastore.go/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
)

/*
A Key represents the unique identifier of an object.
Our Key scheme is inspired by file systems and Google App Engine key model.

Keys are meant to be unique across a system. Keys are hierarchical,
incorporating more and more specific namespaces. Thus keys can be deemed
'children' or 'ancestors' of other keys::

    Key("/Comedy")
    Key("/Comedy/MontyPython")

Also, every namespace can be parametrized to embed relevant object
information. For example, the Key `name` (most specific namespace) could
include the object type::

    Key("/Comedy/MontyPython/Actor:JohnCleese")
    Key("/Comedy/MontyPython/Sketch:CheeseShop")
    Key("/Comedy/MontyPython/Sketch:CheeseShop/Character:Mousebender")

*/
type Key struct {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
31
	string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32 33
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
34
// NewKey constructs a key from string. it will clean the value.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35
func NewKey(s string) Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
36 37 38
	k := Key{s}
	k.Clean()
	return k
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39 40
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
41
// Clean up a Key, using path.Clean.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42
func (k *Key) Clean() {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
43
	k.string = path.Clean("/" + k.string)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44 45
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
46
// Strings is the string value of Key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
47
func (k Key) String() string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
48
	return k.string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49 50
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
51
// Bytes returns the string value of Key as a []byte
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
52
func (k Key) Bytes() []byte {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
53
	return []byte(k.string)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54 55
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
56 57 58
// List returns the `list` representation of this Key.
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
//   ["Comedy", "MontyPythong", "Actor:JohnCleese"]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59
func (k Key) List() []string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
60
	return strings.Split(k.string, "/")[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
61 62
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
63 64 65
// Reverse returns the reverse of this Key.
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").Reverse()
//   NewKey("/Actor:JohnCleese/MontyPython/Comedy")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66
func (k Key) Reverse() Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
67 68 69 70 71 72
	l := k.List()
	r := make([]string, len(l), len(l))
	for i, e := range l {
		r[len(l)-i-1] = e
	}
	return NewKey(strings.Join(r, "/"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
73 74
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
75 76 77
// Namespaces returns the `namespaces` making up this Key.
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
//   ["Comedy", "MontyPythong", "Actor:JohnCleese"]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
78
func (k Key) Namespaces() []string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
79
	return k.List()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80 81
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
82 83 84
// BaseNamespace returns the "base" namespace of this key (path.Base(filename))
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").BaseNamespace()
//   "Actor:JohnCleese"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85
func (k Key) BaseNamespace() string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
86 87
	n := k.Namespaces()
	return n[len(n)-1]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88 89
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
90 91 92
// Type returns the "type" of this key (value of last namespace).
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
//   "Actor"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93
func (k Key) Type() string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
94
	return NamespaceType(k.BaseNamespace())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95 96
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
97 98 99
// Name returns the "name" of this key (field of last namespace).
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
//   "Actor"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100
func (k Key) Name() string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
101
	return NamespaceValue(k.BaseNamespace())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
102 103
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
104 105 106
// Instance returns an "instance" of this type key (appends value to namespace).
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
//   "JohnCleese"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107
func (k Key) Instance(s string) Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
108
	return NewKey(k.string + ":" + s)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
109 110
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
111 112 113
// Path returns the "path" of this key (parent + type).
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").Path()
//   NewKey("/Comedy/MontyPython/Actor")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
114
func (k Key) Path() Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
115 116
	s := k.Parent().string + "/" + NamespaceType(k.BaseNamespace())
	return NewKey(s)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117 118
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
119 120 121
// Parent returns the `parent` Key of this Key.
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese").Parent()
//   NewKey("/Comedy/MontyPython")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
122
func (k Key) Parent() Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
123 124 125 126 127
	n := k.List()
	if len(n) == 1 {
		return NewKey("/")
	}
	return NewKey(strings.Join(n[:len(n)-1], "/"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
130 131 132
// Child returns the `child` Key of this Key.
//   NewKey("/Comedy/MontyPython").Child("Actor:JohnCleese")
//   NewKey("/Comedy/MontyPython/Actor:JohnCleese")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
133
func (k Key) Child(s string) Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
134
	return NewKey(k.string + "/" + s)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135 136
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
137 138 139
// IsAncestorOf returns whether this key is a prefix of `other`
//   NewKey("/Comedy").IsAncestorOf("/Comedy/MontyPython")
//   true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
140
func (k Key) IsAncestorOf(other Key) bool {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
141 142 143 144
	if other.string == k.string {
		return false
	}
	return strings.HasPrefix(other.string, k.string)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
145 146
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
147 148 149
// IsDescendantOf returns whether this key contains another as a prefix.
//   NewKey("/Comedy/MontyPython").IsDescendantOf("/Comedy")
//   true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
150
func (k Key) IsDescendantOf(other Key) bool {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
151 152 153 154
	if other.string == k.string {
		return false
	}
	return strings.HasPrefix(k.string, other.string)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155 156
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
157
// IsTopLevel returns whether this key has only one namespace.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158
func (k Key) IsTopLevel() bool {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
159
	return len(k.List()) == 1
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
160 161
}

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
162 163 164
// RandomKey returns a randomly (uuid) generated key.
//   RandomKey()
//   NewKey("/f98719ea086343f7b71f32ea9d9d521d")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165
func RandomKey() Key {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
166
	return NewKey(strings.Replace(uuid.New(), "-", "", -1))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180
}

/*
A Key Namespace is like a path element.
A namespace can optionally include a type (delimited by ':')

    > NamespaceValue("Song:PhilosopherSong")
    PhilosopherSong
    > NamespaceType("Song:PhilosopherSong")
    Song
    > NamespaceType("Music:Song:PhilosopherSong")
    Music:Song
*/

Juan Batiz-Benet's avatar
linting  
Juan Batiz-Benet committed
181
// NamespaceType is the first component of a namespace. `foo` in `foo:bar`
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
182
func NamespaceType(namespace string) string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
183 184 185 186 187
	parts := strings.Split(namespace, ":")
	if len(parts) < 2 {
		return ""
	}
	return strings.Join(parts[0:len(parts)-1], ":")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188 189 190
}

func NamespaceValue(namespace string) string {
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
191 192
	parts := strings.Split(namespace, ":")
	return parts[len(parts)-1]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
193
}