implement 'editor' abstraction
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Jul 28, 2015 at 14:12 UTC
b32d0ef15325a44861ec58cadf3e8d3286a32815
4 files changed
+127
-55
core/commands/object.go
+19
-5
@@ -587,13 +587,17 @@ func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
587
return "", err
588
}
589
590
- path := strings.Split(req.Arguments()[2], "/")
590
+ path := req.Arguments()[2]
591
+
592
+ e := dagutils.NewDagEditor(nd.DAG, root)
593
592
- nnode, err := dagutils.RmLink(req.Context(), nd.DAG, root, path)
594
+ err = e.RmLink(req.Context(), path)
595
if err != nil {
596
return "", err
597
}
598
599
+ nnode := e.GetNode()
600
+
601
return nnode.Key()
602
}
603
@@ -610,17 +614,27 @@ func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
614
path := req.Arguments()[2]
615
childk := key.B58KeyDecode(req.Arguments()[3])
616
613
- parts := strings.Split(path, "/")
614
-
617
create, _, err := req.Option("create").Bool()
618
if err != nil {
619
return "", err
620
}
621
620
- nnode, err := dagutils.InsertNodeAtPath(req.Context(), nd.DAG, root, parts, childk, create)
622
+ var createfunc func() *dag.Node
623
+ if create {
624
+ createfunc = func() *dag.Node {
625
+ return &dag.Node{Data: ft.FolderPBData()}
626
+ }
627
+ }
628
+
629
+ e := dagutils.NewDagEditor(nd.DAG, root)
630
+
631
+ err = e.InsertNodeAtPath(req.Context(), path, childk, createfunc)
632
if err != nil {
633
return "", err
634
}
635
+
636
+ nnode := e.GetNode()
637
+
638
return nnode.Key()
639
}
640
merkledag/node.go
+15
-5
@@ -129,13 +129,23 @@ func (n *Node) AddRawLink(name string, l *Link) error {
129
// Remove a link on this node by the given name
130
func (n *Node) RemoveNodeLink(name string) error {
131
n.encoded = nil
132
- for i, l := range n.Links {
133
- if l.Name == name {
134
- n.Links = append(n.Links[:i], n.Links[i+1:]...)
135
- return nil
132
+ good := make([]*Link, 0, len(n.Links))
133
+ var found bool
134
+
135
+ for _, l := range n.Links {
136
+ if l.Name != name {
137
+ good = append(good, l)
138
+ } else {
139
+ found = true
140
}
141
}
138
- return ErrNotFound
142
+ n.Links = good
143
+
144
+ if !found {
145
+ return ErrNotFound
146
+ }
147
+
148
+ return nil
149
}
150
151
// Return a copy of the link with given name
merkledag/utils/utils.go
+54
-12
@@ -2,22 +2,44 @@ package dagutils
2
3
import (
4
"errors"
5
- "time"
5
+ "strings"
6
7
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
8
9
key "github.com/ipfs/go-ipfs/blocks/key"
10
dag "github.com/ipfs/go-ipfs/merkledag"
11
- ft "github.com/ipfs/go-ipfs/unixfs"
11
)
12
14
-func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
13
+type Editor struct {
14
+ root *dag.Node
15
+ ds dag.DAGService
16
+}
17
+
18
+func NewDagEditor(ds dag.DAGService, root *dag.Node) *Editor {
19
+ return &Editor{
20
+ root: root,
21
+ ds: ds,
22
+ }
23
+}
24
+
25
+func (e *Editor) GetNode() *dag.Node {
26
+ return e.root.Copy()
27
+}
28
+
29
+func (e *Editor) AddLink(ctx context.Context, childname string, childk key.Key) error {
30
+ nd, err := addLink(ctx, e.ds, e.root, childname, childk)
31
+ if err != nil {
32
+ return err
33
+ }
34
+ e.root = nd
35
+ return nil
36
+}
37
+
38
+func addLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname string, childk key.Key) (*dag.Node, error) {
39
if childname == "" {
40
return nil, errors.New("cannot create link with no name!")
41
}
42
19
- ctx, cancel := context.WithTimeout(ctx, time.Second*30)
20
- defer cancel()
43
childnd, err := ds.Get(ctx, childk)
44
if err != nil {
45
return nil, err
@@ -38,22 +60,32 @@ func AddLink(ctx context.Context, ds dag.DAGService, root *dag.Node, childname s
60
return root, nil
61
}
62
41
-func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create bool) (*dag.Node, error) {
63
+func (e *Editor) InsertNodeAtPath(ctx context.Context, path string, toinsert key.Key, create func() *dag.Node) error {
64
+ splpath := strings.Split(path, "/")
65
+ nd, err := insertNodeAtPath(ctx, e.ds, e.root, splpath, toinsert, create)
66
+ if err != nil {
67
+ return err
68
+ }
69
+ e.root = nd
70
+ return nil
71
+}
72
+
73
+func insertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string, toinsert key.Key, create func() *dag.Node) (*dag.Node, error) {
74
if len(path) == 1 {
43
- return AddLink(ctx, ds, root, path[0], toinsert)
75
+ return addLink(ctx, ds, root, path[0], toinsert)
76
}
77
78
nd, err := root.GetLinkedNode(ctx, ds, path[0])
79
if err != nil {
80
// if 'create' is true, we create directories on the way down as needed
49
- if err == dag.ErrNotFound && create {
50
- nd = &dag.Node{Data: ft.FolderPBData()}
81
+ if err == dag.ErrNotFound && create != nil {
82
+ nd = create()
83
} else {
84
return nil, err
85
}
86
}
87
56
- ndprime, err := InsertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
88
+ ndprime, err := insertNodeAtPath(ctx, ds, nd, path[1:], toinsert, create)
89
if err != nil {
90
return nil, err
91
}
@@ -72,7 +104,17 @@ func InsertNodeAtPath(ctx context.Context, ds dag.DAGService, root *dag.Node, pa
104
return root, nil
105
}
106
75
-func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
107
+func (e *Editor) RmLink(ctx context.Context, path string) error {
108
+ splpath := strings.Split(path, "/")
109
+ nd, err := rmLink(ctx, e.ds, e.root, splpath)
110
+ if err != nil {
111
+ return err
112
+ }
113
+ e.root = nd
114
+ return nil
115
+}
116
+
117
+func rmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []string) (*dag.Node, error) {
118
if len(path) == 1 {
119
// base case, remove node in question
120
err := root.RemoveNodeLink(path[0])
@@ -93,7 +135,7 @@ func RmLink(ctx context.Context, ds dag.DAGService, root *dag.Node, path []strin
135
return nil, err
136
}
137
96
- nnode, err := RmLink(ctx, ds, nd, path[1:])
138
+ nnode, err := rmLink(ctx, ds, nd, path[1:])
139
if err != nil {
140
return nil, err
141
}
merkledag/utils/utils_test.go
+39
-33
@@ -1,6 +1,7 @@
1
package dagutils
2
3
import (
4
+ "strings"
5
"testing"
6
7
key "github.com/ipfs/go-ipfs/blocks/key"
@@ -22,7 +23,7 @@ func TestAddLink(t *testing.T) {
23
}
24
25
nd := new(dag.Node)
25
- nnode, err := AddLink(context.Background(), ds, nd, "fish", fk)
26
+ nnode, err := addLink(context.Background(), ds, nd, "fish", fk)
27
if err != nil {
28
t.Fatal(err)
29
}
@@ -42,9 +43,10 @@ func TestAddLink(t *testing.T) {
43
}
44
}
45
45
-func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []string, exp key.Key) {
46
+func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path string, exp key.Key) {
47
+ parts := strings.Split(path, "/")
48
cur := root
47
- for _, e := range path {
49
+ for _, e := range parts {
50
nxt, err := cur.GetLinkedNode(context.Background(), ds, e)
51
if err != nil {
52
t.Fatal(err)
@@ -66,53 +68,57 @@ func assertNodeAtPath(t *testing.T, ds dag.DAGService, root *dag.Node, path []st
68
func TestInsertNode(t *testing.T) {
69
ds := mdtest.Mock(t)
70
root := new(dag.Node)
71
+ e := NewDagEditor(ds, root)
72
70
- childa := &dag.Node{
71
- Data: []byte("This is child A"),
72
- }
73
- ak, err := ds.Add(childa)
73
+ testInsert(t, e, "a", "anodefortesting", false, "")
74
+ testInsert(t, e, "a/b", "data", false, "")
75
+ testInsert(t, e, "a/b/c/d/e", "blah", false, "merkledag: not found")
76
+ testInsert(t, e, "a/b/c/d/e", "foo", true, "")
77
+ testInsert(t, e, "a/b/c/d/f", "baz", true, "")
78
+ testInsert(t, e, "a/b/c/d/f", "bar", true, "")
79
+
80
+ testInsert(t, e, "", "bar", true, "cannot create link with no name!")
81
+ testInsert(t, e, "////", "slashes", true, "cannot create link with no name!")
82
+
83
+ k, err := e.GetNode().Key()
84
if err != nil {
85
t.Fatal(err)
86
}
87
78
- path := []string{"a", "b", "c", "d"}
79
- root_a, err := InsertNodeAtPath(context.Background(), ds, root, path, ak, true)
80
- if err != nil {
81
- t.Fatal(err)
88
+ if k.B58String() != "QmThorWojP6YzLJwDukxiYCoKQSwyrMCvdt4WZ6rPm221t" {
89
+ t.Fatal("output was different than expected")
90
}
83
- assertNodeAtPath(t, ds, root_a, path, ak)
91
+}
92
85
- childb := &dag.Node{Data: []byte("this is the second child")}
86
- bk, err := ds.Add(childb)
93
+func testInsert(t *testing.T, e *Editor, path, data string, create bool, experr string) {
94
+ child := &dag.Node{Data: []byte(data)}
95
+ ck, err := e.ds.Add(child)
96
if err != nil {
97
t.Fatal(err)
98
}
99
91
- // this one should fail, we are specifying a non-existant path
92
- // with create == false
93
- path2 := []string{"a", "b", "e", "f"}
94
- _, err = InsertNodeAtPath(context.Background(), ds, root_a, path2, bk, false)
95
- if err == nil {
96
- t.Fatal("that shouldnt have worked")
97
- }
98
- if err != dag.ErrNotFound {
99
- t.Fatal("expected this to fail with 'not found'")
100
+ var c func() *dag.Node
101
+ if create {
102
+ c = func() *dag.Node {
103
+ return &dag.Node{}
104
+ }
105
}
106
102
- // inserting a path of length one should work with create == false
103
- path3 := []string{"x"}
104
- root_b, err := InsertNodeAtPath(context.Background(), ds, root_a, path3, bk, false)
105
- if err != nil {
106
- t.Fatal(err)
107
+ err = e.InsertNodeAtPath(context.TODO(), path, ck, c)
108
+ if experr != "" {
109
+ var got string
110
+ if err != nil {
111
+ got = err.Error()
112
+ }
113
+ if got != experr {
114
+ t.Fatalf("expected '%s' but got '%s'", experr, got)
115
+ }
116
+ return
117
}
118
109
- assertNodeAtPath(t, ds, root_b, path3, bk)
110
-
111
- // now try overwriting a path
112
- root_c, err := InsertNodeAtPath(context.Background(), ds, root_b, path, bk, false)
119
if err != nil {
120
t.Fatal(err)
121
}
122
117
- assertNodeAtPath(t, ds, root_c, path, bk)
123
+ assertNodeAtPath(t, e.ds, e.root, path, ck)
124
}