github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/config/flags/flags.go (about)

     1  // Package flags contains enhanced versions of spf13/pflag flag
     2  // routines which will read from the environment also.
     3  package flags
     4  
     5  import (
     6  	"log"
     7  	"os"
     8  	"regexp"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/rclone/rclone/fs"
    13  	"github.com/spf13/pflag"
    14  )
    15  
    16  // Groups of Flags
    17  type Groups struct {
    18  	// Groups of flags
    19  	Groups []*Group
    20  
    21  	// GroupsMaps maps a group name to a Group
    22  	ByName map[string]*Group
    23  }
    24  
    25  // Group related flags together for documentation purposes
    26  type Group struct {
    27  	Groups *Groups
    28  	Name   string
    29  	Help   string
    30  	Flags  *pflag.FlagSet
    31  }
    32  
    33  // NewGroups constructs a collection of Groups
    34  func NewGroups() *Groups {
    35  	return &Groups{
    36  		ByName: make(map[string]*Group),
    37  	}
    38  }
    39  
    40  // NewGroup to create Group
    41  func (gs *Groups) NewGroup(name, help string) *Group {
    42  	group := &Group{
    43  		Name:  name,
    44  		Help:  help,
    45  		Flags: pflag.NewFlagSet(name, pflag.ExitOnError),
    46  	}
    47  	gs.ByName[group.Name] = group
    48  	gs.Groups = append(gs.Groups, group)
    49  	return group
    50  }
    51  
    52  // Filter makes a copy of groups filtered by flagsRe
    53  func (gs *Groups) Filter(flagsRe *regexp.Regexp) *Groups {
    54  	newGs := NewGroups()
    55  	for _, g := range gs.Groups {
    56  		newG := newGs.NewGroup(g.Name, g.Help)
    57  		g.Flags.VisitAll(func(f *pflag.Flag) {
    58  			matched := flagsRe == nil || flagsRe.MatchString(f.Name) || flagsRe.MatchString(f.Usage)
    59  			if matched {
    60  				newG.Flags.AddFlag(f)
    61  			}
    62  		})
    63  	}
    64  	return newGs
    65  }
    66  
    67  // Include makes a copy of groups only including the ones in the filter string
    68  func (gs *Groups) Include(groupsString string) *Groups {
    69  	if groupsString == "" {
    70  		return gs
    71  	}
    72  	want := map[string]bool{}
    73  	for _, groupName := range strings.Split(groupsString, ",") {
    74  		_, ok := All.ByName[groupName]
    75  		if !ok {
    76  			log.Fatalf("Couldn't find group %q in command annotation", groupName)
    77  		}
    78  		want[groupName] = true
    79  	}
    80  	newGs := NewGroups()
    81  	for _, g := range gs.Groups {
    82  		if !want[g.Name] {
    83  			continue
    84  		}
    85  		newG := newGs.NewGroup(g.Name, g.Help)
    86  		newG.Flags = g.Flags
    87  	}
    88  	return newGs
    89  }
    90  
    91  // Add a new flag to the Group
    92  func (g *Group) Add(flag *pflag.Flag) {
    93  	g.Flags.AddFlag(flag)
    94  }
    95  
    96  // AllRegistered returns all flags in a group
    97  func (gs *Groups) AllRegistered() map[*pflag.Flag]struct{} {
    98  	out := make(map[*pflag.Flag]struct{})
    99  	for _, g := range gs.Groups {
   100  		g.Flags.VisitAll(func(f *pflag.Flag) {
   101  			out[f] = struct{}{}
   102  		})
   103  	}
   104  	return out
   105  }
   106  
   107  // All is the global stats Groups
   108  var All *Groups
   109  
   110  // Groups of flags for documentation purposes
   111  func init() {
   112  	All = NewGroups()
   113  	All.NewGroup("Copy", "Flags for anything which can Copy a file.")
   114  	All.NewGroup("Sync", "Flags just used for `rclone sync`.")
   115  	All.NewGroup("Important", "Important flags useful for most commands.")
   116  	All.NewGroup("Check", "Flags used for `rclone check`.")
   117  	All.NewGroup("Networking", "General networking and HTTP stuff.")
   118  	All.NewGroup("Performance", "Flags helpful for increasing performance.")
   119  	All.NewGroup("Config", "General configuration of rclone.")
   120  	All.NewGroup("Debugging", "Flags for developers.")
   121  	All.NewGroup("Filter", "Flags for filtering directory listings.")
   122  	All.NewGroup("Listing", "Flags for listing directories.")
   123  	All.NewGroup("Logging", "Logging and statistics.")
   124  	All.NewGroup("Metadata", "Flags to control metadata.")
   125  	All.NewGroup("RC", "Flags to control the Remote Control API.")
   126  }
   127  
   128  // installFlag constructs a name from the flag passed in and
   129  // sets the value and default from the environment if possible
   130  // the value may be overridden when the command line is parsed
   131  //
   132  // Used to create non-backend flags like --stats.
   133  //
   134  // It also adds the flag to the groups passed in.
   135  func installFlag(flags *pflag.FlagSet, name string, groupsString string) {
   136  	// Find flag
   137  	flag := flags.Lookup(name)
   138  	if flag == nil {
   139  		log.Fatalf("Couldn't find flag --%q", name)
   140  	}
   141  
   142  	// Read default from environment if possible
   143  	envKey := fs.OptionToEnv(name)
   144  	if envValue, envFound := os.LookupEnv(envKey); envFound {
   145  		err := flags.Set(name, envValue)
   146  		if err != nil {
   147  			log.Fatalf("Invalid value when setting --%s from environment variable %s=%q: %v", name, envKey, envValue, err)
   148  		}
   149  		fs.Debugf(nil, "Setting --%s %q from environment variable %s=%q", name, flag.Value, envKey, envValue)
   150  		flag.DefValue = envValue
   151  	}
   152  
   153  	// Add flag to Group if it is a global flag
   154  	if flags == pflag.CommandLine {
   155  		for _, groupName := range strings.Split(groupsString, ",") {
   156  			if groupName == "rc-" {
   157  				groupName = "RC"
   158  			}
   159  			group, ok := All.ByName[groupName]
   160  			if !ok {
   161  				log.Fatalf("Couldn't find group %q for flag --%s", groupName, name)
   162  			}
   163  			group.Add(flag)
   164  		}
   165  	}
   166  }
   167  
   168  // SetDefaultFromEnv constructs a name from the flag passed in and
   169  // sets the default from the environment if possible
   170  //
   171  // Used to create backend flags like --skip-links
   172  func SetDefaultFromEnv(flags *pflag.FlagSet, name string) {
   173  	envKey := fs.OptionToEnv(name)
   174  	envValue, found := os.LookupEnv(envKey)
   175  	if found {
   176  		flag := flags.Lookup(name)
   177  		if flag == nil {
   178  			log.Fatalf("Couldn't find flag --%q", name)
   179  		}
   180  		fs.Debugf(nil, "Setting default for %s=%q from environment variable %s", name, envValue, envKey)
   181  		//err = tempValue.Set()
   182  		flag.DefValue = envValue
   183  	}
   184  }
   185  
   186  // StringP defines a flag which can be set by an environment variable
   187  //
   188  // It is a thin wrapper around pflag.StringP
   189  func StringP(name, shorthand string, value string, usage string, groups string) (out *string) {
   190  	out = pflag.StringP(name, shorthand, value, usage)
   191  	installFlag(pflag.CommandLine, name, groups)
   192  	return out
   193  }
   194  
   195  // StringVarP defines a flag which can be set by an environment variable
   196  //
   197  // It is a thin wrapper around pflag.StringVarP
   198  func StringVarP(flags *pflag.FlagSet, p *string, name, shorthand string, value string, usage string, groups string) {
   199  	flags.StringVarP(p, name, shorthand, value, usage)
   200  	installFlag(flags, name, groups)
   201  }
   202  
   203  // BoolP defines a flag which can be set by an environment variable
   204  //
   205  // It is a thin wrapper around pflag.BoolP
   206  func BoolP(name, shorthand string, value bool, usage string, groups string) (out *bool) {
   207  	out = pflag.BoolP(name, shorthand, value, usage)
   208  	installFlag(pflag.CommandLine, name, groups)
   209  	return out
   210  }
   211  
   212  // BoolVarP defines a flag which can be set by an environment variable
   213  //
   214  // It is a thin wrapper around pflag.BoolVarP
   215  func BoolVarP(flags *pflag.FlagSet, p *bool, name, shorthand string, value bool, usage string, groups string) {
   216  	flags.BoolVarP(p, name, shorthand, value, usage)
   217  	installFlag(flags, name, groups)
   218  }
   219  
   220  // IntP defines a flag which can be set by an environment variable
   221  //
   222  // It is a thin wrapper around pflag.IntP
   223  func IntP(name, shorthand string, value int, usage string, groups string) (out *int) {
   224  	out = pflag.IntP(name, shorthand, value, usage)
   225  	installFlag(pflag.CommandLine, name, groups)
   226  	return out
   227  }
   228  
   229  // Int64P defines a flag which can be set by an environment variable
   230  //
   231  // It is a thin wrapper around pflag.IntP
   232  func Int64P(name, shorthand string, value int64, usage string, groups string) (out *int64) {
   233  	out = pflag.Int64P(name, shorthand, value, usage)
   234  	installFlag(pflag.CommandLine, name, groups)
   235  	return out
   236  }
   237  
   238  // Int64VarP defines a flag which can be set by an environment variable
   239  //
   240  // It is a thin wrapper around pflag.Int64VarP
   241  func Int64VarP(flags *pflag.FlagSet, p *int64, name, shorthand string, value int64, usage string, groups string) {
   242  	flags.Int64VarP(p, name, shorthand, value, usage)
   243  	installFlag(flags, name, groups)
   244  }
   245  
   246  // IntVarP defines a flag which can be set by an environment variable
   247  //
   248  // It is a thin wrapper around pflag.IntVarP
   249  func IntVarP(flags *pflag.FlagSet, p *int, name, shorthand string, value int, usage string, groups string) {
   250  	flags.IntVarP(p, name, shorthand, value, usage)
   251  	installFlag(flags, name, groups)
   252  }
   253  
   254  // Uint32VarP defines a flag which can be set by an environment variable
   255  //
   256  // It is a thin wrapper around pflag.Uint32VarP
   257  func Uint32VarP(flags *pflag.FlagSet, p *uint32, name, shorthand string, value uint32, usage string, groups string) {
   258  	flags.Uint32VarP(p, name, shorthand, value, usage)
   259  	installFlag(flags, name, groups)
   260  }
   261  
   262  // Float64P defines a flag which can be set by an environment variable
   263  //
   264  // It is a thin wrapper around pflag.Float64P
   265  func Float64P(name, shorthand string, value float64, usage string, groups string) (out *float64) {
   266  	out = pflag.Float64P(name, shorthand, value, usage)
   267  	installFlag(pflag.CommandLine, name, groups)
   268  	return out
   269  }
   270  
   271  // Float64VarP defines a flag which can be set by an environment variable
   272  //
   273  // It is a thin wrapper around pflag.Float64VarP
   274  func Float64VarP(flags *pflag.FlagSet, p *float64, name, shorthand string, value float64, usage string, groups string) {
   275  	flags.Float64VarP(p, name, shorthand, value, usage)
   276  	installFlag(flags, name, groups)
   277  }
   278  
   279  // DurationP defines a flag which can be set by an environment variable
   280  //
   281  // It wraps the duration in an fs.Duration for extra suffixes and
   282  // passes it to pflag.VarP
   283  func DurationP(name, shorthand string, value time.Duration, usage string, groups string) (out *time.Duration) {
   284  	out = new(time.Duration)
   285  	*out = value
   286  	pflag.VarP((*fs.Duration)(out), name, shorthand, usage)
   287  	installFlag(pflag.CommandLine, name, groups)
   288  	return out
   289  }
   290  
   291  // DurationVarP defines a flag which can be set by an environment variable
   292  //
   293  // It wraps the duration in an fs.Duration for extra suffixes and
   294  // passes it to pflag.VarP
   295  func DurationVarP(flags *pflag.FlagSet, p *time.Duration, name, shorthand string, value time.Duration, usage string, groups string) {
   296  	flags.VarP((*fs.Duration)(p), name, shorthand, usage)
   297  	installFlag(flags, name, groups)
   298  }
   299  
   300  // VarP defines a flag which can be set by an environment variable
   301  //
   302  // It is a thin wrapper around pflag.VarP
   303  func VarP(value pflag.Value, name, shorthand, usage string, groups string) {
   304  	pflag.VarP(value, name, shorthand, usage)
   305  	installFlag(pflag.CommandLine, name, groups)
   306  }
   307  
   308  // FVarP defines a flag which can be set by an environment variable
   309  //
   310  // It is a thin wrapper around pflag.VarP
   311  func FVarP(flags *pflag.FlagSet, value pflag.Value, name, shorthand, usage string, groups string) {
   312  	flags.VarP(value, name, shorthand, usage)
   313  	installFlag(flags, name, groups)
   314  }
   315  
   316  // StringArrayP defines a flag which can be set by an environment variable
   317  //
   318  // It sets one value only - command line flags can be used to set more.
   319  //
   320  // It is a thin wrapper around pflag.StringArrayP
   321  func StringArrayP(name, shorthand string, value []string, usage string, groups string) (out *[]string) {
   322  	out = pflag.StringArrayP(name, shorthand, value, usage)
   323  	installFlag(pflag.CommandLine, name, groups)
   324  	return out
   325  }
   326  
   327  // StringArrayVarP defines a flag which can be set by an environment variable
   328  //
   329  // It sets one value only - command line flags can be used to set more.
   330  //
   331  // It is a thin wrapper around pflag.StringArrayVarP
   332  func StringArrayVarP(flags *pflag.FlagSet, p *[]string, name, shorthand string, value []string, usage string, groups string) {
   333  	flags.StringArrayVarP(p, name, shorthand, value, usage)
   334  	installFlag(flags, name, groups)
   335  }
   336  
   337  // CountP defines a flag which can be set by an environment variable
   338  //
   339  // It is a thin wrapper around pflag.CountP
   340  func CountP(name, shorthand string, usage string, groups string) (out *int) {
   341  	out = pflag.CountP(name, shorthand, usage)
   342  	installFlag(pflag.CommandLine, name, groups)
   343  	return out
   344  }
   345  
   346  // CountVarP defines a flag which can be set by an environment variable
   347  //
   348  // It is a thin wrapper around pflag.CountVarP
   349  func CountVarP(flags *pflag.FlagSet, p *int, name, shorthand string, usage string, groups string) {
   350  	flags.CountVarP(p, name, shorthand, usage)
   351  	installFlag(flags, name, groups)
   352  }