github.com/artpar/rclone@v1.67.3/fs/features.go (about) 1 // Filesystem features and optional interfaces 2 3 package fs 4 5 import ( 6 "context" 7 "io" 8 "reflect" 9 "strings" 10 "time" 11 ) 12 13 // Features describe the optional features of the Fs 14 type Features struct { 15 // Feature flags, whether Fs 16 CaseInsensitive bool // has case insensitive files 17 DuplicateFiles bool // allows duplicate files 18 ReadMimeType bool // can read the mime type of objects 19 WriteMimeType bool // can set the mime type of objects 20 CanHaveEmptyDirectories bool // can have empty directories 21 BucketBased bool // is bucket based (like s3, swift, etc.) 22 BucketBasedRootOK bool // is bucket based and can use from root 23 SetTier bool // allows set tier functionality on objects 24 GetTier bool // allows to retrieve storage tier of objects 25 ServerSideAcrossConfigs bool // can server-side copy between different remotes of the same type 26 IsLocal bool // is the local backend 27 SlowModTime bool // if calling ModTime() generally takes an extra transaction 28 SlowHash bool // if calling Hash() generally takes an extra transaction 29 ReadMetadata bool // can read metadata from objects 30 WriteMetadata bool // can write metadata to objects 31 UserMetadata bool // can read/write general purpose metadata 32 ReadDirMetadata bool // can read metadata from directories (implements Directory.Metadata) 33 WriteDirMetadata bool // can write metadata to directories (implements Directory.SetMetadata) 34 WriteDirSetModTime bool // can write metadata to directories (implements Directory.SetModTime) 35 UserDirMetadata bool // can read/write general purpose metadata to/from directories 36 DirModTimeUpdatesOnWrite bool // indicate writing files to a directory updates its modtime 37 FilterAware bool // can make use of filters if provided for listing 38 PartialUploads bool // uploaded file can appear incomplete on the fs while it's being uploaded 39 NoMultiThreading bool // set if can't have multiplethreads on one download open 40 Overlay bool // this wraps one or more backends to add functionality 41 ChunkWriterDoesntSeek bool // set if the chunk writer doesn't need to read the data more than once 42 43 // Purge all files in the directory specified 44 // 45 // Implement this if you have a way of deleting all the files 46 // quicker than just running Remove() on the result of List() 47 // 48 // Return an error if it doesn't exist 49 Purge func(ctx context.Context, dir string) error 50 51 // Copy src to this remote using server-side copy operations. 52 // 53 // This is stored with the remote path given 54 // 55 // It returns the destination Object and a possible error 56 // 57 // Will only be called if src.Fs().Name() == f.Name() 58 // 59 // If it isn't possible then return fs.ErrorCantCopy 60 Copy func(ctx context.Context, src Object, remote string) (Object, error) 61 62 // Move src to this remote using server-side move operations. 63 // 64 // This is stored with the remote path given 65 // 66 // It returns the destination Object and a possible error 67 // 68 // Will only be called if src.Fs().Name() == f.Name() 69 // 70 // If it isn't possible then return fs.ErrorCantMove 71 Move func(ctx context.Context, src Object, remote string) (Object, error) 72 73 // DirMove moves src, srcRemote to this remote at dstRemote 74 // using server-side move operations. 75 // 76 // Will only be called if src.Fs().Name() == f.Name() 77 // 78 // If it isn't possible then return fs.ErrorCantDirMove 79 // 80 // If destination exists then return fs.ErrorDirExists 81 DirMove func(ctx context.Context, src Fs, srcRemote, dstRemote string) error 82 83 // MkdirMetadata makes the directory passed in as dir. 84 // 85 // It shouldn't return an error if it already exists. 86 // 87 // If the metadata is not nil it is set. 88 // 89 // It returns the directory that was created. 90 MkdirMetadata func(ctx context.Context, dir string, metadata Metadata) (Directory, error) 91 92 // ChangeNotify calls the passed function with a path 93 // that has had changes. If the implementation 94 // uses polling, it should adhere to the given interval. 95 ChangeNotify func(context.Context, func(string, EntryType), <-chan time.Duration) 96 97 // UnWrap returns the Fs that this Fs is wrapping 98 UnWrap func() Fs 99 100 // WrapFs returns the Fs that is wrapping this Fs 101 WrapFs func() Fs 102 103 // SetWrapper sets the Fs that is wrapping this Fs 104 SetWrapper func(f Fs) 105 106 // DirCacheFlush resets the directory cache - used in testing 107 // as an optional interface 108 DirCacheFlush func() 109 110 // PublicLink generates a public link to the remote path (usually readable by anyone) 111 PublicLink func(ctx context.Context, remote string, expire Duration, unlink bool) (string, error) 112 113 // Put in to the remote path with the modTime given of the given size 114 // 115 // May create the object even if it returns an error - if so 116 // will return the object and the error, otherwise will return 117 // nil and the error 118 // 119 // May create duplicates or return errors if src already 120 // exists. 121 PutUnchecked func(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error) 122 123 // PutStream uploads to the remote path with the modTime given of indeterminate size 124 // 125 // May create the object even if it returns an error - if so 126 // will return the object and the error, otherwise will return 127 // nil and the error 128 PutStream func(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error) 129 130 // MergeDirs merges the contents of all the directories passed 131 // in into the first one and rmdirs the other directories. 132 MergeDirs func(ctx context.Context, dirs []Directory) error 133 134 // DirSetModTime sets the metadata on the directory to set the modification date 135 DirSetModTime func(ctx context.Context, dir string, modTime time.Time) error 136 137 // CleanUp the trash in the Fs 138 // 139 // Implement this if you have a way of emptying the trash or 140 // otherwise cleaning up old versions of files. 141 CleanUp func(ctx context.Context) error 142 143 // ListR lists the objects and directories of the Fs starting 144 // from dir recursively into out. 145 // 146 // dir should be "" to start from the root, and should not 147 // have trailing slashes. 148 // 149 // This should return ErrDirNotFound if the directory isn't 150 // found. 151 // 152 // It should call callback for each tranche of entries read. 153 // These need not be returned in any particular order. If 154 // callback returns an error then the listing will stop 155 // immediately. 156 // 157 // Don't implement this unless you have a more efficient way 158 // of listing recursively that doing a directory traversal. 159 ListR ListRFn 160 161 // About gets quota information from the Fs 162 About func(ctx context.Context) (*Usage, error) 163 164 // OpenWriterAt opens with a handle for random access writes 165 // 166 // Pass in the remote desired and the size if known. 167 // 168 // It truncates any existing object 169 OpenWriterAt func(ctx context.Context, remote string, size int64) (WriterAtCloser, error) 170 171 // OpenChunkWriter returns the chunk size and a ChunkWriter 172 // 173 // Pass in the remote and the src object 174 // You can also use options to hint at the desired chunk size 175 // 176 OpenChunkWriter func(ctx context.Context, remote string, src ObjectInfo, options ...OpenOption) (info ChunkWriterInfo, writer ChunkWriter, err error) 177 178 // UserInfo returns info about the connected user 179 UserInfo func(ctx context.Context) (map[string]string, error) 180 181 // Disconnect the current user 182 Disconnect func(ctx context.Context) error 183 184 // Command the backend to run a named command 185 // 186 // The command run is name 187 // args may be used to read arguments from 188 // opts may be used to read optional arguments from 189 // 190 // The result should be capable of being JSON encoded 191 // If it is a string or a []string it will be shown to the user 192 // otherwise it will be JSON encoded and shown to the user like that 193 Command func(ctx context.Context, name string, arg []string, opt map[string]string) (interface{}, error) 194 195 // Shutdown the backend, closing any background tasks and any 196 // cached connections. 197 Shutdown func(ctx context.Context) error 198 } 199 200 // Disable nil's out the named feature. If it isn't found then it 201 // will log a message. 202 func (ft *Features) Disable(name string) *Features { 203 // Prefix boolean values with ! to set the feature 204 invert := false 205 if strings.HasPrefix(name, "!") { 206 name = name[1:] 207 invert = true 208 } 209 v := reflect.ValueOf(ft).Elem() 210 vType := v.Type() 211 for i := 0; i < v.NumField(); i++ { 212 vName := vType.Field(i).Name 213 field := v.Field(i) 214 if strings.EqualFold(name, vName) { 215 if !field.CanSet() { 216 Errorf(nil, "Can't set Feature %q", name) 217 } else { 218 if invert { 219 if field.Type().Kind() == reflect.Bool { 220 field.Set(reflect.ValueOf(true)) 221 Debugf(nil, "Set feature %q", name) 222 } else { 223 Errorf(nil, "Can't set Feature %q to true", name) 224 } 225 } else { 226 zero := reflect.Zero(field.Type()) 227 field.Set(zero) 228 Debugf(nil, "Reset feature %q", name) 229 } 230 } 231 } 232 } 233 return ft 234 } 235 236 // List returns a slice of all the possible feature names 237 func (ft *Features) List() (out []string) { 238 v := reflect.ValueOf(ft).Elem() 239 vType := v.Type() 240 for i := 0; i < v.NumField(); i++ { 241 out = append(out, vType.Field(i).Name) 242 } 243 return out 244 } 245 246 // Enabled returns a map of features with keys showing whether they 247 // are enabled or not 248 func (ft *Features) Enabled() (features map[string]bool) { 249 v := reflect.ValueOf(ft).Elem() 250 vType := v.Type() 251 features = make(map[string]bool, v.NumField()) 252 for i := 0; i < v.NumField(); i++ { 253 vName := vType.Field(i).Name 254 field := v.Field(i) 255 if field.Kind() == reflect.Func { 256 // Can't compare functions 257 features[vName] = !field.IsNil() 258 } else { 259 zero := reflect.Zero(field.Type()) 260 features[vName] = field.Interface() != zero.Interface() 261 } 262 } 263 return features 264 } 265 266 // DisableList nil's out the comma separated list of named features. 267 // If it isn't found then it will log a message. 268 func (ft *Features) DisableList(list []string) *Features { 269 for _, feature := range list { 270 ft.Disable(strings.TrimSpace(feature)) 271 } 272 return ft 273 } 274 275 // Fill fills in the function pointers in the Features struct from the 276 // optional interfaces. It returns the original updated Features 277 // struct passed in. 278 func (ft *Features) Fill(ctx context.Context, f Fs) *Features { 279 if do, ok := f.(Purger); ok { 280 ft.Purge = do.Purge 281 } 282 if do, ok := f.(Copier); ok { 283 ft.Copy = do.Copy 284 } 285 if do, ok := f.(Mover); ok { 286 ft.Move = do.Move 287 } 288 if do, ok := f.(DirMover); ok { 289 ft.DirMove = do.DirMove 290 } 291 if do, ok := f.(MkdirMetadataer); ok { 292 ft.MkdirMetadata = do.MkdirMetadata 293 } 294 if do, ok := f.(ChangeNotifier); ok { 295 ft.ChangeNotify = do.ChangeNotify 296 } 297 if do, ok := f.(UnWrapper); ok { 298 ft.UnWrap = do.UnWrap 299 } 300 if do, ok := f.(Wrapper); ok { 301 ft.WrapFs = do.WrapFs 302 ft.SetWrapper = do.SetWrapper 303 ft.Overlay = true // if implement UnWrap then must be an Overlay 304 } 305 if do, ok := f.(DirCacheFlusher); ok { 306 ft.DirCacheFlush = do.DirCacheFlush 307 } 308 if do, ok := f.(PublicLinker); ok { 309 ft.PublicLink = do.PublicLink 310 } 311 if do, ok := f.(PutUncheckeder); ok { 312 ft.PutUnchecked = do.PutUnchecked 313 } 314 if do, ok := f.(PutStreamer); ok { 315 ft.PutStream = do.PutStream 316 } 317 if do, ok := f.(MergeDirser); ok { 318 ft.MergeDirs = do.MergeDirs 319 } 320 if do, ok := f.(DirSetModTimer); ok { 321 ft.DirSetModTime = do.DirSetModTime 322 } 323 if do, ok := f.(CleanUpper); ok { 324 ft.CleanUp = do.CleanUp 325 } 326 if do, ok := f.(ListRer); ok { 327 ft.ListR = do.ListR 328 } 329 if do, ok := f.(Abouter); ok { 330 ft.About = do.About 331 } 332 if do, ok := f.(OpenWriterAter); ok { 333 ft.OpenWriterAt = do.OpenWriterAt 334 } 335 if do, ok := f.(OpenChunkWriter); ok { 336 ft.OpenChunkWriter = do.OpenChunkWriter 337 } 338 if do, ok := f.(UserInfoer); ok { 339 ft.UserInfo = do.UserInfo 340 } 341 if do, ok := f.(Disconnecter); ok { 342 ft.Disconnect = do.Disconnect 343 } 344 if do, ok := f.(Commander); ok { 345 ft.Command = do.Command 346 } 347 if do, ok := f.(Shutdowner); ok { 348 ft.Shutdown = do.Shutdown 349 } 350 return ft.DisableList(GetConfig(ctx).DisableFeatures) 351 } 352 353 // Mask the Features with the Fs passed in 354 // 355 // Only optional features which are implemented in both the original 356 // Fs AND the one passed in will be advertised. Any features which 357 // aren't in both will be set to false/nil, except for UnWrap/Wrap which 358 // will be left untouched. 359 func (ft *Features) Mask(ctx context.Context, f Fs) *Features { 360 mask := f.Features() 361 ft.CaseInsensitive = ft.CaseInsensitive && mask.CaseInsensitive 362 ft.DuplicateFiles = ft.DuplicateFiles && mask.DuplicateFiles 363 ft.ReadMimeType = ft.ReadMimeType && mask.ReadMimeType 364 ft.WriteMimeType = ft.WriteMimeType && mask.WriteMimeType 365 ft.ReadMetadata = ft.ReadMetadata && mask.ReadMetadata 366 ft.WriteMetadata = ft.WriteMetadata && mask.WriteMetadata 367 ft.UserMetadata = ft.UserMetadata && mask.UserMetadata 368 ft.ReadDirMetadata = ft.ReadDirMetadata && mask.ReadDirMetadata 369 ft.WriteDirMetadata = ft.WriteDirMetadata && mask.WriteDirMetadata 370 ft.WriteDirSetModTime = ft.WriteDirSetModTime && mask.WriteDirSetModTime 371 ft.UserDirMetadata = ft.UserDirMetadata && mask.UserDirMetadata 372 ft.DirModTimeUpdatesOnWrite = ft.DirModTimeUpdatesOnWrite && mask.DirModTimeUpdatesOnWrite 373 ft.CanHaveEmptyDirectories = ft.CanHaveEmptyDirectories && mask.CanHaveEmptyDirectories 374 ft.BucketBased = ft.BucketBased && mask.BucketBased 375 ft.BucketBasedRootOK = ft.BucketBasedRootOK && mask.BucketBasedRootOK 376 ft.SetTier = ft.SetTier && mask.SetTier 377 ft.GetTier = ft.GetTier && mask.GetTier 378 ft.ServerSideAcrossConfigs = ft.ServerSideAcrossConfigs && mask.ServerSideAcrossConfigs 379 // ft.IsLocal = ft.IsLocal && mask.IsLocal Don't propagate IsLocal 380 ft.SlowModTime = ft.SlowModTime && mask.SlowModTime 381 ft.SlowHash = ft.SlowHash && mask.SlowHash 382 ft.FilterAware = ft.FilterAware && mask.FilterAware 383 ft.PartialUploads = ft.PartialUploads && mask.PartialUploads 384 ft.NoMultiThreading = ft.NoMultiThreading && mask.NoMultiThreading 385 // ft.Overlay = ft.Overlay && mask.Overlay don't propagate Overlay 386 387 if mask.Purge == nil { 388 ft.Purge = nil 389 } 390 if mask.Copy == nil { 391 ft.Copy = nil 392 } 393 if mask.Move == nil { 394 ft.Move = nil 395 } 396 if mask.DirMove == nil { 397 ft.DirMove = nil 398 } 399 if mask.MkdirMetadata == nil { 400 ft.MkdirMetadata = nil 401 } 402 if mask.ChangeNotify == nil { 403 ft.ChangeNotify = nil 404 } 405 // if mask.UnWrap == nil { 406 // ft.UnWrap = nil 407 // } 408 // if mask.Wrapper == nil { 409 // ft.Wrapper = nil 410 // } 411 if mask.DirCacheFlush == nil { 412 ft.DirCacheFlush = nil 413 } 414 if mask.PublicLink == nil { 415 ft.PublicLink = nil 416 } 417 if mask.PutUnchecked == nil { 418 ft.PutUnchecked = nil 419 } 420 if mask.PutStream == nil { 421 ft.PutStream = nil 422 } 423 if mask.MergeDirs == nil { 424 ft.MergeDirs = nil 425 } 426 if mask.DirSetModTime == nil { 427 ft.DirSetModTime = nil 428 } 429 if mask.CleanUp == nil { 430 ft.CleanUp = nil 431 } 432 if mask.ListR == nil { 433 ft.ListR = nil 434 } 435 if mask.About == nil { 436 ft.About = nil 437 } 438 if mask.OpenWriterAt == nil { 439 ft.OpenWriterAt = nil 440 } 441 if mask.OpenChunkWriter == nil { 442 ft.OpenChunkWriter = nil 443 } 444 if mask.UserInfo == nil { 445 ft.UserInfo = nil 446 } 447 if mask.Disconnect == nil { 448 ft.Disconnect = nil 449 } 450 // Command is always local so we don't mask it 451 if mask.Shutdown == nil { 452 ft.Shutdown = nil 453 } 454 return ft.DisableList(GetConfig(ctx).DisableFeatures) 455 } 456 457 // Wrap makes a Copy of the features passed in, overriding the UnWrap/Wrap 458 // method only if available in f. 459 func (ft *Features) Wrap(f Fs) *Features { 460 ftCopy := new(Features) 461 *ftCopy = *ft 462 if do, ok := f.(UnWrapper); ok { 463 ftCopy.UnWrap = do.UnWrap 464 } 465 if do, ok := f.(Wrapper); ok { 466 ftCopy.WrapFs = do.WrapFs 467 ftCopy.SetWrapper = do.SetWrapper 468 } 469 return ftCopy 470 } 471 472 // WrapsFs adds extra information between `f` which wraps `w` 473 func (ft *Features) WrapsFs(f Fs, w Fs) *Features { 474 wFeatures := w.Features() 475 if wFeatures.WrapFs != nil && wFeatures.SetWrapper != nil { 476 wFeatures.SetWrapper(f) 477 } 478 return ft 479 } 480 481 // Purger is an optional interfaces for Fs 482 type Purger interface { 483 // Purge all files in the directory specified 484 // 485 // Implement this if you have a way of deleting all the files 486 // quicker than just running Remove() on the result of List() 487 // 488 // Return an error if it doesn't exist 489 Purge(ctx context.Context, dir string) error 490 } 491 492 // Copier is an optional interface for Fs 493 type Copier interface { 494 // Copy src to this remote using server-side copy operations. 495 // 496 // This is stored with the remote path given 497 // 498 // It returns the destination Object and a possible error 499 // 500 // Will only be called if src.Fs().Name() == f.Name() 501 // 502 // If it isn't possible then return fs.ErrorCantCopy 503 Copy(ctx context.Context, src Object, remote string) (Object, error) 504 } 505 506 // Mover is an optional interface for Fs 507 type Mover interface { 508 // Move src to this remote using server-side move operations. 509 // 510 // This is stored with the remote path given 511 // 512 // It returns the destination Object and a possible error 513 // 514 // Will only be called if src.Fs().Name() == f.Name() 515 // 516 // If it isn't possible then return fs.ErrorCantMove 517 Move(ctx context.Context, src Object, remote string) (Object, error) 518 } 519 520 // DirMover is an optional interface for Fs 521 type DirMover interface { 522 // DirMove moves src, srcRemote to this remote at dstRemote 523 // using server-side move operations. 524 // 525 // Will only be called if src.Fs().Name() == f.Name() 526 // 527 // If it isn't possible then return fs.ErrorCantDirMove 528 // 529 // If destination exists then return fs.ErrorDirExists 530 DirMove(ctx context.Context, src Fs, srcRemote, dstRemote string) error 531 } 532 533 // MkdirMetadataer is an optional interface for Fs 534 type MkdirMetadataer interface { 535 // MkdirMetadata makes the directory passed in as dir. 536 // 537 // It shouldn't return an error if it already exists. 538 // 539 // If the metadata is not nil it is set. 540 // 541 // It returns the directory that was created. 542 MkdirMetadata(ctx context.Context, dir string, metadata Metadata) (Directory, error) 543 } 544 545 // ChangeNotifier is an optional interface for Fs 546 type ChangeNotifier interface { 547 // ChangeNotify calls the passed function with a path 548 // that has had changes. If the implementation 549 // uses polling, it should adhere to the given interval. 550 // At least one value will be written to the channel, 551 // specifying the initial value and updated values might 552 // follow. A 0 Duration should pause the polling. 553 // The ChangeNotify implementation must empty the channel 554 // regularly. When the channel gets closed, the implementation 555 // should stop polling and release resources. 556 ChangeNotify(context.Context, func(string, EntryType), <-chan time.Duration) 557 } 558 559 // EntryType can be associated with remote paths to identify their type 560 type EntryType int 561 562 // Constants 563 const ( 564 // EntryDirectory should be used to classify remote paths in directories 565 EntryDirectory EntryType = iota // 0 566 // EntryObject should be used to classify remote paths in objects 567 EntryObject // 1 568 ) 569 570 // UnWrapper is an optional interfaces for Fs 571 type UnWrapper interface { 572 // UnWrap returns the Fs that this Fs is wrapping 573 UnWrap() Fs 574 } 575 576 // Wrapper is an optional interfaces for Fs 577 type Wrapper interface { 578 // Wrap returns the Fs that is wrapping this Fs 579 WrapFs() Fs 580 // SetWrapper sets the Fs that is wrapping this Fs 581 SetWrapper(f Fs) 582 } 583 584 // DirCacheFlusher is an optional interface for Fs 585 type DirCacheFlusher interface { 586 // DirCacheFlush resets the directory cache - used in testing 587 // as an optional interface 588 DirCacheFlush() 589 } 590 591 // PutUncheckeder is an optional interface for Fs 592 type PutUncheckeder interface { 593 // Put in to the remote path with the modTime given of the given size 594 // 595 // May create the object even if it returns an error - if so 596 // will return the object and the error, otherwise will return 597 // nil and the error 598 // 599 // May create duplicates or return errors if src already 600 // exists. 601 PutUnchecked(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error) 602 } 603 604 // PutStreamer is an optional interface for Fs 605 type PutStreamer interface { 606 // PutStream uploads to the remote path with the modTime given of indeterminate size 607 // 608 // May create the object even if it returns an error - if so 609 // will return the object and the error, otherwise will return 610 // nil and the error 611 PutStream(ctx context.Context, in io.Reader, src ObjectInfo, options ...OpenOption) (Object, error) 612 } 613 614 // PublicLinker is an optional interface for Fs 615 type PublicLinker interface { 616 // PublicLink generates a public link to the remote path (usually readable by anyone) 617 PublicLink(ctx context.Context, remote string, expire Duration, unlink bool) (string, error) 618 } 619 620 // MergeDirser is an option interface for Fs 621 type MergeDirser interface { 622 // MergeDirs merges the contents of all the directories passed 623 // in into the first one and rmdirs the other directories. 624 MergeDirs(ctx context.Context, dirs []Directory) error 625 } 626 627 // DirSetModTimer is an optional interface for Fs 628 type DirSetModTimer interface { 629 // DirSetModTime sets the metadata on the directory to set the modification date 630 DirSetModTime(ctx context.Context, dir string, modTime time.Time) error 631 } 632 633 // CleanUpper is an optional interfaces for Fs 634 type CleanUpper interface { 635 // CleanUp the trash in the Fs 636 // 637 // Implement this if you have a way of emptying the trash or 638 // otherwise cleaning up old versions of files. 639 CleanUp(ctx context.Context) error 640 } 641 642 // ListRer is an optional interfaces for Fs 643 type ListRer interface { 644 // ListR lists the objects and directories of the Fs starting 645 // from dir recursively into out. 646 // 647 // dir should be "" to start from the root, and should not 648 // have trailing slashes. 649 // 650 // This should return ErrDirNotFound if the directory isn't 651 // found. 652 // 653 // It should call callback for each tranche of entries read. 654 // These need not be returned in any particular order. If 655 // callback returns an error then the listing will stop 656 // immediately. 657 // 658 // Don't implement this unless you have a more efficient way 659 // of listing recursively that doing a directory traversal. 660 ListR(ctx context.Context, dir string, callback ListRCallback) error 661 } 662 663 // RangeSeeker is the interface that wraps the RangeSeek method. 664 // 665 // Some of the returns from Object.Open() may optionally implement 666 // this method for efficiency purposes. 667 type RangeSeeker interface { 668 // RangeSeek behaves like a call to Seek(offset int64, whence 669 // int) with the output wrapped in an io.LimitedReader 670 // limiting the total length to limit. 671 // 672 // RangeSeek with a limit of < 0 is equivalent to a regular Seek. 673 RangeSeek(ctx context.Context, offset int64, whence int, length int64) (int64, error) 674 } 675 676 // Abouter is an optional interface for Fs 677 type Abouter interface { 678 // About gets quota information from the Fs 679 About(ctx context.Context) (*Usage, error) 680 } 681 682 // OpenWriterAter is an optional interface for Fs 683 type OpenWriterAter interface { 684 // OpenWriterAt opens with a handle for random access writes 685 // 686 // Pass in the remote desired and the size if known. 687 // 688 // It truncates any existing object 689 OpenWriterAt(ctx context.Context, remote string, size int64) (WriterAtCloser, error) 690 } 691 692 // OpenWriterAtFn describes the OpenWriterAt function pointer 693 type OpenWriterAtFn func(ctx context.Context, remote string, size int64) (WriterAtCloser, error) 694 695 // ChunkWriterInfo describes how a backend would like ChunkWriter called 696 type ChunkWriterInfo struct { 697 ChunkSize int64 // preferred chunk size 698 Concurrency int // how many chunks to write at once 699 LeavePartsOnError bool // if set don't delete parts uploaded so far on error 700 } 701 702 // OpenChunkWriter is an option interface for Fs to implement chunked writing 703 type OpenChunkWriter interface { 704 // OpenChunkWriter returns the chunk size and a ChunkWriter 705 // 706 // Pass in the remote and the src object 707 // You can also use options to hint at the desired chunk size 708 OpenChunkWriter(ctx context.Context, remote string, src ObjectInfo, options ...OpenOption) (info ChunkWriterInfo, writer ChunkWriter, err error) 709 } 710 711 // OpenChunkWriterFn describes the OpenChunkWriter function pointer 712 type OpenChunkWriterFn func(ctx context.Context, remote string, src ObjectInfo, options ...OpenOption) (info ChunkWriterInfo, writer ChunkWriter, err error) 713 714 // ChunkWriter is returned by OpenChunkWriter to implement chunked writing 715 type ChunkWriter interface { 716 // WriteChunk will write chunk number with reader bytes, where chunk number >= 0 717 WriteChunk(ctx context.Context, chunkNumber int, reader io.ReadSeeker) (bytesWritten int64, err error) 718 719 // Close complete chunked writer finalising the file. 720 Close(ctx context.Context) error 721 722 // Abort chunk write 723 // 724 // You can and should call Abort without calling Close. 725 Abort(ctx context.Context) error 726 } 727 728 // UserInfoer is an optional interface for Fs 729 type UserInfoer interface { 730 // UserInfo returns info about the connected user 731 UserInfo(ctx context.Context) (map[string]string, error) 732 } 733 734 // Disconnecter is an optional interface for Fs 735 type Disconnecter interface { 736 // Disconnect the current user 737 Disconnect(ctx context.Context) error 738 } 739 740 // CommandHelp describes a single backend Command 741 // 742 // These are automatically inserted in the docs 743 type CommandHelp struct { 744 Name string // Name of the command, e.g. "link" 745 Short string // Single line description 746 Long string // Long multi-line description 747 Opts map[string]string // maps option name to a single line help 748 } 749 750 // Commander is an interface to wrap the Command function 751 type Commander interface { 752 // Command the backend to run a named command 753 // 754 // The command run is name 755 // args may be used to read arguments from 756 // opts may be used to read optional arguments from 757 // 758 // The result should be capable of being JSON encoded 759 // If it is a string or a []string it will be shown to the user 760 // otherwise it will be JSON encoded and shown to the user like that 761 Command(ctx context.Context, name string, arg []string, opt map[string]string) (interface{}, error) 762 } 763 764 // Shutdowner is an interface to wrap the Shutdown function 765 type Shutdowner interface { 766 // Shutdown the backend, closing any background tasks and any 767 // cached connections. 768 Shutdown(ctx context.Context) error 769 } 770 771 // ObjectsChan is a channel of Objects 772 type ObjectsChan chan Object 773 774 // Objects is a slice of Object~s 775 type Objects []Object 776 777 // ObjectPair is a pair of Objects used to describe a potential copy 778 // operation. 779 type ObjectPair struct { 780 Src, Dst Object 781 } 782 783 // UnWrapFs unwraps f as much as possible and returns the base Fs 784 func UnWrapFs(f Fs) Fs { 785 for { 786 unwrap := f.Features().UnWrap 787 if unwrap == nil { 788 break // not a wrapped Fs, use current 789 } 790 next := unwrap() 791 if next == nil { 792 break // no base Fs found, use current 793 } 794 f = next 795 } 796 return f 797 } 798 799 // UnWrapObject unwraps o as much as possible and returns the base object 800 func UnWrapObject(o Object) Object { 801 for { 802 u, ok := o.(ObjectUnWrapper) 803 if !ok { 804 break // not a wrapped object, use current 805 } 806 next := u.UnWrap() 807 if next == nil { 808 break // no base object found, use current 809 } 810 o = next 811 } 812 return o 813 } 814 815 // UnWrapObjectInfo returns the underlying Object unwrapped as much as 816 // possible or nil. 817 func UnWrapObjectInfo(oi ObjectInfo) Object { 818 o, ok := oi.(Object) 819 if !ok { 820 return nil 821 } 822 return UnWrapObject(o) 823 }