forked from rclone/rclone
-
Notifications
You must be signed in to change notification settings - Fork 3
/
object.go
315 lines (268 loc) · 8.24 KB
/
object.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
// Package object defines some useful Objects
package object
import (
"bytes"
"context"
"errors"
"io"
"time"
"github.com/artpar/rclone/fs"
"github.com/artpar/rclone/fs/hash"
)
// StaticObjectInfo is an ObjectInfo which can be constructed from scratch
type StaticObjectInfo struct {
remote string
modTime time.Time
size int64
storable bool
hashes map[hash.Type]string
fs fs.Info
meta fs.Metadata
mimeType string
}
// NewStaticObjectInfo returns a static ObjectInfo
// If hashes is nil and fs is not nil, the hash map will be replaced with
// empty hashes of the types supported by the fs.
func NewStaticObjectInfo(remote string, modTime time.Time, size int64, storable bool, hashes map[hash.Type]string, f fs.Info) *StaticObjectInfo {
info := &StaticObjectInfo{
remote: remote,
modTime: modTime,
size: size,
storable: storable,
hashes: hashes,
fs: f,
}
if f != nil && hashes == nil {
set := f.Hashes().Array()
info.hashes = make(map[hash.Type]string)
for _, ht := range set {
info.hashes[ht] = ""
}
}
return info
}
// WithMetadata adds meta to the ObjectInfo
func (i *StaticObjectInfo) WithMetadata(meta fs.Metadata) *StaticObjectInfo {
i.meta = meta
return i
}
// WithMimeType adds meta to the ObjectInfo
func (i *StaticObjectInfo) WithMimeType(mimeType string) *StaticObjectInfo {
i.mimeType = mimeType
return i
}
// Fs returns read only access to the Fs that this object is part of
func (i *StaticObjectInfo) Fs() fs.Info {
return i.fs
}
// Remote returns the remote path
func (i *StaticObjectInfo) Remote() string {
return i.remote
}
// String returns a description of the Object
func (i *StaticObjectInfo) String() string {
return i.remote
}
// ModTime returns the modification date of the file
func (i *StaticObjectInfo) ModTime(ctx context.Context) time.Time {
return i.modTime
}
// Size returns the size of the file
func (i *StaticObjectInfo) Size() int64 {
return i.size
}
// Storable says whether this object can be stored
func (i *StaticObjectInfo) Storable() bool {
return i.storable
}
// Hash returns the requested hash of the contents
func (i *StaticObjectInfo) Hash(ctx context.Context, h hash.Type) (string, error) {
if len(i.hashes) == 0 {
return "", hash.ErrUnsupported
}
if hash, ok := i.hashes[h]; ok {
return hash, nil
}
return "", hash.ErrUnsupported
}
// Metadata on the object
func (i *StaticObjectInfo) Metadata(ctx context.Context) (fs.Metadata, error) {
return i.meta, nil
}
// MimeType returns the content type of the Object if
// known, or "" if not
func (i *StaticObjectInfo) MimeType(ctx context.Context) string {
return i.mimeType
}
// Check interfaces
var (
_ fs.ObjectInfo = (*StaticObjectInfo)(nil)
_ fs.Metadataer = (*StaticObjectInfo)(nil)
_ fs.MimeTyper = (*StaticObjectInfo)(nil)
)
// MemoryFs is an in memory Fs, it only supports FsInfo and Put
var MemoryFs memoryFs
// memoryFs is an in memory fs
type memoryFs struct{}
// Name of the remote (as passed into NewFs)
func (memoryFs) Name() string { return "memory" }
// Root of the remote (as passed into NewFs)
func (memoryFs) Root() string { return "" }
// String returns a description of the FS
func (memoryFs) String() string { return "memory" }
// Precision of the ModTimes in this Fs
func (memoryFs) Precision() time.Duration { return time.Nanosecond }
// Returns the supported hash types of the filesystem
func (memoryFs) Hashes() hash.Set { return hash.Supported() }
// Features returns the optional features of this Fs
func (memoryFs) Features() *fs.Features { return &fs.Features{} }
// List the objects and directories in dir into entries. The
// entries can be returned in any order but should be for a
// complete directory.
//
// dir should be "" to list the root, and should not have
// trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
func (memoryFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
return nil, nil
}
// NewObject finds the Object at remote. If it can't be found
// it returns the error ErrorObjectNotFound.
func (memoryFs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
return nil, fs.ErrorObjectNotFound
}
// Put in to the remote path with the modTime given of the given size
//
// May create the object even if it returns an error - if so
// will return the object and the error, otherwise will return
// nil and the error
func (memoryFs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
o := NewMemoryObject(src.Remote(), src.ModTime(ctx), nil)
return o, o.Update(ctx, in, src, options...)
}
// Mkdir makes the directory (container, bucket)
//
// Shouldn't return an error if it already exists
func (memoryFs) Mkdir(ctx context.Context, dir string) error {
return errors.New("memoryFs: can't make directory")
}
// Rmdir removes the directory (container, bucket) if empty
//
// Return an error if it doesn't exist or isn't empty
func (memoryFs) Rmdir(ctx context.Context, dir string) error {
return fs.ErrorDirNotFound
}
var _ fs.Fs = MemoryFs
// MemoryObject is an in memory object
type MemoryObject struct {
remote string
modTime time.Time
content []byte
meta fs.Metadata
}
// NewMemoryObject returns an in memory Object with the modTime and content passed in
func NewMemoryObject(remote string, modTime time.Time, content []byte) *MemoryObject {
return &MemoryObject{
remote: remote,
modTime: modTime,
content: content,
}
}
// WithMetadata adds meta to the MemoryObject
func (o *MemoryObject) WithMetadata(meta fs.Metadata) *MemoryObject {
o.meta = meta
return o
}
// Content returns the underlying buffer
func (o *MemoryObject) Content() []byte {
return o.content
}
// Fs returns read only access to the Fs that this object is part of
func (o *MemoryObject) Fs() fs.Info {
return MemoryFs
}
// Remote returns the remote path
func (o *MemoryObject) Remote() string {
return o.remote
}
// String returns a description of the Object
func (o *MemoryObject) String() string {
return o.remote
}
// ModTime returns the modification date of the file
func (o *MemoryObject) ModTime(ctx context.Context) time.Time {
return o.modTime
}
// Size returns the size of the file
func (o *MemoryObject) Size() int64 {
return int64(len(o.content))
}
// Storable says whether this object can be stored
func (o *MemoryObject) Storable() bool {
return true
}
// Hash returns the requested hash of the contents
func (o *MemoryObject) Hash(ctx context.Context, h hash.Type) (string, error) {
hash, err := hash.NewMultiHasherTypes(hash.Set(h))
if err != nil {
return "", err
}
_, err = hash.Write(o.content)
if err != nil {
return "", err
}
return hash.Sums()[h], nil
}
// SetModTime sets the metadata on the object to set the modification date
func (o *MemoryObject) SetModTime(ctx context.Context, modTime time.Time) error {
o.modTime = modTime
return nil
}
// Open opens the file for read. Call Close() on the returned io.ReadCloser
func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
content := o.content
for _, option := range options {
switch x := option.(type) {
case *fs.RangeOption:
content = o.content[x.Start:x.End]
case *fs.SeekOption:
content = o.content[x.Offset:]
default:
if option.Mandatory() {
fs.Logf(o, "Unsupported mandatory option: %v", option)
}
}
}
return io.NopCloser(bytes.NewBuffer(content)), nil
}
// Update in to the object with the modTime given of the given size
//
// This re-uses the internal buffer if at all possible.
func (o *MemoryObject) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) {
size := src.Size()
if size == 0 {
o.content = nil
} else if size < 0 || int64(cap(o.content)) < size {
o.content, err = io.ReadAll(in)
} else {
o.content = o.content[:size]
_, err = io.ReadFull(in, o.content)
}
o.modTime = src.ModTime(ctx)
return err
}
// Remove this object
func (o *MemoryObject) Remove(ctx context.Context) error {
return errors.New("memoryObject.Remove not supported")
}
// Metadata on the object
func (o *MemoryObject) Metadata(ctx context.Context) (fs.Metadata, error) {
return o.meta, nil
}
// Check interfaces
var (
_ fs.Object = (*MemoryObject)(nil)
_ fs.Metadataer = (*MemoryObject)(nil)
)