|
|
@@ -26,12 +26,17 @@ package issues
|
|
|
|
|
|
import (
|
|
|
"errors"
|
|
|
+ "fmt"
|
|
|
+ "math/rand"
|
|
|
"os"
|
|
|
+ "os/exec"
|
|
|
"path/filepath"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
)
|
|
|
|
|
|
// converts file from []string to string, reports errors
|
|
|
-func readPath(path string) (output string, err error) {
|
|
|
+func readPath(path string) (output string, err error) { // TODO DEPRECATE
|
|
|
content, err := os.ReadFile(path)
|
|
|
|
|
|
if err != nil {
|
|
|
@@ -45,6 +50,119 @@ func readPath(path string) (output string, err error) {
|
|
|
return output, nil
|
|
|
}
|
|
|
|
|
|
+// The quote string used to generate a template description
|
|
|
+type Template string
|
|
|
+
|
|
|
+var DescriptionTemplate = Template(`# Please provide a short, one line description.
|
|
|
+
|
|
|
+# Include any additional comments here.
|
|
|
+# -----------
|
|
|
+# Note: Lines beginning with "#" are automatically ignored.
|
|
|
+# Note: It is recommended to leave a blank line after the short description.`)
|
|
|
+
|
|
|
+// generates template description files
|
|
|
+func GenerateTemplate(t Template, path string) error {
|
|
|
+ err := os.WriteFile(path, []byte(DescriptionTemplate), 0755)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+// parses template description files, removing any lines beginning with #
|
|
|
+//
|
|
|
+// Note: the function deletes the template file upon completion.
|
|
|
+func ReadTemplate(path string) (lines []string, err error) {
|
|
|
+ data, err := os.ReadFile(path)
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, err
|
|
|
+ }
|
|
|
+ sdata := string(data)
|
|
|
+ slines := strings.SplitSeq(sdata, "\n")
|
|
|
+
|
|
|
+ for line := range slines {
|
|
|
+ if len(string(line)) > 0 {
|
|
|
+ if string(line[0]) != "#" {
|
|
|
+ lines = append(lines, line)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ lines = append(lines, line)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return lines, err
|
|
|
+}
|
|
|
+
|
|
|
+// InvokeEditor invokes a preconfigured editor, and reports the output and any errors.
|
|
|
+func InvokeEditor(path string) error {
|
|
|
+ // determine editor
|
|
|
+ // 1. Git config
|
|
|
+ // 2. $EDITOR
|
|
|
+ // 3. Panic?
|
|
|
+
|
|
|
+ // execute editor
|
|
|
+ cmd := exec.Command("vim", path)
|
|
|
+ cmd.Stdin = os.Stdin // capture data
|
|
|
+ cmd.Stdout = os.Stdout // capture data
|
|
|
+ cmd.Stderr = os.Stderr // capture data
|
|
|
+
|
|
|
+ // wait for cmd and error out if err
|
|
|
+ if err := cmd.Run(); err != nil {
|
|
|
+ return fmt.Errorf("editor execution failed: %w", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// EditTemplate wraps GenerateTemplate and ReadTemplate, providing
|
|
|
+// both with a tempfile. EditTemplate then reports the content of the tempfile
|
|
|
+// after any modifications made by the editFunc callback and any encountered
|
|
|
+// errors.
|
|
|
+//
|
|
|
+// Note: A pre-built editor function is provided. See InvokeEditor for more.
|
|
|
+func EditTemplate(t Template, editFunc func(path string) error) (data []string, err error) {
|
|
|
+ // note: wraps writeText
|
|
|
+
|
|
|
+ var seededRand = rand.New(
|
|
|
+ rand.NewSource(time.Now().UnixNano()))
|
|
|
+
|
|
|
+ makeID := func(length int) string {
|
|
|
+ var charset = "abcdefghijklmnopqrstuvwxyz" +
|
|
|
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
|
+ b := make([]byte, length)
|
|
|
+ for i := range b {
|
|
|
+ b[i] = charset[seededRand.Intn(len(charset))]
|
|
|
+ }
|
|
|
+ return string(b)
|
|
|
+ }
|
|
|
+
|
|
|
+ // get get temp file
|
|
|
+ tempfile, err := os.CreateTemp("", makeID(8))
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ cleanup := func() {
|
|
|
+ err = os.Remove(tempfile.Name())
|
|
|
+ if err != nil {
|
|
|
+ panic(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ defer cleanup()
|
|
|
+
|
|
|
+ err = GenerateTemplate(DescriptionTemplate, tempfile.Name())
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // invoke editor
|
|
|
+ err = editFunc(tempfile.Name())
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // get data
|
|
|
+ result, err := ReadTemplate(tempfile.Name())
|
|
|
+
|
|
|
+ return result, err
|
|
|
+}
|
|
|
+
|
|
|
// Reports true when the specified path conforms to the minimum Poorman spec
|
|
|
func IsIssue(path string) bool {
|
|
|
files, err := os.ReadDir(path)
|