fuzzyworld

template / commit

A few minor changes:

10676da375ccfab1efc6473e2dbbefbc662c754b · Pavel Safronov · 2022-11-06 14:43:56 -0800 · parents 8c15a714adc1

A few minor changes:
- added a way to have a disabled profile
- added a few more tests
- made the new name method more generic by supplying a validator function
 .vscode/settings.json |  7 ++++++-
 default_config.go     | 31 +++++++++++++------------------
 main.go               |  2 +-
 name_pattern.go       | 12 ++++++------
 name_pattern_test.go  | 11 ++++++++---
 profile.go            | 22 ++++++++++++++++++++--
 6 files changed, 54 insertions(+), 31 deletions(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index bd1c779..ee0aba7 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,8 @@
 {
-    "cSpell.words": ["stretchr"]
+  "cSpell.words": [
+    "pwsh",
+    "runpostscriptonfailure",
+    "scriptshell",
+    "stretchr"
+  ]
 }
diff --git a/default_config.go b/default_config.go
index cc5e942..a27885a 100644
--- a/default_config.go
+++ b/default_config.go
@@ -151,15 +151,9 @@ Sincerely, me
 						"{{if (gt $version 1)}}v{{$version}}{{end}}" +
 						".md",
 				},
-			},
-			{
-				Name:        "therapy-open",
-				Description: "Open the ~/Therapy folder in VS Code",
-				OS:          "All",
-				Path:        "~/Therapy",
-				Type:        ScriptType,
 				ScriptShell: "pwsh",
 				PostScripts: []ParameterizedText{
+					"code . \"{{.FileName}}\"",
 					"code .",
 				},
 			},
@@ -232,9 +226,8 @@ echo "Let's test {{.ProjectName}}!"`,
 			},
 			{
 				Name:        "git",
-				Description: "Creates a  folder in the c:/code folder in the format YYYY-MM-DD[-hh][-mm][-ss][-ProjectName]",
-				OS:          "Windows",
-				Path:        "C:/code",
+				Description: "Creates a  folder in the ~/code folder in the format YYYY-MM-DD[-hh][-mm][-ss][-ProjectName]",
+				Path:        "~/code",
 				Type:        FolderType,
 				NamePatterns: []ParameterizedText{
 					"{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}",
@@ -249,19 +242,21 @@ echo "Let's test {{.ProjectName}}!"`,
 				},
 			},
 			{
-				Name:        "test",
-				Description: "Echoes formatted date and time, and project name if it is provided",
-				Type:        ScriptType,
+				Name:        "git-clone",
+				Description: "DISABLED - Clones a repository into a new folder under ~/code in the format YYYY-MM-DD[-hh][-mm][-ss][-ProjectName]",
+				IsDisabled:  true,
+				Path:        "~/code",
+				Type:        FolderType,
 				NamePatterns: []ParameterizedText{
 					"{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}",
+					"{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}{{if .ProjectName}}-{{.ProjectName}}{{end}}",
+					"{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}{{if .ProjectName}}-{{.ProjectName}}{{end}}",
+					"{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}-{{.Second}}{{if .ProjectName}}-{{.ProjectName}}{{end}}",
 				},
 				ScriptShell: "pwsh",
 				PostScripts: []ParameterizedText{
-					"echo \"{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}\"",
-					"echo \"Name={{.FileName}}\"",
-					"echo \"Path={{.FullPath}}\"",
-					"echo \"Value1={{.Extras.value1}}\"",
-					"echo \"Value2={{.Extras.value2}}\"",
+					"git clone {{.Url}} .",
+					"code .",
 				},
 			},
 		},
diff --git a/main.go b/main.go
index 3f56d33..485b68f 100644
--- a/main.go
+++ b/main.go
@@ -141,7 +141,7 @@ func main() {
 					}
 
 					yamlText := writer.String()
-					fmt.Printf("Profile '%v':\n", settings.projectName)
+					fmt.Printf("Profile '%v':\n", settings.profile)
 					fmt.Println(yamlText)
 					return nil
 				},
