github.com/GGP1/kure@v0.8.4/commands/it/it.go (about)

     1  package it
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/GGP1/kure/auth"
     7  	cmdutil "github.com/GGP1/kure/commands"
     8  
     9  	"github.com/spf13/cobra"
    10  	bolt "go.etcd.io/bbolt"
    11  )
    12  
    13  const example = `
    14  * No arguments
    15  kure it
    16  
    17  * Command without flags
    18  kure it ls
    19  
    20  * Command with flags
    21  kure it ls -s -q
    22  
    23  * Only the name
    24  kure sample`
    25  
    26  // NewCmd returns a new command.
    27  func NewCmd(db *bolt.DB) *cobra.Command {
    28  	return &cobra.Command{
    29  		Use:   "it <command|flags|name>",
    30  		Short: "Execute commands through an interactive prompt",
    31  		Long: `Interactive prompt.
    32  This command behaves depending on the arguments received, it requests the missing information.
    33  
    34  Given 				Requests
    35  command 			flags and name
    36  command and flags 		name
    37  name 				command and flags`,
    38  		Example:            example,
    39  		DisableFlagParsing: true,
    40  		PreRunE:            auth.Login(db),
    41  		RunE:               runIt(db),
    42  	}
    43  }
    44  
    45  func runIt(db *bolt.DB) cmdutil.RunEFunc {
    46  	return func(cmd *cobra.Command, args []string) error {
    47  		root := cmd.Root()
    48  		// Get rid of unnecessary information and reset in case we are inside a session
    49  		defer root.SetUsageTemplate(root.UsageTemplate())
    50  		root.SetUsageTemplate(template)
    51  
    52  		// We received nothing, request all
    53  		if len(args) == 0 {
    54  			arguments, err := requestCommands(db, root, nil)
    55  			if err != nil {
    56  				return err
    57  			}
    58  
    59  			return execute(root, arguments)
    60  		}
    61  
    62  		command, _, err := root.Find(args)
    63  		if err != nil || command == root {
    64  			// If the command does not exist or is the root, assume the user passed a name
    65  			arguments, err := gotName(db, root, args)
    66  			if err != nil {
    67  				return err
    68  			}
    69  
    70  			return execute(root, arguments)
    71  		}
    72  
    73  		foundFlags := false
    74  		for _, a := range args {
    75  			if strings.HasPrefix(a, "-") {
    76  				foundFlags = true
    77  				break
    78  			}
    79  		}
    80  
    81  		if foundFlags {
    82  			// Got command+flags, do not look for subcommands
    83  			if err := command.ParseFlags(args); err != nil {
    84  				return err
    85  			}
    86  
    87  			// Get rid of the command and flags to validate the name
    88  			argsWoFlags := strings.Join(command.Flags().Args(), " ")
    89  			name := strings.Replace(argsWoFlags, command.Name(), "", 1)
    90  
    91  			// The validation won't fail if the user lists records
    92  			err := command.ValidateArgs([]string{name})
    93  			if err != nil || strings.Contains(command.Name(), "ls") {
    94  				// Received commands+flags, request name
    95  				arguments, err := requestName(db, args)
    96  				if err != nil {
    97  					return err
    98  				}
    99  
   100  				return execute(root, arguments)
   101  			}
   102  
   103  			// Received command+flags+name, nothing to request
   104  			return execute(root, args)
   105  		}
   106  
   107  		// Pass on received command(s) and look for subcommands
   108  		arguments, err := requestCommands(db, command, args)
   109  		if err != nil {
   110  			return err
   111  		}
   112  
   113  		return execute(root, arguments)
   114  	}
   115  }
   116  
   117  func execute(root *cobra.Command, args []string) error {
   118  	// Discard empty arguments as some commands will fail if we don't
   119  	// eg. file cat
   120  	filteredArgs := make([]string, 0, len(args))
   121  	for _, a := range args {
   122  		if a != "" {
   123  			filteredArgs = append(filteredArgs, a)
   124  		}
   125  	}
   126  
   127  	root.SetArgs(filteredArgs)
   128  	return root.Execute()
   129  }
   130  
   131  func requestCommands(db *bolt.DB, root *cobra.Command, receivedCmds []string) ([]string, error) {
   132  	commands, err := selectCommands(root)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	flags, err := selectFlags(root, commands)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	args := append(commands, flags...)
   143  	// Preprend the received commands if there is any
   144  	// We would have [received commands] [commands] [flags]
   145  	if len(receivedCmds) > 0 {
   146  		args = append(receivedCmds, args...)
   147  	}
   148  	return requestName(db, args)
   149  }
   150  
   151  // args contains commands and flags.
   152  func requestName(db *bolt.DB, args []string) ([]string, error) {
   153  	var (
   154  		name string
   155  		err  error
   156  	)
   157  
   158  	search := strings.Join(args, " ")
   159  	// contains reports whether s is within search
   160  	contains := func(s string) bool {
   161  		return strings.Contains(search, s)
   162  	}
   163  
   164  	// Behave depending on which command the user is executing
   165  	switch {
   166  	case contains("add"),
   167  		contains("ls") && contains("-f"), // Filter
   168  		contains("rm") && contains("-d"): // Remove directory
   169  		name, err = inputName()
   170  
   171  	case contains("import"), contains("export"):
   172  		name, err = selectManager(db)
   173  
   174  	case contains("file cat"), contains("file touch"):
   175  		names, err := fileMultiselect(db)
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  		return append(args, names...), nil
   180  
   181  	case contains("file mv"):
   182  		names, err := fileMvNames(db)
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  		return append(args, names...), nil
   187  
   188  	default:
   189  		list := []string{"2fa", "copy", "edit", "ls", "rm"}
   190  		// Request the name depending on the command
   191  		for _, cmd := range list {
   192  			if contains(cmd) {
   193  				// Skip "config edit" as it doesn't need a name
   194  				if args[0] != "config" {
   195  					name, err = selectName(db, args)
   196  					break
   197  				}
   198  			}
   199  		}
   200  	}
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	// Remember: the flags are inside the commands slice
   206  	result := append(args, name)
   207  	return result, nil
   208  }
   209  
   210  // gotName is executed when the user already provided the name, commands and flags are requested only.
   211  func gotName(db *bolt.DB, root *cobra.Command, args []string) ([]string, error) {
   212  	var (
   213  		name  []string
   214  		flags []string
   215  	)
   216  
   217  	for _, a := range args {
   218  		if strings.HasPrefix(a, "-") {
   219  			flags = append(flags, a)
   220  			continue
   221  		}
   222  		name = append(name, a)
   223  	}
   224  
   225  	commands, err := selectCommands(root)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	if len(flags) == 0 {
   231  		flags, err = selectFlags(root, commands)
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  	}
   236  
   237  	if len(name) == 0 {
   238  		name, err = requestName(db, commands)
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  	}
   243  
   244  	result := append(commands, flags...)
   245  	result = append(result, name...)
   246  
   247  	return result, nil
   248  }