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 }