1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-19 18:05:32 +08:00

implement 'editor' abstraction

License: MIT
Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
Jeromy
2015-07-28 14:12:01 -07:00
parent 314f7bbfea
commit b32d0ef153
4 changed files with 140 additions and 68 deletions

View File

@ -587,13 +587,17 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
return "", err return "", err
} }
path := strings.Split(req.Arguments()[2], "/") path := req.Arguments()[2]
nnode, err := dagutils.RmLink(req.Context(), nd.DAG, root, path) e := dagutils.NewDagEditor(nd.DAG, root)
err = e.RmLink(req.Context(), path)
if err != nil { if err != nil {
return "", err return "", err
} }
nnode := e.GetNode()
return nnode.Key() return nnode.Key()
} }
@ -610,17 +614,27 @@ func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
path := req.Arguments()[2] path := req.Arguments()[2]
childk := key.B58KeyDecode(req.Arguments()[3]) childk := key.B58KeyDecode(req.Arguments()[3])
parts := strings.Split(path, "/")
create, _, err := req.Option("create").Bool() create, _, err := req.Option("create").Bool()
if err != nil { if err != nil {
return "", err return "", err
} }
nnode, err := dagutils.InsertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create) var createfunc func() *dag.Node
if create {
createfunc = func() *dag.Node {
return &dag.Node{Data: ft.FolderPBData()}
}
}
e := dagutils.NewDagEditor(nd.DAG, root)
err = e.InsertNodeAtPath(req.Context(), path, childk, createfunc)
if err != nil { if err != nil {
return "", err return "", err
} }
nnode := e.GetNode()
return nnode.Key() return nnode.Key()
} }

View File

@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error {
// Remove a link on this node by the given name // Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error { func (n *Node) RemoveNodeLink(name string) error {
n.encoded = nil n.encoded = nil
for i, l := range n.Links { good := make([]*Link, 0, len(n.Links))
if l.Name == name { var found bool
n.Links = append(n.Links[:i], n.Links[i+1:]...)
return nil for _, l := range n.Links {
if l.Name != name {
good = append(good, l)
} else {
found = true
} }
} }
n.Links = good
if !found {
return ErrNotFound return ErrNotFound
}
return nil
} }
// Return a copy of the link with given name // Return a copy of the link with given name

View File

@ -2,22 +2,44 @@ package dagutils
import ( import (
"errors" "errors"
"time" "strings"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
key "github.com/ipfs/go-ipfs/blocks/key" key "github.com/ipfs/go-ipfs/blocks/key"
dag "github.com/ipfs/go-ipfs/merkledag" 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 == "" { if childname == "" {
return nil, errors.New("cannot create link with no name!") 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) childnd, err := ds.Get(ctx, childk)
if err != nil { if err != nil {
return nil, err return nil, err
@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s
return root, nil 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 { 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]) nd, err := root.GetLinkedNode(ctx, ds, path[0])
if err != nil { if err != nil {
// if 'create' is true, we create directories on the way down as needed // if 'create' is true, we create directories on the way down as needed
if err == dag.ErrNotFound && create { if err == dag.ErrNotFound && create != nil {
nd = &dag.Node{Data: ft.FolderPBData()} nd = create()
} else { } else {
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
return root, nil 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 { if len(path) == 1 {
// base case, remove node in question // base case, remove node in question
err := root.RemoveNodeLink(path[0]) 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 return nil, err
} }
nnode, err := RmLink(ctx, ds, nd, path[1:]) nnode, err := rmLink(ctx, ds, nd, path[1:])
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,6 +1,7 @@
package dagutils package dagutils
import ( import (
"strings"
"testing" "testing"
key "github.com/ipfs/go-ipfs/blocks/key" key "github.com/ipfs/go-ipfs/blocks/key"
@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) {
} }
nd := new(dag.Node) 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 { if err != nil {
t.Fatal(err) 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 cur := root
for _, e := range path { for _, e := range parts {
nxt, err := cur.GetLinkedNode(context.Background(), ds, e) nxt, err := cur.GetLinkedNode(context.Background(), ds, e)
if err != nil { if err != nil {
t.Fatal(err) 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) { func TestInsertNode(t *testing.T) {
ds := mdtest.Mock(t) ds := mdtest.Mock(t)
root := new(dag.Node) root := new(dag.Node)
e := NewDagEditor(ds, root)
childa := &dag.Node{ testInsert(t, e, "a", "anodefortesting", false, "")
Data: []byte("This is child A"), testInsert(t, e, "a/b", "data", false, "")
} testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found")
ak, err := ds.Add(childa) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
path := []string{"a", "b", "c", "d"} if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" {
root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true) t.Fatal("output was different than expected")
if err != nil {
t.Fatal(err)
} }
assertNodeAtPath(t, ds, root_a, path, ak) }
childb := &dag.Node{Data: []byte("this is the second child")} func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) {
bk, err := ds.Add(childb) child := &dag.Node{Data: []byte(data)}
if err != nil { ck, err := e.ds.Add(child)
t.Fatal(err) if err != nil {
} t.Fatal(err)
}
// this one should fail, we are specifying a non-existant path
// with create == false var c func() *dag.Node
path2 := []string{"a", "b", "e", "f"} if create {
_, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false) c = func() *dag.Node {
if err == nil { return &dag.Node{}
t.Fatal("that shouldnt have worked") }
} }
if err != dag.ErrNotFound {
t.Fatal("expected this to fail with 'not found'") err = e.InsertNodeAtPath(context.TODO(), path, ck, c)
} if experr != "" {
var got string
// inserting a path of length one should work with create == false if err != nil {
path3 := []string{"x"} got = err.Error()
root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false) }
if err != nil { if got != experr {
t.Fatal(err) t.Fatalf("expected '%s' but got '%s'", experr, got)
} }
return
assertNodeAtPath(t, ds, root_b, path3, bk) }
// now try overwriting a path if err != nil {
root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false) t.Fatal(err)
if err != nil { }
t.Fatal(err)
} assertNodeAtPath(t, e.ds, e.root, path, ck)
assertNodeAtPath(t, ds, root_c, path, bk)
} }