fuzzyworld

template / commit

A few changes:

e86c26ae8eaa059447b2c2fb25f0e5442b93bc9d · Pavel Safronov · 2022-11-02 16:53:40 -0700 · parents 886f7fa24ec6

A few changes:
- improving the YAML representation of configs: fields reorganized, empty fields excluded
- change some log.Debug back to fmt.Printf calls, whenever the logged info is important to the user
- minor readme updates
- testing of config equality is now done by converting objects to YAML and comparing that text
 README.md      | 41 +++++++++++++++++-----------------
 config_test.go | 16 +++++++++++---
 profile.go     | 69 +++++++++++++++++++++++++++++-----------------------------
 utils.go       | 10 +++++++++
 4 files changed, 77 insertions(+), 59 deletions(-)

diff --git a/README.md b/README.md
index aeb9ff8..b6e09c9 100644
--- a/README.md
+++ b/README.md
@@ -20,11 +20,22 @@ Please keep in mind that since my focus is on the CLI, CLI bugs and features wil
 This is one of the profiles that comes with the tool.
 
 ```yaml
-    - file:
+    - name: daily
+      description: Creates a MD file in ~/DailyNotes folder in the format YYYY-MM-DD[-vX].md
+      isdefault: false
+      type: file
+      path: ~/DailyNotes
+      namepatterns:
+        - '{{.Year}}-{{.Month}}-{{.Day}}.md'
+      scriptshell: pwsh
+      runpostscriptonfailure: true
+      postscripts:
+        - code . {{ if .FullPath }}"{{.FullPath}}"{{else}}(gci . | select -last 1){{ end }}
+      file:
         template: |-
-            # {{.Year}}-{{.Month}}-{{.Day}}
+            # {{.FileNameWithoutExtension}}
 
-            Today's notes
+            Today's notes for {{.Year}}-{{.Month}}-{{.Day}}.
 
             ## Todo
 
@@ -34,29 +45,17 @@ This is one of the profiles that comes with the tool.
             4. Check yesterday's notes
             5. Have fun!
             6. ADD MORE ITEMS HERE
-      isdefault: false
-      name: daily
-      description: Creates a MD file in ~/DailyNotes folder in the format YYYY-MM-DD[-vX].md
-      path: ~/DailyNotes
-      type: file
-      namepatterns:
-        - '{{.Year}}-{{.Month}}-{{.Day}}.md'
-      postscripts:
-        - code . "{{.Path}}"
-      scriptshell: pwsh
-      os: ""
-      files: []
 ```
 
 Using the above configuration:
 ```pwsh
-> template daily
-Config loaded from 'C:\Users\fuzzy\.template.yaml'
-args: &[daily]
+> template daily               
 Profile: daily
-extras file ~/.template-extras.yaml does not exist, extras not being used
-Creating file 'C:\Users\fuzzy\DailyNotes\2022-10-23.md' from template string
-Executing post script: pwsh -c code . "C:\Users\fuzzy\DailyNotes\2022-10-23.md"
+Project name: 
+Loaded config from /Users/fuzzy/.template.yaml
+Creating file: /Users/fuzzy/DailyNotes/2022-11-02.md
+Executing post script: pwsh -c code . "/Users/fuzzy/DailyNotes/2022-11-02.md"
+Post script had no output
 ```
 
 This will cause VSCode to open the `~\DailyNotes` folder as a workspace, and the newly-created file `2022-09-26.md` will be opened as well.
