package main import ( "bytes" "embed" "errors" "flag" "fmt" "io/fs" "os" "path/filepath" "strings" "text/template" "unicode" "git.eidonix.com/eidonix/cletus-plugins/sdk/manifest" ) //go:embed all:template var templateFS embed.FS const sdkModule = "git.eidonix.com/eidonix/cletus-plugins/sdk" type templateData struct { ID, Publisher, Name, Title, Short, Module, SDKModule, SDKVersion string } func runNew(args []string) error { fs := flag.NewFlagSet("new", flag.ContinueOnError) sdkDir := fs.String("sdk-dir", "", "use a local copy of the SDK (for developing the SDK itself)") if err := fs.Parse(args); err != nil { return err } if fs.NArg() < 1 || fs.NArg() > 2 { return errors.New("usage: cletus-plugin new . [folder]") } id := fs.Arg(0) probe := manifest.Plugin{ID: id, Name: "x", Description: "x", Version: "0.1.0", MinAppVersion: "0.5.0", HostAPI: manifest.HostAPI} if errs := probe.Validate(); len(errs) > 0 { return errs[0] } publisher, name, _ := strings.Cut(id, ".") dir := name if fs.NArg() == 2 { dir = fs.Arg(1) } if _, err := os.Stat(dir); err == nil { return fmt.Errorf("%s already exists", dir) } data := templateData{ID: id, Publisher: publisher, Name: name, Title: title(name), Short: strings.ReplaceAll(name, "-", "_"), Module: publisher + ".plugins/" + name, SDKModule: sdkModule, SDKVersion: sdkVersion} if err := writeTemplate(dir, data); err != nil { return err } fmt.Printf("Created %s in %s.\n", id, dir) if *sdkDir != "" { abs, err := filepath.Abs(*sdkDir) if err != nil { return err } // Only for developing the SDK: check refuses replace directives. if out, err := goRun(dir, goEnv("", ""), "mod", "edit", "-replace", sdkModule+"="+abs); err != nil { return fmt.Errorf("go mod edit: %s", out) } } for _, step := range [][]string{{"mod", "tidy"}, {"mod", "vendor"}} { if out, err := goRun(dir, goEnv("", ""), step...); err != nil { return fmt.Errorf("go %s failed. Fix the problem below and run it in %s yourself:\n%s", strings.Join(step, " "), dir, out) } } fmt.Printf("\nNext:\n cd %s\n cletus-plugin check\n cletus-plugin run tools\n cletus-plugin run invoke %s_count_lines '{\"path\":\"sample.txt\"}'\n", dir, data.Short) return nil } func writeTemplate(dir string, data templateData) error { return fs.WalkDir(templateFS, "template", func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return err } rel := strings.TrimPrefix(path, "template/") rel = strings.TrimSuffix(rel, ".tmpl") rel = strings.ReplaceAll(rel, "_dot_", ".") src, err := templateFS.ReadFile(path) if err != nil { return err } t, err := template.New(rel).Delims("[[", "]]").Parse(string(src)) if err != nil { return fmt.Errorf("template %s: %w", rel, err) } var out bytes.Buffer if err := t.Execute(&out, data); err != nil { return fmt.Errorf("template %s: %w", rel, err) } dst := filepath.Join(dir, filepath.FromSlash(rel)) if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { return err } return os.WriteFile(dst, out.Bytes(), 0o644) }) } func title(name string) string { words := strings.Split(name, "-") for i, w := range words { if w != "" { r := []rune(w) r[0] = unicode.ToUpper(r[0]) words[i] = string(r) } } return strings.Join(words, " ") }