diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go index 6f3bded8..66b4690f 100644 --- a/tools/goctl/goctl.go +++ b/tools/goctl/goctl.go @@ -20,6 +20,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/docker" "github.com/tal-tech/go-zero/tools/goctl/kube" model "github.com/tal-tech/go-zero/tools/goctl/model/sql/command" + "github.com/tal-tech/go-zero/tools/goctl/plugin" rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/cli" "github.com/tal-tech/go-zero/tools/goctl/tpl" "github.com/urfave/cli" @@ -186,6 +187,30 @@ var ( }, Action: ktgen.KtCommand, }, + { + Name: "plugin", + Usage: "custom file generator", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "plugin", + Usage: "the plugin file", + }, + cli.StringFlag{ + Name: "dir", + Usage: "the target directory", + }, + cli.StringFlag{ + Name: "api", + Usage: "the api file", + }, + cli.StringFlag{ + Name: "style", + Required: false, + Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]", + }, + }, + Action: plugin.PluginCommand, + }, }, }, { diff --git a/tools/goctl/plugin/demo/goctlplugin.go b/tools/goctl/plugin/demo/goctlplugin.go new file mode 100644 index 00000000..65028f4e --- /dev/null +++ b/tools/goctl/plugin/demo/goctlplugin.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "github.com/tal-tech/go-zero/tools/goctl/plugin" +) + +func main() { + plugin, err := plugin.NewPlugin() + if err != nil { + panic(err) + } + + if plugin.Api != nil { + fmt.Printf("api: %+v \n", plugin.Api) + } + fmt.Printf("dir: %s \n", plugin.Dir) + fmt.Println("Enjoy anything you want.") +} diff --git a/tools/goctl/plugin/plugin.go b/tools/goctl/plugin/plugin.go new file mode 100644 index 00000000..2470ea3e --- /dev/null +++ b/tools/goctl/plugin/plugin.go @@ -0,0 +1,166 @@ +package plugin + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/tal-tech/go-zero/tools/goctl/api/parser" + "github.com/tal-tech/go-zero/tools/goctl/api/spec" + "github.com/tal-tech/go-zero/tools/goctl/rpc/execx" + "github.com/tal-tech/go-zero/tools/goctl/util" + "github.com/urfave/cli" +) + +const ( + pluginArg = "_plugin" +) + +type Plugin struct { + Api *spec.ApiSpec + Style string + Dir string +} + +func PluginCommand(c *cli.Context) error { + ex, err := os.Executable() + if err != nil { + panic(err) + } + + var plugin = c.String("plugin") + if len(plugin) == 0 { + return errors.New("missing plugin") + } + + transferData, err := prepareArgs(c) + if err != nil { + return err + } + + bin, download, err := getCommand(plugin) + if err != nil { + return err + } + if download { + defer func() { + _ = os.Remove(bin) + }() + } + + content, err := execx.Run(bin, filepath.Dir(ex), bytes.NewBuffer(transferData)) + if err != nil { + return err + } + + fmt.Println(content) + return nil +} + +func prepareArgs(c *cli.Context) ([]byte, error) { + apiPath := c.String("api") + + var transferData Plugin + if len(apiPath) > 0 && util.FileExists(apiPath) { + p, err := parser.NewParser(apiPath) + if err != nil { + return nil, err + } + + api, err := p.Parse() + if err != nil { + return nil, err + } + + transferData.Api = api + } + + dirAbs, err := filepath.Abs(c.String("dir")) + if err != nil { + return nil, err + } + + transferData.Dir = dirAbs + transferData.Style = c.String("style") + data, err := json.Marshal(transferData) + if err != nil { + return nil, err + } + + return data, nil +} + +func getCommand(arg string) (string, bool, error) { + p, err := exec.LookPath(arg) + if err == nil { + abs, err := filepath.Abs(p) + if err != nil { + return "", false, err + } + return abs, false, nil + } + + var defaultErr = errors.New("invalid plugin value " + arg) + if strings.HasPrefix(arg, "http") { + items := strings.Split(arg, "/") + if len(items) == 0 { + return "", false, defaultErr + } + + filename, err := filepath.Abs(pluginArg + items[len(items)-1]) + if err != nil { + return "", false, err + } + + err = downloadFile(filename, arg) + if err != nil { + return "", false, err + } + + os.Chmod(filename, os.ModePerm) + return filename, true, nil + } + return arg, false, nil +} + +func downloadFile(filepath string, url string) error { + resp, err := http.Get(url) + if err != nil { + return err + } + defer func() { + _ = resp.Body.Close() + }() + + out, err := os.Create(filepath) + if err != nil { + return err + } + defer func() { + _ = out.Close() + }() + + _, err = io.Copy(out, resp.Body) + return err +} + +func NewPlugin() (*Plugin, error) { + var plugin Plugin + content, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + err = json.Unmarshal(content, &plugin) + if err != nil { + return nil, err + } + return &plugin, nil +} diff --git a/tools/goctl/rpc/execx/execx.go b/tools/goctl/rpc/execx/execx.go index 9b7e482f..9ce67305 100644 --- a/tools/goctl/rpc/execx/execx.go +++ b/tools/goctl/rpc/execx/execx.go @@ -12,7 +12,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/vars" ) -func Run(arg string, dir string) (string, error) { +func Run(arg string, dir string, in ...*bytes.Buffer) (string, error) { goos := runtime.GOOS var cmd *exec.Cmd switch goos { @@ -28,6 +28,9 @@ func Run(arg string, dir string) (string, error) { } stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) + if len(in) > 0 { + cmd.Stdin = in[0] + } cmd.Stdout = stdout cmd.Stderr = stderr err := cmd.Run() diff --git a/tools/goctl/util/string.go b/tools/goctl/util/string.go index b4fa326d..fc3145a0 100644 --- a/tools/goctl/util/string.go +++ b/tools/goctl/util/string.go @@ -17,3 +17,12 @@ func Untitle(s string) string { return strings.ToLower(s[:1]) + s[1:] } + +func Index(slice []string, item string) int { + for i, _ := range slice { + if slice[i] == item { + return i + } + } + return -1 +}