diff --git a/config_test.go b/config_test.go
index 01a7528..4163c89 100644
--- a/config_test.go
+++ b/config_test.go
@@ -8,6 +8,15 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+// Compares YAML representation of two objects
+func compareObjectYaml(t *testing.T, expected, actual interface{}) {
+	expectedYaml, err := getYaml(expected)
+	assert.Nil(t, err, "Error marshalling expected")
+	actualYaml, err := getYaml(actual)
+	assert.Nil(t, err, "Error marshalling actual")
+	assert.Equal(t, expectedYaml, actualYaml)
+}
+
 func TestSaveLoadConfig(t *testing.T) {
 
 	config := defaultConfig()
@@ -22,7 +31,7 @@ func TestSaveLoadConfig(t *testing.T) {
 	assert.Nil(t, err, "Error loading config")
 
 	// test config
-	assert.Equal(t, loadedConfig, config)
+	compareObjectYaml(t, loadedConfig, config)
 
 	// test load non-existent config
 	_, err = loadConfig("non-existent-file.yaml")
@@ -50,9 +59,10 @@ func TestSaveLoadConfig(t *testing.T) {
 
 	// create default config
 	err = saveConfig(configPath, defaultConfig())
+	assert.Nil(t, err, "Error saving config")
 
 	// test config
-	assert.Equal(t, loadedConfig, config)
+	compareObjectYaml(t, loadedConfig, config)
 
 	// verify config file exists
 	stat1, err := os.Stat(configPath)
@@ -63,7 +73,7 @@ func TestSaveLoadConfig(t *testing.T) {
 	assert.Nil(t, err, "Error loading config")
 
 	// test config
-	assert.Equal(t, loadedConfig, config)
+	compareObjectYaml(t, loadedConfig, config)
 
 	// verify config file wasn't updated
 	stat2, err := os.Stat(configPath)
diff --git a/profile.go b/profile.go
index 1a1ac07..55b70a4 100644
--- a/profile.go
+++ b/profile.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 	"time"
 
-	log "github.com/sirupsen/logrus"
 	"golang.org/x/exp/slices"
 )
 
@@ -33,27 +32,27 @@ const (
 type ParameterizedText string
 
 type File struct {
-	Template ParameterizedText
+	Template ParameterizedText `yaml:"template,omitempty"`
 }
 
 type FileInFolder struct {
-	File
+	File `yaml:"file,omitempty"`
 	Name ParameterizedText
 }
 
 type Profile struct {
-	File
-	IsDefault              bool
 	Name                   string
-	Description            string
-	Path                   string
+	Description            string `yaml:"description,omitempty"`
+	IsDefault              bool
 	Type                   ProfileType
+	Path                   string `yaml:"path,omitempty"`
+	OS                     OSType `yaml:"os,omitempty"`
 	NamePatterns           []ParameterizedText
-	PostScripts            []ParameterizedText
-	ScriptShell            string
-	OS                     OSType
-	Files                  []FileInFolder
-	RunPostscriptOnFailure bool
+	ScriptShell            string              `yaml:"scriptshell,omitempty"`
+	Files                  []FileInFolder      `yaml:"files,omitempty"`
+	RunPostscriptOnFailure bool                `yaml:"runpostscriptonfailure,omitempty"`
+	PostScripts            []ParameterizedText `yaml:"postscripts,omitempty"`
+	File                   `yaml:"file,omitempty"`
 }
 
 var forbiddenProfileNames = []string{
@@ -113,18 +112,14 @@ func processProfile(profile Profile, projectName string, extras map[string]strin
 	now := time.Now()
 	errors := []error{}
 	newName, prePatternData, err := createNewNameForPatterns(now, projectName, profile.NamePatterns, extras, entities)
-
-	if err != nil {
-		errors = append(errors, err)
-		if !profile.RunPostscriptOnFailure {
-			return errors
-		}
-	}
+	swallowPrePostScriptErrors := profile.RunPostscriptOnFailure && profile.PostScripts != nil && len(profile.PostScripts) > 0
 
 	// check newName is created
 	if newName == "" {
-		errors = append(errors, fmt.Errorf("couldn't find a good new name"))
-		if !profile.RunPostscriptOnFailure {
+		if swallowPrePostScriptErrors {
+			fmt.Println("No new name was created, but will attempt to run post scripts")
+		} else {
+			errors = append(errors, fmt.Errorf("couldn't find a good new name"))
 			return errors
 		}
 	}
@@ -133,8 +128,10 @@ func processProfile(profile Profile, projectName string, extras map[string]strin
 	if err == nil && newName != "" {
 		fullPath, err = createEntity(now, profile, cwd, newName, projectName, extras, prePatternData)
 		if err != nil {
-			errors = append(errors, fmt.Errorf("unable to create entity: %v", err))
-			if !profile.RunPostscriptOnFailure {
+			if swallowPrePostScriptErrors {
+				fmt.Printf("Error creating entity, but will attempt to run post scripts: %v", err)
+			} else {
+				errors = append(errors, fmt.Errorf("unable to create entity: %v", err))
 				return errors
 			}
 		}
@@ -150,14 +147,20 @@ func processProfile(profile Profile, projectName string, extras map[string]strin
 	for _, postScript := range postScripts {
 		allArgs := []string{"-c", postScript}
 		shell := profile.ScriptShell
+
+		var out bytes.Buffer
 		fmt.Printf("Executing post script: %v %v\n", shell, strings.Join(allArgs, " "))
 		cmd := monkey.ExecCommand(shell, allArgs...)
-		var out bytes.Buffer
 		cmd.Stdout = &out
 		err := cmd.Run()
-		fmt.Printf("Post script output: %v", out.String())
+		output := out.String()
+		if output != "" {
+			fmt.Printf("Post script output: %v\n", output)
+		} else {
+			fmt.Printf("Post script had no output\n")
+		}
 		if err != nil {
-			// post scripts tend to fail (TODO: investigate why), so for now we'll just log the error
+			// post scripts can fail (TODO: investigate why), so for now we'll just log the error
 
 			fmt.Printf("Error executing post script '%v': %v\n",
 				fmt.Sprintf("%v %v", shell, strings.Trim(fmt.Sprint(allArgs), "[]")), err)
@@ -179,10 +182,7 @@ func createEntity(
 	profileType := profile.Type
 	fullPath := filepath.Join(cwd, newName)
 	if profileType == FolderType {
-		log.WithFields(log.Fields{
-			"fullPath":    fullPath,
-			"profileType": profileType,
-		}).Debug("Creating directory")
+		fmt.Printf("Creating folder: %v\n", fullPath)
 		err := monkey.OSMkdir(fullPath, os.ModePerm)
 		if err != nil {
 			return "", fmt.Errorf("error creating directory: %v", err)
@@ -194,16 +194,14 @@ func createEntity(
 
 		// create files in the new folder
 		if len(profile.Files) > 0 {
-			log.WithFields(log.Fields{
-				"fullPath":  fullPath,
-				"fileCount": len(profile.Files),
-			}).Debug("Creating files in directory")
+			fmt.Printf("Creating %v files in folder: %v\n", len(profile.Files), fullPath)
 			for index, file := range profile.Files {
 				fileName, filePrePatternData, err := createNewNameForPatterns(now, projectName, []ParameterizedText{file.Name}, extras, []string{})
 				if err != nil {
 					return "", fmt.Errorf("error creating file name for file index %v : %v", index, err)
 				}
 				subFilePath := filepath.Join(fullPath, fileName)
+				fmt.Printf("Creating sub file: %v\n", subFilePath)
 				err = createFile(filePrePatternData, file.File, subFilePath)
 				if err != nil {
 					return "", fmt.Errorf("error creating file %v: %v", subFilePath, err)
@@ -211,12 +209,13 @@ func createEntity(
 			}
 		}
 	} else if profileType == FileType {
+		fmt.Printf("Creating file: %v\n", fullPath)
 		err := createFile(prePatternData, profile.File, fullPath)
 		if err != nil {
 			return "", err
 		}
 	} else {
-		log.Debug("Profile type is script, doing nothing during main phase")
+		fmt.Printf("Profile type is script, doing nothing during main phase\n")
 	}
 	return fullPath, nil
 }
diff --git a/utils.go b/utils.go
index 7a794bb..eb774fc 100644
--- a/utils.go
+++ b/utils.go
@@ -4,6 +4,8 @@ import (
 	"os/user"
 	"path/filepath"
 	"strings"
+
+	"gopkg.in/yaml.v3"
 )
 
 func getFullPath(path string, usr user.User) string {
@@ -18,3 +20,11 @@ func getFullPath(path string, usr user.User) string {
 	}
 	return newPath
 }
+
+func getYaml(data interface{}) (string, error) {
+	yaml, err := yaml.Marshal(data)
+	if err != nil {
+		return "", err
+	}
+	return string(yaml), nil
+}