Commit 4cb48da2 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

updated datastore

parent ac62d13e
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
}, },
{ {
"ImportPath": "github.com/jbenet/datastore.go", "ImportPath": "github.com/jbenet/datastore.go",
"Rev": "60ebc56447b5a8264cfed3ae3ff48deb984d7cf1" "Rev": "da593f5071b3ce60bf45b548193863bc3c885c3c"
}, },
{ {
"ImportPath": "github.com/jbenet/go-base58", "ImportPath": "github.com/jbenet/go-base58",
......
package datastore_test
import (
. "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
. "launchpad.net/gocheck"
)
// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }
type BasicSuite struct{}
var _ = Suite(&BasicSuite{})
package datastore package datastore
import ( import (
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
"path" "path"
"strings" "strings"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
) )
/* /*
...@@ -30,93 +31,126 @@ type Key struct { ...@@ -30,93 +31,126 @@ type Key struct {
string string
} }
// NewKey constructs a key from string. it will clean the value.
func NewKey(s string) Key { func NewKey(s string) Key {
k := Key{s} k := Key{s}
k.Clean() k.Clean()
return k return k
} }
// Cleans up a Key, using path.Clean. // KeyWithNamespaces constructs a key out of a namespace slice.
func KeyWithNamespaces(ns []string) Key {
return NewKey(strings.Join(ns, "/"))
}
// Clean up a Key, using path.Clean.
func (k *Key) Clean() { func (k *Key) Clean() {
k.string = path.Clean("/" + k.string) k.string = path.Clean("/" + k.string)
} }
// Returns the string value of Key // Strings is the string value of Key
func (k Key) String() string { func (k Key) String() string {
return k.string return k.string
} }
// Returns the bytes value of Key // Bytes returns the string value of Key as a []byte
func (k Key) Bytes() []byte { func (k Key) Bytes() []byte {
return []byte(k.string) return []byte(k.string)
} }
// Returns the `list` representation of this Key. // Equal checks equality of two keys
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List() func (k Key) Equal(k2 Key) bool {
// ["Comedy", "MontyPythong", "Actor:JohnCleese"] return k.string == k2.string
}
// Less checks whether this key is sorted lower than another.
func (k Key) Less(k2 Key) bool {
list1 := k.List()
list2 := k2.List()
for i, c1 := range list1 {
if len(list2) < (i + 1) {
return false
}
c2 := list2[i]
if c1 < c2 {
return true
} else if c1 > c2 {
return false
}
// c1 == c2, continue
}
// list1 is shorter or exactly the same.
return len(list1) < len(list2)
}
// List returns the `list` representation of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// ["Comedy", "MontyPythong", "Actor:JohnCleese"]
func (k Key) List() []string { func (k Key) List() []string {
return strings.Split(k.string, "/")[1:] return strings.Split(k.string, "/")[1:]
} }
// Returns the reverse of this Key. // Reverse returns the reverse of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Reverse() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").Reverse()
// NewKey("/Actor:JohnCleese/MontyPython/Comedy") // NewKey("/Actor:JohnCleese/MontyPython/Comedy")
func (k Key) Reverse() Key { func (k Key) Reverse() Key {
l := k.List() l := k.List()
r := make([]string, len(l), len(l)) r := make([]string, len(l), len(l))
for i, e := range l { for i, e := range l {
r[len(l)-i-1] = e r[len(l)-i-1] = e
} }
return NewKey(strings.Join(r, "/")) return KeyWithNamespaces(r)
} }
// Returns the `namespaces` making up this Key. // Namespaces returns the `namespaces` making up this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// ["Comedy", "MontyPythong", "Actor:JohnCleese"] // ["Comedy", "MontyPythong", "Actor:JohnCleese"]
func (k Key) Namespaces() []string { func (k Key) Namespaces() []string {
return k.List() return k.List()
} }
// Returns the "base" namespace of this key (like path.Base(filename)) // BaseNamespace returns the "base" namespace of this key (path.Base(filename))
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").BaseNamespace() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").BaseNamespace()
// "Actor:JohnCleese" // "Actor:JohnCleese"
func (k Key) BaseNamespace() string { func (k Key) BaseNamespace() string {
n := k.Namespaces() n := k.Namespaces()
return n[len(n)-1] return n[len(n)-1]
} }
// Returns the "type" of this key (value of last namespace). // Type returns the "type" of this key (value of last namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "Actor" // "Actor"
func (k Key) Type() string { func (k Key) Type() string {
return NamespaceType(k.BaseNamespace()) return NamespaceType(k.BaseNamespace())
} }
// Returns the "name" of this key (field of last namespace). // Name returns the "name" of this key (field of last namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "Actor" // "Actor"
func (k Key) Name() string { func (k Key) Name() string {
return NamespaceValue(k.BaseNamespace()) return NamespaceValue(k.BaseNamespace())
} }
// Returns an "instance" of this type key (appends value to namespace). // Instance returns an "instance" of this type key (appends value to namespace).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").List() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").List()
// "JohnCleese" // "JohnCleese"
func (k Key) Instance(s string) Key { func (k Key) Instance(s string) Key {
return NewKey(k.string + ":" + s) return NewKey(k.string + ":" + s)
} }
// Returns the "path" of this key (parent + type). // Path returns the "path" of this key (parent + type).
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Path() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").Path()
// NewKey("/Comedy/MontyPython/Actor") // NewKey("/Comedy/MontyPython/Actor")
func (k Key) Path() Key { func (k Key) Path() Key {
s := k.Parent().string + "/" + NamespaceType(k.BaseNamespace()) s := k.Parent().string + "/" + NamespaceType(k.BaseNamespace())
return NewKey(s) return NewKey(s)
} }
// Returns the `parent` Key of this Key. // Parent returns the `parent` Key of this Key.
// NewKey("/Comedy/MontyPython/Actor:JohnCleese").Parent() // NewKey("/Comedy/MontyPython/Actor:JohnCleese").Parent()
// NewKey("/Comedy/MontyPython") // NewKey("/Comedy/MontyPython")
func (k Key) Parent() Key { func (k Key) Parent() Key {
n := k.List() n := k.List()
if len(n) == 1 { if len(n) == 1 {
...@@ -125,16 +159,23 @@ func (k Key) Parent() Key { ...@@ -125,16 +159,23 @@ func (k Key) Parent() Key {
return NewKey(strings.Join(n[:len(n)-1], "/")) return NewKey(strings.Join(n[:len(n)-1], "/"))
} }
// Returns the `child` Key of this Key. // Child returns the `child` Key of this Key.
// NewKey("/Comedy/MontyPython").Child("Actor:JohnCleese") // NewKey("/Comedy/MontyPython").Child("Actor:JohnCleese")
// NewKey("/Comedy/MontyPython/Actor:JohnCleese") // NewKey("/Comedy/MontyPython/Actor:JohnCleese")
func (k Key) Child(s string) Key { func (k Key) Child(k2 Key) Key {
return NewKey(k.string + "/" + k2.string)
}
// ChildString returns the `child` Key of this Key -- string helper.
// NewKey("/Comedy/MontyPython").Child("Actor:JohnCleese")
// NewKey("/Comedy/MontyPython/Actor:JohnCleese")
func (k Key) ChildString(s string) Key {
return NewKey(k.string + "/" + s) return NewKey(k.string + "/" + s)
} }
// Returns whether this key is an ancestor of `other` // IsAncestorOf returns whether this key is a prefix of `other`
// NewKey("/Comedy").IsAncestorOf("/Comedy/MontyPython") // NewKey("/Comedy").IsAncestorOf("/Comedy/MontyPython")
// true // true
func (k Key) IsAncestorOf(other Key) bool { func (k Key) IsAncestorOf(other Key) bool {
if other.string == k.string { if other.string == k.string {
return false return false
...@@ -142,9 +183,9 @@ func (k Key) IsAncestorOf(other Key) bool { ...@@ -142,9 +183,9 @@ func (k Key) IsAncestorOf(other Key) bool {
return strings.HasPrefix(other.string, k.string) return strings.HasPrefix(other.string, k.string)
} }
// Returns whether this key is a descendent of `other` // IsDescendantOf returns whether this key contains another as a prefix.
// NewKey("/Comedy/MontyPython").IsDescendantOf("/Comedy") // NewKey("/Comedy/MontyPython").IsDescendantOf("/Comedy")
// true // true
func (k Key) IsDescendantOf(other Key) bool { func (k Key) IsDescendantOf(other Key) bool {
if other.string == k.string { if other.string == k.string {
return false return false
...@@ -152,13 +193,14 @@ func (k Key) IsDescendantOf(other Key) bool { ...@@ -152,13 +193,14 @@ func (k Key) IsDescendantOf(other Key) bool {
return strings.HasPrefix(k.string, other.string) return strings.HasPrefix(k.string, other.string)
} }
// IsTopLevel returns whether this key has only one namespace.
func (k Key) IsTopLevel() bool { func (k Key) IsTopLevel() bool {
return len(k.List()) == 1 return len(k.List()) == 1
} }
// Returns a randomly (uuid) generated key. // RandomKey returns a randomly (uuid) generated key.
// RandomKey() // RandomKey()
// NewKey("/f98719ea086343f7b71f32ea9d9d521d") // NewKey("/f98719ea086343f7b71f32ea9d9d521d")
func RandomKey() Key { func RandomKey() Key {
return NewKey(strings.Replace(uuid.New(), "-", "", -1)) return NewKey(strings.Replace(uuid.New(), "-", "", -1))
} }
...@@ -175,6 +217,7 @@ A namespace can optionally include a type (delimited by ':') ...@@ -175,6 +217,7 @@ A namespace can optionally include a type (delimited by ':')
Music:Song Music:Song
*/ */
// NamespaceType is the first component of a namespace. `foo` in `foo:bar`
func NamespaceType(namespace string) string { func NamespaceType(namespace string) string {
parts := strings.Split(namespace, ":") parts := strings.Split(namespace, ":")
if len(parts) < 2 { if len(parts) < 2 {
...@@ -183,7 +226,16 @@ func NamespaceType(namespace string) string { ...@@ -183,7 +226,16 @@ func NamespaceType(namespace string) string {
return strings.Join(parts[0:len(parts)-1], ":") return strings.Join(parts[0:len(parts)-1], ":")
} }
// NamespaceValue returns the last component of a namespace. `baz` in `f:b:baz`
func NamespaceValue(namespace string) string { func NamespaceValue(namespace string) string {
parts := strings.Split(namespace, ":") parts := strings.Split(namespace, ":")
return parts[len(parts)-1] return parts[len(parts)-1]
} }
// KeySlice attaches the methods of sort.Interface to []Key,
// sorting in increasing order.
type KeySlice []Key
func (p KeySlice) Len() int { return len(p) }
func (p KeySlice) Less(i, j int) bool { return p[i].Less(p[j]) }
func (p KeySlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
...@@ -54,14 +54,24 @@ func (ks *KeySuite) SubtestKey(s string, c *C) { ...@@ -54,14 +54,24 @@ func (ks *KeySuite) SubtestKey(s string, c *C) {
c.Check(NewKey(s).Path().String(), Equals, kpath) c.Check(NewKey(s).Path().String(), Equals, kpath)
c.Check(NewKey(s).Instance("inst").String(), Equals, kinstance) c.Check(NewKey(s).Instance("inst").String(), Equals, kinstance)
c.Check(NewKey(s).Child("cchildd").String(), Equals, kchild) c.Check(NewKey(s).Child(NewKey("cchildd")).String(), Equals, kchild)
c.Check(NewKey(s).Child("cchildd").Parent().String(), Equals, fixed) c.Check(NewKey(s).Child(NewKey("cchildd")).Parent().String(), Equals, fixed)
c.Check(NewKey(s).ChildString("cchildd").String(), Equals, kchild)
c.Check(NewKey(s).ChildString("cchildd").Parent().String(), Equals, fixed)
c.Check(NewKey(s).Parent().String(), Equals, kparent) c.Check(NewKey(s).Parent().String(), Equals, kparent)
c.Check(len(NewKey(s).List()), Equals, len(namespaces)) c.Check(len(NewKey(s).List()), Equals, len(namespaces))
c.Check(len(NewKey(s).Namespaces()), Equals, len(namespaces)) c.Check(len(NewKey(s).Namespaces()), Equals, len(namespaces))
for i, e := range NewKey(s).List() { for i, e := range NewKey(s).List() {
c.Check(namespaces[i], Equals, e) c.Check(namespaces[i], Equals, e)
} }
c.Check(NewKey(s), Equals, NewKey(s))
c.Check(NewKey(s).Equal(NewKey(s)), Equals, true)
c.Check(NewKey(s).Equal(NewKey("/fdsafdsa/"+s)), Equals, false)
// less
c.Check(NewKey(s).Less(NewKey(s).Parent()), Equals, false)
c.Check(NewKey(s).Less(NewKey(s).ChildString("foo")), Equals, true)
} }
func (ks *KeySuite) TestKeyBasic(c *C) { func (ks *KeySuite) TestKeyBasic(c *C) {
...@@ -100,7 +110,8 @@ func (ks *KeySuite) TestKeyAncestry(c *C) { ...@@ -100,7 +110,8 @@ func (ks *KeySuite) TestKeyAncestry(c *C) {
CheckTrue(c, !k1.IsAncestorOf(NewKey("/A"))) CheckTrue(c, !k1.IsAncestorOf(NewKey("/A")))
CheckTrue(c, !k2.IsAncestorOf(k2)) CheckTrue(c, !k2.IsAncestorOf(k2))
CheckTrue(c, !k1.IsAncestorOf(k1)) CheckTrue(c, !k1.IsAncestorOf(k1))
c.Check(k1.Child("D").String(), Equals, k2.String()) c.Check(k1.Child(NewKey("D")).String(), Equals, k2.String())
c.Check(k1.ChildString("D").String(), Equals, k2.String())
c.Check(k1.String(), Equals, k2.Parent().String()) c.Check(k1.String(), Equals, k2.Parent().String())
c.Check(k1.Path().String(), Equals, k2.Parent().Path().String()) c.Check(k1.Path().String(), Equals, k2.Parent().Path().String())
} }
...@@ -126,3 +137,21 @@ func (ks *KeySuite) TestRandom(c *C) { ...@@ -126,3 +137,21 @@ func (ks *KeySuite) TestRandom(c *C) {
} }
CheckTrue(c, len(keys) == 1000) CheckTrue(c, len(keys) == 1000)
} }
func (ks *KeySuite) TestLess(c *C) {
checkLess := func(a, b string) {
ak := NewKey(a)
bk := NewKey(b)
c.Check(ak.Less(bk), Equals, true)
c.Check(bk.Less(ak), Equals, false)
}
checkLess("/a/b/c", "/a/b/c/d")
checkLess("/a/b", "/a/b/c/d")
checkLess("/a", "/a/b/c/d")
checkLess("/a/a/c", "/a/b/c")
checkLess("/a/a/d", "/a/b/c")
checkLess("/a/b/c/d/e/f/g/h", "/b")
checkLess("/", "/a")
}
// Package keytransform introduces a Datastore Shim that transforms keys before
// passing them to its child. It can be used to manipulate what keys look like
// to the user, for example namespacing keys, reversing them, etc.
//
// Use the Wrap function to wrap a datastore with any KeyTransform.
// A KeyTransform is simply an interface with two functions, a conversion and
// its inverse. For example:
//
// import (
// ktds "github.com/jbenet/datastore.go/keytransform"
// ds "github.com/jbenet/datastore.go"
// )
//
// func reverseKey(k ds.Key) ds.Key {
// return k.Reverse()
// }
//
// func invertKeys(d ds.Datastore) {
// return ktds.Wrap(d, &ktds.Pair{
// Convert: reverseKey,
// Invert: reverseKey, // reverse is its own inverse.
// })
// }
//
package keytransform
package keytransform
import ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
// KeyMapping is a function that maps one key to annother
type KeyMapping func(ds.Key) ds.Key
// KeyTransform is an object with a pair of functions for (invertibly)
// transforming keys
type KeyTransform interface {
ConvertKey(ds.Key) ds.Key
InvertKey(ds.Key) ds.Key
}
// Datastore is a keytransform.Datastore
type Datastore interface {
ds.Shim
KeyTransform
}
// Wrap wraps a given datastore with a KeyTransform function.
// The resulting wrapped datastore will use the transform on all Datastore
// operations.
func Wrap(child ds.Datastore, t KeyTransform) Datastore {
if t == nil {
panic("t (KeyTransform) is nil")
}
if child == nil {
panic("child (ds.Datastore) is nil")
}
return &ktds{child: child, KeyTransform: t}
}
...@@ -2,49 +2,24 @@ package keytransform ...@@ -2,49 +2,24 @@ package keytransform
import ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" import ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
// KeyTransform is a function that transforms one key into another. type Pair struct {
type KeyTransform func(ds.Key) ds.Key Convert KeyMapping
Invert KeyMapping
// Datastore is a keytransform.Datastore }
type Datastore interface {
ds.Shim
// Transform runs the transformation function func (t *Pair) ConvertKey(k ds.Key) ds.Key {
Transform(ds.Key) ds.Key return t.Convert(k)
}
// TransformFunc returns the KeyTransform function func (t *Pair) InvertKey(k ds.Key) ds.Key {
TransformFunc() KeyTransform return t.Invert(k)
} }
// ktds keeps a KeyTransform function // ktds keeps a KeyTransform function
type ktds struct { type ktds struct {
child ds.Datastore child ds.Datastore
xform KeyTransform
}
// WrapDatastore wraps a given datastore with a KeyTransform function.
// The resulting wrapped datastore will use the transform on all Datastore
// operations.
func WrapDatastore(child ds.Datastore, f KeyTransform) Datastore {
if f == nil {
panic("f (KeyTransform) is nil")
}
if child == nil {
panic("child (ds.Datastore) is nil")
}
return &ktds{child, f}
}
// TransformFunc returns the KeyTransform function
func (d *ktds) TransformFunc() KeyTransform {
return d.xform
}
// Transform runs the KeyTransform function KeyTransform
func (d *ktds) Transform(k ds.Key) ds.Key {
return d.xform(k)
} }
// Children implements ds.Shim // Children implements ds.Shim
...@@ -54,23 +29,23 @@ func (d *ktds) Children() []ds.Datastore { ...@@ -54,23 +29,23 @@ func (d *ktds) Children() []ds.Datastore {
// Put stores the given value, transforming the key first. // Put stores the given value, transforming the key first.
func (d *ktds) Put(key ds.Key, value interface{}) (err error) { func (d *ktds) Put(key ds.Key, value interface{}) (err error) {
return d.child.Put(d.Transform(key), value) return d.child.Put(d.ConvertKey(key), value)
} }
// Get returns the value for given key, transforming the key first. // Get returns the value for given key, transforming the key first.
func (d *ktds) Get(key ds.Key) (value interface{}, err error) { func (d *ktds) Get(key ds.Key) (value interface{}, err error) {
return d.child.Get(d.Transform(key)) return d.child.Get(d.ConvertKey(key))
} }
// Has returns whether the datastore has a value for a given key, transforming // Has returns whether the datastore has a value for a given key, transforming
// the key first. // the key first.
func (d *ktds) Has(key ds.Key) (exists bool, err error) { func (d *ktds) Has(key ds.Key) (exists bool, err error) {
return d.child.Has(d.Transform(key)) return d.child.Has(d.ConvertKey(key))
} }
// Delete removes the value for given key // Delete removes the value for given key
func (d *ktds) Delete(key ds.Key) (err error) { func (d *ktds) Delete(key ds.Key) (err error) {
return d.child.Delete(d.Transform(key)) return d.child.Delete(d.ConvertKey(key))
} }
// KeyList returns a list of all keys in the datastore, transforming keys out. // KeyList returns a list of all keys in the datastore, transforming keys out.
...@@ -82,7 +57,7 @@ func (d *ktds) KeyList() ([]ds.Key, error) { ...@@ -82,7 +57,7 @@ func (d *ktds) KeyList() ([]ds.Key, error) {
} }
for i, k := range keys { for i, k := range keys {
keys[i] = d.Transform(k) keys[i] = d.InvertKey(k)
} }
return keys, nil return keys, nil
} }
...@@ -2,6 +2,7 @@ package keytransform_test ...@@ -2,6 +2,7 @@ package keytransform_test
import ( import (
"bytes" "bytes"
"sort"
"testing" "testing"
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
...@@ -12,19 +13,28 @@ import ( ...@@ -12,19 +13,28 @@ import (
// Hook up gocheck into the "go test" runner. // Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) } func Test(t *testing.T) { TestingT(t) }
type DSSuite struct { type DSSuite struct{}
dir string
ds ds.Datastore
}
var _ = Suite(&DSSuite{}) var _ = Suite(&DSSuite{})
func (ks *DSSuite) TestBasic(c *C) { func (ks *DSSuite) TestBasic(c *C) {
pair := &kt.Pair{
Convert: func(k ds.Key) ds.Key {
return ds.NewKey("/abc").Child(k)
},
Invert: func(k ds.Key) ds.Key {
// remove abc prefix
l := k.List()
if l[0] != "abc" {
panic("key does not have prefix. convert failed?")
}
return ds.KeyWithNamespaces(l[1:])
},
}
mpds := ds.NewMapDatastore() mpds := ds.NewMapDatastore()
ktds := kt.WrapDatastore(mpds, func(k ds.Key) ds.Key { ktds := kt.Wrap(mpds, pair)
return k.Reverse()
})
keys := strsToKeys([]string{ keys := strsToKeys([]string{
"foo", "foo",
...@@ -45,10 +55,26 @@ func (ks *DSSuite) TestBasic(c *C) { ...@@ -45,10 +55,26 @@ func (ks *DSSuite) TestBasic(c *C) {
c.Check(err, Equals, nil) c.Check(err, Equals, nil)
c.Check(bytes.Equal(v1.([]byte), []byte(k.String())), Equals, true) c.Check(bytes.Equal(v1.([]byte), []byte(k.String())), Equals, true)
v2, err := mpds.Get(k.Reverse()) v2, err := mpds.Get(ds.NewKey("abc").Child(k))
c.Check(err, Equals, nil) c.Check(err, Equals, nil)
c.Check(bytes.Equal(v2.([]byte), []byte(k.String())), Equals, true) c.Check(bytes.Equal(v2.([]byte), []byte(k.String())), Equals, true)
} }
listA, errA := mpds.KeyList()
listB, errB := ktds.KeyList()
c.Check(errA, Equals, nil)
c.Check(errB, Equals, nil)
c.Check(len(listA), Equals, len(listB))
// sort them cause yeah.
sort.Sort(ds.KeySlice(listA))
sort.Sort(ds.KeySlice(listB))
for i, kA := range listA {
kB := listB[i]
c.Check(pair.Invert(kA), Equals, kB)
c.Check(kA, Equals, pair.Convert(kB))
}
} }
func strsToKeys(strs []string) []ds.Key { func strsToKeys(strs []string) []ds.Key {
......
// Package namespace introduces a namespace Datastore Shim, which basically
// mounts the entire child datastore under a prefix.
//
// Use the Wrap function to wrap a datastore with any Key prefix. For example:
//
// import (
// "fmt"
//
// ds "github.com/jbenet/datastore.go"
// nsds "github.com/jbenet/datastore.go/namespace"
// )
//
// func main() {
// mp := ds.NewMapDatastore()
// ns := nsds.Wrap(mp, ds.NewKey("/foo/bar"))
//
// // in the Namespace Datastore:
// ns.Put(ds.NewKey("/beep"), "boop")
// v2, _ := ns.Get(ds.NewKey("/beep")) // v2 == "boop"
//
// // and, in the underlying MapDatastore:
// v3, _ := mp.Get(ds.NewKey("/foo/bar/beep")) // v3 == "boop"
// }
package namespace
package namespace_test
import (
"fmt"
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/namespace"
)
func Example() {
mp := ds.NewMapDatastore()
ns := nsds.Wrap(mp, ds.NewKey("/foo/bar"))
k := ds.NewKey("/beep")
v := "boop"
ns.Put(k, v)
fmt.Printf("ns.Put %s %s\n", k, v)
v2, _ := ns.Get(k)
fmt.Printf("ns.Get %s -> %s\n", k, v2)
k3 := ds.NewKey("/foo/bar/beep")
v3, _ := mp.Get(k3)
fmt.Printf("mp.Get %s -> %s\n", k3, v3)
// Output:
// ns.Put /beep -> boop
// ns.Get /beep -> boop
// mp.Get /foo/bar/beep -> boop
}
package namespace
import (
"fmt"
"strings"
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
ktds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/keytransform"
)
// PrefixTransform constructs a KeyTransform with a pair of functions that
// add or remove the given prefix key.
//
// Warning: will panic if prefix not found when it should be there. This is
// to avoid insidious data inconsistency errors.
func PrefixTransform(prefix ds.Key) ktds.KeyTransform {
return &ktds.Pair{
// Convert adds the prefix
Convert: func(k ds.Key) ds.Key {
return prefix.Child(k)
},
// Invert removes the prefix. panics if prefix not found.
Invert: func(k ds.Key) ds.Key {
if !prefix.IsAncestorOf(k) {
fmt.Errorf("Expected prefix (%s) in key (%s)", prefix, k)
panic("expected prefix not found")
}
s := strings.TrimPrefix(k.String(), prefix.String())
return ds.NewKey(s)
},
}
}
// Wrap wraps a given datastore with a key-prefix.
func Wrap(child ds.Datastore, prefix ds.Key) ktds.Datastore {
if child == nil {
panic("child (ds.Datastore) is nil")
}
return ktds.Wrap(child, PrefixTransform(prefix))
}
package namespace_test
import (
"bytes"
"sort"
"testing"
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
ns "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go/namespace"
. "launchpad.net/gocheck"
)
// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) }
type DSSuite struct{}
var _ = Suite(&DSSuite{})
func (ks *DSSuite) TestBasic(c *C) {
mpds := ds.NewMapDatastore()
nsds := ns.Wrap(mpds, ds.NewKey("abc"))
keys := strsToKeys([]string{
"foo",
"foo/bar",
"foo/bar/baz",
"foo/barb",
"foo/bar/bazb",
"foo/bar/baz/barb",
})
for _, k := range keys {
err := nsds.Put(k, []byte(k.String()))
c.Check(err, Equals, nil)
}
for _, k := range keys {
v1, err := nsds.Get(k)
c.Check(err, Equals, nil)
c.Check(bytes.Equal(v1.([]byte), []byte(k.String())), Equals, true)
v2, err := mpds.Get(ds.NewKey("abc").Child(k))
c.Check(err, Equals, nil)
c.Check(bytes.Equal(v2.([]byte), []byte(k.String())), Equals, true)
}
listA, errA := mpds.KeyList()
listB, errB := nsds.KeyList()
c.Check(errA, Equals, nil)
c.Check(errB, Equals, nil)
c.Check(len(listA), Equals, len(listB))
// sort them cause yeah.
sort.Sort(ds.KeySlice(listA))
sort.Sort(ds.KeySlice(listB))
for i, kA := range listA {
kB := listB[i]
c.Check(nsds.InvertKey(kA), Equals, kB)
c.Check(kA, Equals, nsds.ConvertKey(kB))
}
}
func strsToKeys(strs []string) []ds.Key {
keys := make([]ds.Key, len(strs))
for i, s := range strs {
keys[i] = ds.NewKey(s)
}
return keys
}
package sync
import (
"fmt"
"os"
ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
)
type datastore struct {
child ds.Datastore
}
// Wrap shims a datastore such than _any_ operation failing triggers a panic
// This is useful for debugging invariants.
func Wrap(d ds.Datastore) ds.Shim {
return &datastore{child: d}
}
func (d *datastore) Children() []ds.Datastore {
return []ds.Datastore{d.child}
}
func (d *datastore) Put(key ds.Key, value interface{}) error {
err := d.child.Put(key, value)
if err != nil {
fmt.Fprintf(os.Stdout, "panic datastore: %s", err)
panic("panic datastore: Put failed")
}
return nil
}
func (d *datastore) Get(key ds.Key) (interface{}, error) {
val, err := d.child.Get(key)
if err != nil {
fmt.Fprintf(os.Stdout, "panic datastore: %s", err)
panic("panic datastore: Get failed")
}
return val, nil
}
func (d *datastore) Has(key ds.Key) (bool, error) {
e, err := d.child.Has(key)
if err != nil {
fmt.Fprintf(os.Stdout, "panic datastore: %s", err)
panic("panic datastore: Has failed")
}
return e, nil
}
func (d *datastore) Delete(key ds.Key) error {
err := d.child.Delete(key)
if err != nil {
fmt.Fprintf(os.Stdout, "panic datastore: %s", err)
panic("panic datastore: Delete failed")
}
return nil
}
func (d *datastore) KeyList() ([]ds.Key, error) {
kl, err := d.child.KeyList()
if err != nil {
fmt.Fprintf(os.Stdout, "panic datastore: %s", err)
panic("panic datastore: KeyList failed")
}
return kl, nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment