template / commit
Got basic patterns working:
.vscode/launch.json | 39 ++++++++++- config.go | 49 ++++++++++---- main.go | 78 ++++++++++++---------- new.go | 187 +++++++++++++++++++++++++++++++++++++++++++--------- utils.go | 20 ++++++ 5 files changed, 293 insertions(+), 80 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 896dd01..f9195f2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,21 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch Package", + "name": "Delete ~/.template.yaml", + "type": "PowerShell", + "request": "launch", + "script": "rm ~/.template.yaml -ErrorAction SilentlyContinue", + "cwd": "${workspaceFolder}" + }, + { + "name": "Run: template", + "type": "go", + "request": "launch", + "mode": "auto", + "program": ".", + }, + { + "name": "Run: template blog", "type": "go", "request": "launch", "mode": "auto", @@ -13,6 +27,29 @@ "args": [ "blog" ] + }, + { + "name": "Run: template work", + "type": "go", + "request": "launch", + "mode": "auto", + "program": ".", + "args": [ + "work" + ] + }, + { + "name": "Run: template --profile work --name testProj", + "type": "go", + "request": "launch", + "mode": "auto", + "program": ".", + "args": [ + "--profile", + "work", + "--name", + "testProj" + ] } ] } \ No newline at end of file diff --git a/config.go b/config.go index 2aa8cfe..962eccc 100644 --- a/config.go +++ b/config.go @@ -14,9 +14,12 @@ const ( ) type Profile struct { + IsDefault bool Path string Type ProfileType NamePatterns []string + PostScript []string + // ReplacementStrings map[string][]string // FileTemplatePath string } @@ -25,31 +28,53 @@ type Config struct { Profiles map[string]Profile } -type PatternInfo struct { - Year4, Month2, Day2, Hour2, Minute2, Second2 string - ProjectName string -} - func defaultConfig() Config { return Config{ Profiles: map[string]Profile{ - "default": { - Path: "C:/DailyNotes", - Type: FileType, + "daily": { + IsDefault: true, + Path: "C:\\work\\daily-notes", + Type: FileType, NamePatterns: []string{ - "{{.Year4}}-{{.Month2}}-{{.Day2}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", - "{{.Year4}}-{{.Month2}}-{{.Day2}}-{{.Hour2}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", - "{{.Year4}}-{{.Month2}}-{{.Day2}}-{{.Hour2}}-{{.Minute2}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", - "{{.Year4}}-{{.Month2}}-{{.Day2}}-{{.Hour2}}-{{.Minute2}}-{{.Second2}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}-{{.Second}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + }, + PostScript: []string{ + "code", + ".", + "{{.Path}}", }, }, "blog": { Path: "~/Dropbox/Blog", Type: FileType, + NamePatterns: []string{ + "{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + "{{.Year}}-{{.Month}}-{{.Day}}-{{.Hour}}-{{.Minute}}-{{.Second}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.md", + }, + PostScript: []string{ + "code", + ".", + "{{.Name}}", + }, }, "work": { Path: "C:/work", Type: FolderType, + NamePatterns: []string{ + "{{.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}}", + }, + PostScript: []string{ + "explorer", + "{{.Path}}", + }, }, }, } diff --git a/main.go b/main.go index eea92df..d1e87f6 100644 --- a/main.go +++ b/main.go @@ -4,13 +4,13 @@ import ( "fmt" "log" "os" - "path/filepath" "github.com/urfave/cli/v2" // imports as package "cli" ) func main() { - defaultProfileName := "default" + configFilePath := getFullPath("~/.template.yaml") + // defaultProfileName := "default" var profileName string var projectName string @@ -20,9 +20,10 @@ func main() { EnableBashCompletion: true, Flags: []cli.Flag{ &cli.StringFlag{ - Name: "profile", - Aliases: []string{"p"}, - Value: defaultProfileName, + Name: "profile", + Aliases: []string{"p"}, + Value: "", + // Value: defaultProfileName, Usage: "`PROFILE` to use for the template", Destination: &profileName, // Required: true, @@ -37,25 +38,10 @@ func main() { }, }, Action: func(context *cli.Context) error { - // get profile name from args - if profileName == defaultProfileName && context.Args().Len() > 0 { - profileName = context.Args().Get(0) - } - message := fmt.Sprintf("Profile: %v", profileName) - fmt.Println(message) - - // construct config file path - userProfile, err := os.UserHomeDir() - if err != nil { - processError(err, "Getting user home dir") - } - configFilePath := filepath.Join(userProfile, ".template.yaml") - // try and load config config, err := loadConfig(configFilePath) if err != nil { - fmt.Printf("No config present at '%v', creating and using sample config", configFilePath) - fmt.Println() + fmt.Printf("No config present at '%v', creating and using sample config\n", configFilePath) // create config and save err := saveConfig(configFilePath, defaultConfig()) @@ -70,24 +56,46 @@ func main() { } } else { // log successful load - fmt.Printf("Config loaded from '%v'", configFilePath) - fmt.Println() + fmt.Printf("Config loaded from '%v'\n", configFilePath) } - // find profile by name - profile, ok := config.Profiles[profileName] - if ok { - fmt.Println("Found profile!") - // fmt.Println(profile) - } else { - processError(fmt.Errorf("Unable to find profile '%v'", profileName), "Error finding profile") + // read args + args := context.Args() + + // get profile name from args + // if not set explicitly, use first arg, if present + if profileName == "" && args.Len() > 0 { + profileName = args.Get(0) + } + fmt.Println("Profile:", profileName) + + // get project name from args + // if not set explicitly, use second arg, if present + if projectName == "" && args.Len() > 1 { + projectName = args.Get(1) + } + if projectName != "" { + fmt.Println("Project name:", projectName) + } + + // find profile + profile, err := findProfile(config, profileName) + if err != nil { + processError(err, "Error while finding profile") } - // cwd, err := os.Getwd() - // if err != nil { - // panic(err) - // } - processProfile(profile, projectName, profile.Path) + // change cwd + path := getFullPath(profile.Path) + err = os.Chdir(path) + if err != nil { + processError(err, fmt.Sprintf("Error while changing directory to '%v'", path)) + } + + // process profile + err = processProfile(profile, projectName) + if err != nil { + processError(err, "Error processing profile") + } return nil }, diff --git a/new.go b/new.go index 30e787c..2812a37 100644 --- a/new.go +++ b/new.go @@ -4,7 +4,8 @@ import ( "errors" "fmt" "os" - "path" + "os/exec" + "path/filepath" "strings" "text/template" "time" @@ -12,12 +13,45 @@ import ( "golang.org/x/exp/slices" ) -func processProfile(profile Profile, name string, cwd string) error { +func findProfile(config Config, name string) (Profile, error) { + if name == "" { + // use default profile + for _, p := range config.Profiles { + if p.IsDefault { + return p, nil + } + } + } else { + // find profile by name + profile, ok := config.Profiles[name] + if ok { + return profile, nil + } else { + return Profile{}, fmt.Errorf("Unable to find profile '%v'", name) + } + } + return Profile{}, errors.New("Couldn't find profile") +} + +func processProfile(profile Profile, name string) error { + // validate profile + if profile.NamePatterns == nil || len(profile.NamePatterns) == 0 { + return errors.New("No name patterns specified") + } + + // get current directory + cwd, err := os.Getwd() + if err != nil { + return err + } + + // get all entities in current directory files, err := os.ReadDir(cwd) if err != nil { return err } + // determine if we're dealing with files or directories var useDirs bool switch profile.Type { case FileType: @@ -28,6 +62,7 @@ func processProfile(profile Profile, name string, cwd string) error { return fmt.Errorf("Unknown profile type: %v", profile.Type) } + // filter entities to just files or directories var entities []string for _, file := range files { isDir := file.IsDir() @@ -36,50 +71,138 @@ func processProfile(profile Profile, name string, cwd string) error { } } - var newName string + // generate a new name based on profile and existing entities + newName, err := createNewName(name, profile, entities) + if err != nil { + return err + } + + fullPath := filepath.Join(cwd, newName) + + // create new entity + if newName == "" { + return errors.New("Couldn't find a good new name") + } else { + fmt.Printf("newName: %v\n", newName) + if useDirs { + fmt.Printf("Creating directory: %v\n", fullPath) + err = os.Mkdir(fullPath, os.ModePerm) + if err != nil { + return err + } + } else { + fmt.Printf("Creating file: %v\n", fullPath) + os.WriteFile(fullPath, []byte{}, os.ModePerm) + } + } + + // execute PostScript, if present + postScripts, err := createPostScriptArray(profile, newName, fullPath) + if err != nil { + return err + } + if postScripts != nil && len(postScripts) > 0 { + allArgs := []string{"/C"} + allArgs = append(allArgs, postScripts...) + fmt.Printf("Executing post script: cmd %v \n", strings.Trim(fmt.Sprint(allArgs), "[]")) + cmd := exec.Command("cmd", allArgs...) + if err := cmd.Run(); err != nil { + // return err + } + } + + return nil +} + +type NamePatternData struct { + Year, Month, Day, Hour, Minute, Second string + ProjectName string +} +type PostScriptPatternData struct { + Name string + Path string +} + +func createNewName(name string, profile Profile, existingEntities []string) (string, error) { now := time.Now() - patternInfo := PatternInfo{ - Year4: now.Format("2006"), - Month2: now.Format("01"), - Day2: now.Format("02"), - Hour2: now.Format("15"), - Minute2: now.Format("04"), - Second2: now.Format("04"), - ProjectName: name, + patternInfos := createNamePatternData(now, name) + for _, patternInfo := range patternInfos { + newName, err := createNewNameForProfile(profile, patternInfo, existingEntities) + if err != nil { + return "", err + } else { + return newName, nil + } + } + return "", errors.New("Couldn't find a new name") +} + +func createNamePatternData(now time.Time, projectName string) []NamePatternData { + patternInfo := NamePatternData{ + Year: now.Format("2006"), + Month: now.Format("01"), + Day: now.Format("02"), + Hour: now.Format("15"), + Minute: now.Format("04"), + Second: now.Format("05"), + ProjectName: projectName, } + + return []NamePatternData{patternInfo} +} + +func createNewNameForProfile(profile Profile, patternData NamePatternData, existingEntities []string) (string, error) { for _, namePattern := range profile.NamePatterns { - newName = "" - t, err := template.New("template-name").Parse(namePattern) + template, err := template.New("template-name").Parse(namePattern) if err != nil { - panic(err) + return "", err } + sb := new(strings.Builder) - err = t.Execute(sb, patternInfo) + err = template.Execute(sb, patternData) if err != nil { - panic(err) + return "", err } - newName = sb.String() - alreadyExists := slices.Contains(entities, newName) - if alreadyExists { - break + + newName := sb.String() + alreadyExists := slices.Contains(existingEntities, newName) + if !alreadyExists { + return newName, nil } } + return "", errors.New("Couldn't find a new name") +} - if newName == "" { - panic(errors.New("Couldn't find a good new name")) - } else { - fullName := path.Join(cwd, newName) - if useDirs { - err = os.Mkdir(fullName, os.ModePerm) - if err != nil { - panic(err) - } - } else { - os.WriteFile(fullName, []byte{}, os.ModePerm) +func createPostScriptArray(profile Profile, name string, path string) ([]string, error) { + array := make([]string, 0) + + if profile.PostScript == nil || len(profile.PostScript) == 0 { + return nil, nil + } + + patternData := PostScriptPatternData{ + Name: name, + Path: path, + } + + for _, postScriptPattern := range profile.PostScript { + + template, err := template.New("template-name").Parse(postScriptPattern) + if err != nil { + return nil, err + } + + sb := new(strings.Builder) + err = template.Execute(sb, patternData) + if err != nil { + return nil, err } + + postScript := sb.String() + array = append(array, postScript) } - return nil + return array, nil } // func getNextEntry(name string, existingNames []string, profile Profile) string { diff --git a/utils.go b/utils.go index d76a032..bbc6035 100644 --- a/utils.go +++ b/utils.go @@ -3,6 +3,9 @@ package main import ( "fmt" "os" + "os/user" + "path/filepath" + "strings" ) func processError(err error, message string) { @@ -15,3 +18,20 @@ func processError(err error, message string) { os.Exit(2) } + +func getFullPath(path string) string { + usr, _ := user.Current() + dir := usr.HomeDir + var newPath string + if path == "~" { + // In case of "~", which won't be caught by the "else if" + newPath = dir + } else if strings.HasPrefix(path, "~/") { + // Use strings.HasPrefix so we don't match paths like + // "/something/~/something/" + newPath = filepath.Join(dir, path[2:]) + } else { + newPath = path + } + return newPath +}