/
mount.go
116 lines (101 loc) · 2.82 KB
/
mount.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Package mount provides a Datastore that has other Datastores
// mounted at various key prefixes.
package mount
import (
"errors"
"strings"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/keytransform"
"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/query"
)
var (
ErrNoMount = errors.New("no datastore mounted for this key")
)
type Mount struct {
Prefix datastore.Key
Datastore datastore.Datastore
}
func New(mounts []Mount) *Datastore {
// make a copy so we're sure it doesn't mutate
m := make([]Mount, len(mounts))
for i, v := range mounts {
m[i] = v
}
return &Datastore{mounts: m}
}
type Datastore struct {
mounts []Mount
}
var _ datastore.Datastore = (*Datastore)(nil)
func (d *Datastore) lookup(key datastore.Key) (ds datastore.Datastore, mountpoint, rest datastore.Key) {
for _, m := range d.mounts {
if m.Prefix.Equal(key) || m.Prefix.IsAncestorOf(key) {
s := strings.TrimPrefix(key.String(), m.Prefix.String())
k := datastore.NewKey(s)
return m.Datastore, m.Prefix, k
}
}
return nil, datastore.NewKey("/"), key
}
func (d *Datastore) Put(key datastore.Key, value interface{}) error {
ds, _, k := d.lookup(key)
if ds == nil {
return ErrNoMount
}
return ds.Put(k, value)
}
func (d *Datastore) Get(key datastore.Key) (value interface{}, err error) {
ds, _, k := d.lookup(key)
if ds == nil {
return nil, datastore.ErrNotFound
}
return ds.Get(k)
}
func (d *Datastore) Has(key datastore.Key) (exists bool, err error) {
ds, _, k := d.lookup(key)
if ds == nil {
return false, nil
}
return ds.Has(k)
}
func (d *Datastore) Delete(key datastore.Key) error {
ds, _, k := d.lookup(key)
if ds == nil {
return datastore.ErrNotFound
}
return ds.Delete(k)
}
func (d *Datastore) Query(q query.Query) (query.Results, error) {
if len(q.Filters) > 0 ||
len(q.Orders) > 0 ||
q.Limit > 0 ||
q.Offset > 0 {
// TODO this is overly simplistic, but the only caller is
// `ipfs refs local` for now, and this gets us moving.
return nil, errors.New("mount only supports listing all prefixed keys in random order")
}
key := datastore.NewKey(q.Prefix)
ds, mount, k := d.lookup(key)
if ds == nil {
return nil, errors.New("mount only supports listing a mount point")
}
// TODO support listing cross mount points too
// delegate the query to the mounted datastore, while adjusting
// keys in and out
q2 := q
q2.Prefix = k.String()
wrapDS := keytransform.Wrap(ds, &keytransform.Pair{
Convert: func(datastore.Key) datastore.Key {
panic("this should never be called")
},
Invert: func(k datastore.Key) datastore.Key {
return mount.Child(k)
},
})
r, err := wrapDS.Query(q2)
if err != nil {
return nil, err
}
r = query.ResultsReplaceQuery(r, q)
return r, nil
}