mirror of
https://github.com/rclone/rclone.git
synced 2024-11-29 12:04:11 +08:00
e43b5ce5e5
This is possible now that we no longer support go1.12 and brings rclone into line with standard practices in the Go world. This also removes errors.New and errors.Errorf from lib/errors and prefers the stdlib errors package over lib/errors.
277 lines
7.3 KiB
Go
277 lines
7.3 KiB
Go
// Filesystem registry and backend options
|
|
|
|
package fs
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/rclone/rclone/fs/config/configmap"
|
|
"github.com/rclone/rclone/fs/config/configstruct"
|
|
)
|
|
|
|
// Registry of filesystems
|
|
var Registry []*RegInfo
|
|
|
|
// RegInfo provides information about a filesystem
|
|
type RegInfo struct {
|
|
// Name of this fs
|
|
Name string
|
|
// Description of this fs - defaults to Name
|
|
Description string
|
|
// Prefix for command line flags for this fs - defaults to Name if not set
|
|
Prefix string
|
|
// Create a new file system. If root refers to an existing
|
|
// object, then it should return an Fs which which points to
|
|
// the parent of that object and ErrorIsFile.
|
|
NewFs func(ctx context.Context, name string, root string, config configmap.Mapper) (Fs, error) `json:"-"`
|
|
// Function to call to help with config - see docs for ConfigIn for more info
|
|
Config func(ctx context.Context, name string, m configmap.Mapper, configIn ConfigIn) (*ConfigOut, error) `json:"-"`
|
|
// Options for the Fs configuration
|
|
Options Options
|
|
// The command help, if any
|
|
CommandHelp []CommandHelp
|
|
}
|
|
|
|
// FileName returns the on disk file name for this backend
|
|
func (ri *RegInfo) FileName() string {
|
|
return strings.Replace(ri.Name, " ", "", -1)
|
|
}
|
|
|
|
// Options is a slice of configuration Option for a backend
|
|
type Options []Option
|
|
|
|
// Set the default values for the options
|
|
func (os Options) setValues() {
|
|
for i := range os {
|
|
o := &os[i]
|
|
if o.Default == nil {
|
|
o.Default = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the Option corresponding to name or return nil if not found
|
|
func (os Options) Get(name string) *Option {
|
|
for i := range os {
|
|
opt := &os[i]
|
|
if opt.Name == name {
|
|
return opt
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Overridden discovers which config items have been overridden in the
|
|
// configmap passed in, either by the config string, command line
|
|
// flags or environment variables
|
|
func (os Options) Overridden(m *configmap.Map) configmap.Simple {
|
|
var overridden = configmap.Simple{}
|
|
for i := range os {
|
|
opt := &os[i]
|
|
value, isSet := m.GetPriority(opt.Name, configmap.PriorityNormal)
|
|
if isSet {
|
|
overridden.Set(opt.Name, value)
|
|
}
|
|
}
|
|
return overridden
|
|
}
|
|
|
|
// NonDefault discovers which config values aren't at their default
|
|
func (os Options) NonDefault(m configmap.Getter) configmap.Simple {
|
|
var nonDefault = configmap.Simple{}
|
|
for i := range os {
|
|
opt := &os[i]
|
|
value, isSet := m.Get(opt.Name)
|
|
if !isSet {
|
|
continue
|
|
}
|
|
defaultValue := fmt.Sprint(opt.Default)
|
|
if value != defaultValue {
|
|
nonDefault.Set(opt.Name, value)
|
|
}
|
|
}
|
|
return nonDefault
|
|
}
|
|
|
|
// HasAdvanced discovers if any options have an Advanced setting
|
|
func (os Options) HasAdvanced() bool {
|
|
for i := range os {
|
|
opt := &os[i]
|
|
if opt.Advanced {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// OptionVisibility controls whether the options are visible in the
|
|
// configurator or the command line.
|
|
type OptionVisibility byte
|
|
|
|
// Constants Option.Hide
|
|
const (
|
|
OptionHideCommandLine OptionVisibility = 1 << iota
|
|
OptionHideConfigurator
|
|
OptionHideBoth = OptionHideCommandLine | OptionHideConfigurator
|
|
)
|
|
|
|
// Option is describes an option for the config wizard
|
|
//
|
|
// This also describes command line options and environment variables
|
|
type Option struct {
|
|
Name string // name of the option in snake_case
|
|
Help string // Help, the first line only is used for the command line help
|
|
Provider string // Set to filter on provider
|
|
Default interface{} // default value, nil => ""
|
|
Value interface{} // value to be set by flags
|
|
Examples OptionExamples `json:",omitempty"` // config examples
|
|
ShortOpt string // the short option for this if required
|
|
Hide OptionVisibility // set this to hide the config from the configurator or the command line
|
|
Required bool // this option is required
|
|
IsPassword bool // set if the option is a password
|
|
NoPrefix bool // set if the option for this should not use the backend prefix
|
|
Advanced bool // set if this is an advanced config option
|
|
Exclusive bool // set if the answer can only be one of the examples
|
|
}
|
|
|
|
// BaseOption is an alias for Option used internally
|
|
type BaseOption Option
|
|
|
|
// MarshalJSON turns an Option into JSON
|
|
//
|
|
// It adds some generated fields for ease of use
|
|
// - DefaultStr - a string rendering of Default
|
|
// - ValueStr - a string rendering of Value
|
|
// - Type - the type of the option
|
|
func (o *Option) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(struct {
|
|
BaseOption
|
|
DefaultStr string
|
|
ValueStr string
|
|
Type string
|
|
}{
|
|
BaseOption: BaseOption(*o),
|
|
DefaultStr: fmt.Sprint(o.Default),
|
|
ValueStr: o.String(),
|
|
Type: o.Type(),
|
|
})
|
|
}
|
|
|
|
// GetValue gets the current current value which is the default if not set
|
|
func (o *Option) GetValue() interface{} {
|
|
val := o.Value
|
|
if val == nil {
|
|
val = o.Default
|
|
if val == nil {
|
|
val = ""
|
|
}
|
|
}
|
|
return val
|
|
}
|
|
|
|
// String turns Option into a string
|
|
func (o *Option) String() string {
|
|
return fmt.Sprint(o.GetValue())
|
|
}
|
|
|
|
// Set an Option from a string
|
|
func (o *Option) Set(s string) (err error) {
|
|
newValue, err := configstruct.StringToInterface(o.GetValue(), s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o.Value = newValue
|
|
return nil
|
|
}
|
|
|
|
// Type of the value
|
|
func (o *Option) Type() string {
|
|
return reflect.TypeOf(o.GetValue()).Name()
|
|
}
|
|
|
|
// FlagName for the option
|
|
func (o *Option) FlagName(prefix string) string {
|
|
name := strings.Replace(o.Name, "_", "-", -1) // convert snake_case to kebab-case
|
|
if !o.NoPrefix {
|
|
name = prefix + "-" + name
|
|
}
|
|
return name
|
|
}
|
|
|
|
// EnvVarName for the option
|
|
func (o *Option) EnvVarName(prefix string) string {
|
|
return OptionToEnv(prefix + "-" + o.Name)
|
|
}
|
|
|
|
// Copy makes a shallow copy of the option
|
|
func (o *Option) Copy() *Option {
|
|
copy := new(Option)
|
|
*copy = *o
|
|
return copy
|
|
}
|
|
|
|
// OptionExamples is a slice of examples
|
|
type OptionExamples []OptionExample
|
|
|
|
// Len is part of sort.Interface.
|
|
func (os OptionExamples) Len() int { return len(os) }
|
|
|
|
// Swap is part of sort.Interface.
|
|
func (os OptionExamples) Swap(i, j int) { os[i], os[j] = os[j], os[i] }
|
|
|
|
// Less is part of sort.Interface.
|
|
func (os OptionExamples) Less(i, j int) bool { return os[i].Help < os[j].Help }
|
|
|
|
// Sort sorts an OptionExamples
|
|
func (os OptionExamples) Sort() { sort.Sort(os) }
|
|
|
|
// OptionExample describes an example for an Option
|
|
type OptionExample struct {
|
|
Value string
|
|
Help string
|
|
Provider string
|
|
}
|
|
|
|
// Register a filesystem
|
|
//
|
|
// Fs modules should use this in an init() function
|
|
func Register(info *RegInfo) {
|
|
info.Options.setValues()
|
|
if info.Prefix == "" {
|
|
info.Prefix = info.Name
|
|
}
|
|
Registry = append(Registry, info)
|
|
}
|
|
|
|
// Find looks for a RegInfo object for the name passed in. The name
|
|
// can be either the Name or the Prefix.
|
|
//
|
|
// Services are looked up in the config file
|
|
func Find(name string) (*RegInfo, error) {
|
|
for _, item := range Registry {
|
|
if item.Name == name || item.Prefix == name || item.FileName() == name {
|
|
return item, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("didn't find backend called %q", name)
|
|
}
|
|
|
|
// MustFind looks for an Info object for the type name passed in
|
|
//
|
|
// Services are looked up in the config file
|
|
//
|
|
// Exits with a fatal error if not found
|
|
func MustFind(name string) *RegInfo {
|
|
fs, err := Find(name)
|
|
if err != nil {
|
|
log.Fatalf("Failed to find remote: %v", err)
|
|
}
|
|
return fs
|
|
}
|