diff --git a/name_pattern.go b/name_pattern.go
index 2f30d6f..6992d39 100644
--- a/name_pattern.go
+++ b/name_pattern.go
@@ -6,8 +6,6 @@ import (
 	"strings"
 	"text/template"
 	"time"
-
-	"golang.org/x/exp/slices"
 )
 
 func createNewNameForPatterns(
@@ -15,7 +13,8 @@ func createNewNameForPatterns(
 	patternData PrePatternData,
 	projectName string,
 	namePatterns []ParameterizedText,
-	extras map[string]string, existingEntities []string) (string, error) {
+	extras map[string]string,
+	validator func(newName string) bool) (string, error) {
 	for index, namePattern := range namePatterns {
 
 		startingVersion := 1
@@ -42,14 +41,15 @@ func createNewNameForPatterns(
 			}
 
 			newName := sb.String()
-			alreadyExists := slices.Contains(existingEntities, newName)
+			isValid := validator(newName)
 			useIndex := invokeCount > 0
 			isLastPattern := index == len(namePatterns)-1
 
-			// if no conflict, use this name
-			if !alreadyExists {
+			// if new name is valid, use this name
+			if isValid {
 				return newName, nil
 			}
+
 			// if index is not being used (template did not invoke getVersion)
 			// then we're done
 			if !useIndex {
diff --git a/name_pattern_test.go b/name_pattern_test.go
index 9168364..6d509a1 100644
--- a/name_pattern_test.go
+++ b/name_pattern_test.go
@@ -3,6 +3,8 @@ package main
 import (
 	"testing"
 	"time"
+
+	"golang.org/x/exp/slices"
 )
 
 func TestCreateNewName(t *testing.T) {
@@ -30,7 +32,8 @@ func TestCreateNewName(t *testing.T) {
 	testHappyPath := func(projectName string, expectedName string, existingEntities []string) func(t *testing.T) {
 		return func(t *testing.T) {
 			prePatternData := createPrePatternData(now, projectName, extras)
-			newName, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, existingEntities)
+			validator := func(newName string) bool { return !slices.Contains(existingEntities, newName) }
+			newName, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, validator)
 			if err != nil {
 				t.Error("Error creating new name: ", err)
 			}
@@ -64,7 +67,8 @@ func TestCreateNewName(t *testing.T) {
 	testErrorPath := func(projectName string, existingEntities []string) func(t *testing.T) {
 		return func(t *testing.T) {
 			prePatternData := createPrePatternData(now, projectName, extras)
-			_, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, existingEntities)
+			validator := func(newName string) bool { return !slices.Contains(existingEntities, newName) }
+			_, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, validator)
 			if err == nil {
 				t.Error("Expected error but got none")
 			}
@@ -103,7 +107,8 @@ func TestCreateNewName_Index(t *testing.T) {
 	testHappyPath := func(expectedName string, existingEntities []string) func(t *testing.T) {
 		return func(t *testing.T) {
 			prePatternData := createPrePatternData(now, "test-project", extras)
-			newName, err := createNewNameForPatterns(now, prePatternData, "test-project", profile.NamePatterns, extras, existingEntities)
+			validator := func(newName string) bool { return !slices.Contains(existingEntities, newName) }
+			newName, err := createNewNameForPatterns(now, prePatternData, "test-project", profile.NamePatterns, extras, validator)
 			if err != nil {
 				t.Error("Error creating new name: ", err)
 			}
diff --git a/profile.go b/profile.go
index edede6c..af933e4 100644
--- a/profile.go
+++ b/profile.go
@@ -44,6 +44,7 @@ type Profile struct {
 	Name                   string
 	Description            string `yaml:"description,omitempty"`
 	IsDefault              bool
+	IsDisabled             bool
 	Type                   ProfileType
 	Path                   string `yaml:"path,omitempty"`
 	OS                     OSType `yaml:"os,omitempty"`
@@ -61,6 +62,11 @@ var forbiddenProfileNames = []string{
 }
 
 func processProfile(profile Profile, projectName string, extras map[string]string, usr user.User) []error {
+	if profile.IsDisabled {
+		fmt.Println("Profile is disabled, doing nothing")
+		return nil
+	}
+
 	// decide if the profile should run on this OS
 	profileOs := profile.OS
 	isWindows := runtime.GOOS == "windows"
@@ -114,7 +120,8 @@ func processProfile(profile Profile, projectName string, extras map[string]strin
 		}
 
 		// generate a new name based on time, profile, and existing entities
-		newName, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, entities)
+		validator := func(newName string) bool { return !slices.Contains(entities, newName) }
+		newName, err := createNewNameForPatterns(now, prePatternData, projectName, profile.NamePatterns, extras, validator)
 		swallowPrePostScriptErrors := profile.RunPostscriptOnFailure && profile.PostScripts != nil && len(profile.PostScripts) > 0
 
 		// check newName is created
@@ -212,12 +219,15 @@ func createEntity(
 		// create files in the new folder
 		if len(profile.Files) > 0 {
 			fmt.Printf("Creating %v files in folder: %v\n", len(profile.Files), fullPath)
+			existingFiles := []string{}
 			for index, file := range profile.Files {
 				filePrePatternData := createPrePatternData(now, projectName, extras)
-				fileName, err := createNewNameForPatterns(now, filePrePatternData, projectName, []ParameterizedText{file.Name}, extras, []string{})
+				validator := func(newName string) bool { return !slices.Contains(existingFiles, newName) }
+				fileName, err := createNewNameForPatterns(now, filePrePatternData, projectName, []ParameterizedText{file.Name}, extras, validator)
 				if err != nil {
 					return "", fmt.Errorf("error creating file name for file index %v : %v", index, err)
 				}
+				existingFiles = append(existingFiles, fileName)
 				subFilePath := filepath.Join(fullPath, fileName)
 				fmt.Printf("Creating sub file: %v\n", subFilePath)
 				err = createFile(filePrePatternData, file.File, subFilePath)
@@ -245,6 +255,14 @@ func validateProfile(profile *Profile) error {
 	if slices.Contains(forbiddenProfileNames, profile.Name) {
 		return fmt.Errorf("forbidden profile name '%s'", profile.Name)
 	}
+	if profile.NamePatterns == nil || len(profile.NamePatterns) == 0 {
+		return fmt.Errorf("namePatterns is required")
+	}
+	for index, namePattern := range profile.NamePatterns {
+		if namePattern == "" {
+			return fmt.Errorf("name pattern is required for pattern index %v of profile %v", index, profile.Name)
+		}
+	}
 	if profile.Type == "" || (profile.Type != FileType && profile.Type != FolderType && profile.Type != ScriptType) {
 		return fmt.Errorf("invalid profile type %s for profile %s", profile.Type, profile.Name)
 	}