mirror of
https://github.com/ipfs/kubo.git
synced 2025-09-10 05:52:20 +08:00
rework editor creation and finalization
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
This commit is contained in:
@ -9,6 +9,8 @@ import (
|
||||
key "github.com/ipfs/go-ipfs/blocks/key"
|
||||
)
|
||||
|
||||
var ErrLinkNotFound = fmt.Errorf("no link by that name")
|
||||
|
||||
// Node represents a node in the IPFS Merkle DAG.
|
||||
// nodes have opaque data and a set of navigable links.
|
||||
type Node struct {
|
||||
@ -160,7 +162,7 @@ func (n *Node) GetNodeLink(name string) (*Link, error) {
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
return nil, ErrLinkNotFound
|
||||
}
|
||||
|
||||
func (n *Node) GetLinkedNode(ctx context.Context, ds DAGService, name string) (*Node, error) {
|
||||
|
@ -37,7 +37,7 @@ func (c *Change) String() string {
|
||||
}
|
||||
|
||||
func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Change) (*dag.Node, error) {
|
||||
e := NewDagEditor(ds, nd)
|
||||
e := NewDagEditor(nd, ds)
|
||||
for _, c := range cs {
|
||||
switch c.Type {
|
||||
case Add:
|
||||
@ -71,7 +71,8 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha
|
||||
}
|
||||
}
|
||||
}
|
||||
return e.GetNode(), nil
|
||||
|
||||
return e.Finalize(ds)
|
||||
}
|
||||
|
||||
func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change {
|
||||
|
@ -4,20 +4,41 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
|
||||
syncds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/sync"
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
|
||||
bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
|
||||
bserv "github.com/ipfs/go-ipfs/blockservice"
|
||||
offline "github.com/ipfs/go-ipfs/exchange/offline"
|
||||
dag "github.com/ipfs/go-ipfs/merkledag"
|
||||
)
|
||||
|
||||
type Editor struct {
|
||||
root *dag.Node
|
||||
ds dag.DAGService
|
||||
|
||||
// tmp is a temporary in memory (for now) dagstore for all of the
|
||||
// intermediary nodes to be stored in
|
||||
tmp dag.DAGService
|
||||
|
||||
// src is the dagstore with *all* of the data on it, it is used to pull
|
||||
// nodes from for modification (nil is a valid value)
|
||||
src dag.DAGService
|
||||
}
|
||||
|
||||
func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor {
|
||||
func NewMemoryDagService() dag.DAGService {
|
||||
// build mem-datastore for editor's intermediary nodes
|
||||
bs := bstore.NewBlockstore(syncds.MutexWrap(ds.NewMapDatastore()))
|
||||
bsrv := bserv.New(bs, offline.Exchange(bs))
|
||||
return dag.NewDAGService(bsrv)
|
||||
}
|
||||
|
||||
// root is the node to be modified, source is the dagstore to pull nodes from (optional)
|
||||
func NewDagEditor(root *dag.Node, source dag.DAGService) *Editor {
|
||||
return &Editor{
|
||||
root: root,
|
||||
ds: ds,
|
||||
tmp: NewMemoryDagService(),
|
||||
src: source,
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +47,7 @@ func (e *Editor) GetNode() *dag.Node {
|
||||
}
|
||||
|
||||
func (e *Editor) GetDagService() dag.DAGService {
|
||||
return e.ds
|
||||
return e.tmp
|
||||
}
|
||||
|
||||
func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childnd *dag.Node) (*dag.Node, error) {
|
||||
@ -57,7 +78,7 @@ func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s
|
||||
|
||||
func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *dag.Node, create func() *dag.Node) error {
|
||||
splpath := strings.Split(path, "/")
|
||||
nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create)
|
||||
nd, err := e.insertNodeAtPath(ctx, e.root, splpath, toinsert, create)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -65,27 +86,32 @@ func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert *da
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) {
|
||||
func (e *Editor) insertNodeAtPath(ctx context.Context, root *dag.Node, path []string, toinsert *dag.Node, create func() *dag.Node) (*dag.Node, error) {
|
||||
if len(path) == 1 {
|
||||
return addLink(ctx, ds, root, path[0], toinsert)
|
||||
return addLink(ctx, e.tmp, root, path[0], toinsert)
|
||||
}
|
||||
|
||||
nd, err := root.GetLinkedNode(ctx, ds, path[0])
|
||||
nd, err := root.GetLinkedNode(ctx, e.tmp, path[0])
|
||||
if err != nil {
|
||||
// if 'create' is true, we create directories on the way down as needed
|
||||
if err == dag.ErrNotFound && create != nil {
|
||||
if err == dag.ErrLinkNotFound && create != nil {
|
||||
nd = create()
|
||||
} else {
|
||||
err = nil // no longer an error case
|
||||
} else if err == dag.ErrNotFound {
|
||||
nd, err = root.GetLinkedNode(ctx, e.src, path[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
|
||||
ndprime, err := e.insertNodeAtPath(ctx, nd, path[1:], toinsert, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = ds.Remove(root)
|
||||
_ = e.tmp.Remove(root)
|
||||
|
||||
_ = root.RemoveNodeLink(path[0])
|
||||
err = root.AddNodeLinkClean(path[0], ndprime)
|
||||
@ -93,7 +119,7 @@ func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = ds.Add(root)
|
||||
_, err = e.tmp.Add(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -103,7 +129,7 @@ func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
|
||||
|
||||
func (e *Editor) RmLink(ctx context.Context, path string) error {
|
||||
splpath := strings.Split(path, "/")
|
||||
nd, err := rmLink(ctx, e.ds, e.root, splpath)
|
||||
nd, err := e.rmLink(ctx, e.root, splpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -111,7 +137,7 @@ func (e *Editor) RmLink(ctx context.Context, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
|
||||
func (e *Editor) rmLink(ctx context.Context, root *dag.Node, path []string) (*dag.Node, error) {
|
||||
if len(path) == 1 {
|
||||
// base case, remove node in question
|
||||
err := root.RemoveNodeLink(path[0])
|
||||
@ -119,7 +145,7 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = ds.Add(root)
|
||||
_, err = e.tmp.Add(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -127,17 +153,21 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
|
||||
return root, nil
|
||||
}
|
||||
|
||||
nd, err := root.GetLinkedNode(ctx, ds, path[0])
|
||||
nd, err := root.GetLinkedNode(ctx, e.tmp, path[0])
|
||||
if err == dag.ErrNotFound {
|
||||
nd, err = root.GetLinkedNode(ctx, e.src, path[0])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nnode, err := rmLink(ctx, ds, nd, path[1:])
|
||||
nnode, err := e.rmLink(ctx, nd, path[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = ds.Remove(root)
|
||||
_ = e.tmp.Remove(root)
|
||||
|
||||
_ = root.RemoveNodeLink(path[0])
|
||||
err = root.AddNodeLinkClean(path[0], nnode)
|
||||
@ -145,7 +175,7 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = ds.Add(root)
|
||||
_, err = e.tmp.Add(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -153,8 +183,10 @@ func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (e *Editor) WriteOutputTo(ds dag.DAGService) error {
|
||||
return copyDag(e.GetNode(), e.ds, ds)
|
||||
func (e *Editor) Finalize(ds dag.DAGService) (*dag.Node, error) {
|
||||
nd := e.GetNode()
|
||||
err := copyDag(nd, e.tmp, ds)
|
||||
return nd, err
|
||||
}
|
||||
|
||||
func copyDag(nd *dag.Node, from, to dag.DAGService) error {
|
||||
|
@ -66,13 +66,12 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path stri
|
||||
}
|
||||
|
||||
func TestInsertNode(t *testing.T) {
|
||||
ds := mdtest.Mock()
|
||||
root := new(dag.Node)
|
||||
e := NewDagEditor(ds, root)
|
||||
e := NewDagEditor(root, nil)
|
||||
|
||||
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", "blah", false, "no link by that name")
|
||||
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, "")
|
||||
@ -92,7 +91,7 @@ func TestInsertNode(t *testing.T) {
|
||||
|
||||
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)
|
||||
ck, err := e.tmp.Add(child)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -117,8 +116,8 @@ func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatal(err, path, data, create, experr)
|
||||
}
|
||||
|
||||
assertNodeAtPath(t, e.ds, e.root, path, ck)
|
||||
assertNodeAtPath(t, e.tmp, e.root, path, ck)
|
||||
}
|
||||
|
Reference in New Issue
Block a user