github.com/wawandco/ox@v0.13.6-0.20230809142027-913b3d837f2a/cli/cli.go (about)

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  
    11  	"github.com/wawandco/ox/internal/info"
    12  	"github.com/wawandco/ox/internal/log"
    13  	"github.com/wawandco/ox/plugins/base/content"
    14  	"github.com/wawandco/ox/plugins/core"
    15  )
    16  
    17  // cli is the CLI wrapper for our tool. It is in charge
    18  // of articulating different commands, finding it and also
    19  // taking care of the CLI interaction.
    20  type cli struct {
    21  	Plugins []core.Plugin
    22  }
    23  
    24  // findCommand looks in the plugins for a command
    25  // with the passed name.
    26  func (c *cli) findCommand(name string) core.Command {
    27  	for _, cm := range c.Plugins {
    28  		// We skip subcommands on this case
    29  		// those will be wired by the parent command implementing
    30  		// Receive.
    31  		command, ok := cm.(core.Command)
    32  		if !ok || command.ParentName() != "" {
    33  			continue
    34  		}
    35  
    36  		alias, ok := cm.(core.Aliaser)
    37  		if ok && alias.Alias() == name {
    38  			return command
    39  		}
    40  
    41  		if command.Name() == name {
    42  			return command
    43  		}
    44  
    45  	}
    46  
    47  	return nil
    48  }
    49  
    50  // Wrap Runs the CLI or cmd/ox/main.go
    51  func (c *cli) Wrap(ctx context.Context, args []string) error {
    52  	if len(args) < 2 {
    53  		return c.Run(ctx, args)
    54  	}
    55  
    56  	mainPath := filepath.Join("cmd", "ox", "main.go")
    57  	if _, err := os.Stat(mainPath); err != nil {
    58  		return c.Run(ctx, args)
    59  	}
    60  
    61  	if modName := info.ModuleName(); modName == "" || modName == "github.com/wawandco/ox" {
    62  		return c.Run(ctx, args)
    63  	}
    64  
    65  	// Fix command should not do the wrapping logic
    66  	// we need to find a way to make this more generic.
    67  	if args[1] == "fix" {
    68  		return c.Run(ctx, args)
    69  	}
    70  
    71  	log.Infof("Using %v \n", mainPath)
    72  
    73  	cmd := exec.CommandContext(ctx, "go", "run")
    74  	cmd.Stdout = os.Stdout
    75  	cmd.Stderr = os.Stderr
    76  	cmd.Stdin = os.Stdin
    77  
    78  	args[0] = mainPath
    79  	cmd.Args = append(cmd.Args, args...)
    80  
    81  	return cmd.Run()
    82  }
    83  
    84  func (c *cli) Run(ctx context.Context, args []string) error {
    85  	if len(args) < 2 {
    86  		fmt.Println(content.Banner)
    87  		log.Error("no command provided, please provide one")
    88  
    89  		return nil
    90  	}
    91  
    92  	// Passing args and plugins to those plugins that require them
    93  	for _, plugin := range c.Plugins {
    94  		pf, ok := plugin.(core.FlagParser)
    95  		if ok {
    96  			pf.ParseFlags(args[1:])
    97  		}
    98  
    99  		pr, ok := plugin.(core.PluginReceiver)
   100  		if ok {
   101  			pr.Receive(c.Plugins)
   102  		}
   103  	}
   104  
   105  	command := c.findCommand(args[1])
   106  	if command == nil {
   107  		fmt.Println(content.Banner)
   108  		log.Infof("did not find %s command\n", args[1])
   109  		return nil
   110  	}
   111  
   112  	// Commands that require running within the ox directory
   113  	// may require its root to be determined with the go.mod. However,
   114  	// some other commands may want to determine the root by themselves,
   115  	// doing os.Getwd or something similar. The latter ones are RootFinders.
   116  	root := info.RootFolder()
   117  	rf, ok := command.(core.RootFinder)
   118  	if root == "" && !ok {
   119  		return errors.New("go.mod not found")
   120  	}
   121  
   122  	if root == "" {
   123  		root = rf.FindRoot()
   124  	}
   125  
   126  	return command.Run(ctx, root, args[1:])
   127  }
   128  
   129  // Use passed Plugins by appending these to the
   130  // plugins list inside the CLI.
   131  func (c *cli) Use(plugins ...core.Plugin) {
   132  	c.Plugins = append(c.Plugins, plugins...)
   133  }
   134  
   135  // Remove looks in the plugins list and removes plugins that
   136  // match passed names.
   137  func (c *cli) Remove(names ...string) {
   138  	result := make([]core.Plugin, 0)
   139  	for _, pl := range c.Plugins {
   140  		var found bool
   141  		for _, restricted := range names {
   142  			if pl.Name() == restricted {
   143  				found = true
   144  			}
   145  		}
   146  
   147  		if found {
   148  			continue
   149  		}
   150  
   151  		result = append(result, pl)
   152  	}
   153  
   154  	c.Plugins = result
   155  }
   156  
   157  // Clear the plugin list of the CLI.
   158  func (c *cli) Clear() {
   159  	c.Plugins = []core.Plugin{}
   160  }