Commit fea3a0bb authored by Jeromy's avatar Jeromy

implement 'editor' abstraction

License: MIT
Signed-off-by: default avatarJeromy <jeromyj@gmail.com>
parent 00f5bdf1
......@@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error {
// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
n.encoded = nil
for i, l := range n.Links {
if l.Name == name {
n.Links = append(n.Links[:i], n.Links[i+1:]...)
return nil
good := make([]*Link, 0, len(n.Links))
var found bool
for _, l := range n.Links {
if l.Name != name {
good = append(good, l)
} else {
found = true
}
}
return ErrNotFound
n.Links = good
if !found {
return ErrNotFound
}
return nil
}
// Return a copy of the link with given name
......
......@@ -2,22 +2,44 @@ package dagutils
import (
"errors"
"time"
"strings"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
key "github.com/ipfs/go-ipfs/blocks/key"
dag "github.com/ipfs/go-ipfs/merkledag"
ft "github.com/ipfs/go-ipfs/unixfs"
)
func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
type Editor struct {
root *dag.Node
ds dag.DAGService
}
func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor {
return &Editor{
root: root,
ds: ds,
}
}
func (e *Editor) GetNode() *dag.Node {
return e.root.Copy()
}
func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error {
nd, err := addLink(ctx, e.ds, e.root, childname, childk)
if err != nil {
return err
}
e.root = nd
return nil
}
func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
if childname == "" {
return nil, errors.New("cannot create link with no name!")
}
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
childnd, err := ds.Get(ctx, childk)
if err != nil {
return nil, err
......@@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s
return root, nil
}
func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) {
func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error {
splpath := strings.Split(path, "/")
nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create)
if err != nil {
return err
}
e.root = nd
return nil
}
func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) {
if len(path) == 1 {
return AddLink(ctx, ds, root, path[0], toinsert)
return addLink(ctx, ds, root, path[0], toinsert)
}
nd, err := root.GetLinkedNode(ctx, ds, path[0])
if err != nil {
// if 'create' is true, we create directories on the way down as needed
if err == dag.ErrNotFound && create {
nd = &dag.Node{Data: ft.FolderPBData()}
if err == dag.ErrNotFound && create != nil {
nd = create()
} else {
return nil, err
}
}
ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
if err != nil {
return nil, err
}
......@@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
return root, nil
}
func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
func (e *Editor) RmLink(ctx context.Context, path string) error {
splpath := strings.Split(path, "/")
nd, err := rmLink(ctx, e.ds, e.root, splpath)
if err != nil {
return err
}
e.root = nd
return nil
}
func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
if len(path) == 1 {
// base case, remove node in question
err := root.RemoveNodeLink(path[0])
......@@ -93,7 +135,7 @@ func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
return nil, err
}
nnode, err := RmLink(ctx, ds, nd, path[1:])
nnode, err := rmLink(ctx, ds, nd, path[1:])
if err != nil {
return nil, err
}
......
package dagutils
import (
"strings"
"testing"
key "github.com/ipfs/go-ipfs/blocks/key"
......@@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) {
}
nd := new(dag.Node)
nnode, err := AddLink(context.Background(), ds, nd, "fish", fk)
nnode, err := addLink(context.Background(), ds, nd, "fish", fk)
if err != nil {
t.Fatal(err)
}
......@@ -42,9 +43,10 @@ func TestAddLink(t *testing.T) {
}
}
func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) {
func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) {
parts := strings.Split(path, "/")
cur := root
for _, e := range path {
for _, e := range parts {
nxt, err := cur.GetLinkedNode(context.Background(), ds, e)
if err != nil {
t.Fatal(err)
......@@ -66,53 +68,57 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []st
func TestInsertNode(t *testing.T) {
ds := mdtest.Mock(t)
root := new(dag.Node)
e := NewDagEditor(ds, root)
childa := &dag.Node{
Data: []byte("This is child A"),
}
ak, err := ds.Add(childa)
testInsert(t, e, "a", "anodefortesting", false, "")
testInsert(t, e, "a/b", "data", false, "")
testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found")
testInsert(t, e, "a/b/c/d/e", "foo", true, "")
testInsert(t, e, "a/b/c/d/f", "baz", true, "")
testInsert(t, e, "a/b/c/d/f", "bar", true, "")
testInsert(t, e, "", "bar", true, "cannot create link with no name!")
testInsert(t, e, "////", "slashes", true, "cannot create link with no name!")
k, err := e.GetNode().Key()
if err != nil {
t.Fatal(err)
}
path := []string{"a", "b", "c", "d"}
root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true)
if err != nil {
t.Fatal(err)
if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" {
t.Fatal("output was different than expected")
}
assertNodeAtPath(t, ds, root_a, path, ak)
}
childb := &dag.Node{Data: []byte("this is the second child")}
bk, err := ds.Add(childb)
func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) {
child := &dag.Node{Data: []byte(data)}
ck, err := e.ds.Add(child)
if err != nil {
t.Fatal(err)
}
// this one should fail, we are specifying a non-existant path
// with create == false
path2 := []string{"a", "b", "e", "f"}
_, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false)
if err == nil {
t.Fatal("that shouldnt have worked")
}
if err != dag.ErrNotFound {
t.Fatal("expected this to fail with 'not found'")
var c func() *dag.Node
if create {
c = func() *dag.Node {
return &dag.Node{}
}
}
// inserting a path of length one should work with create == false
path3 := []string{"x"}
root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false)
if err != nil {
t.Fatal(err)
err = e.InsertNodeAtPath(context.TODO(), path, ck, c)
if experr != "" {
var got string
if err != nil {
got = err.Error()
}
if got != experr {
t.Fatalf("expected '%s' but got '%s'", experr, got)
}
return
}
assertNodeAtPath(t, ds, root_b, path3, bk)
// now try overwriting a path
root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false)
if err != nil {
t.Fatal(err)
}
assertNodeAtPath(t, ds, root_c, path, bk)
assertNodeAtPath(t, e.ds, e.root, path, ck)
}
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