From bf78461bbb4a6d752f3787c5f3a739eb1e25e50f Mon Sep 17 00:00:00 2001 From: steven carpenter Date: Fri, 25 Jul 2025 15:38:34 -0400 Subject: [PATCH 1/3] fixed project name in flake --- flake.nix | 2 +- internal/logger/logger.go | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index c47b7fe..81207eb 100644 --- a/flake.nix +++ b/flake.nix @@ -39,7 +39,7 @@ default = pkgs.callPackage ( { buildGoModule }: buildGoModule { - pname = "wand-templater"; + pname = "bubblewand"; version = "0.0.1"; src = builtins.path { name = "source"; diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 1d09dcc..de1415c 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -16,4 +16,11 @@ func init() { Level: log.InfoLevel, }) } - +func initLogging() { + level, err := log.ParseLevel(logLevel) + if err != nil { + Log.Warn("Invalid log level; defaulting to info", "input", logLevel) + level = log.InfoLevel + } + Log.SetLevel(level) +} From 9e4d1a6a28a42d1440d2774dfd24ecac442392bd Mon Sep 17 00:00:00 2001 From: steven carpenter Date: Fri, 25 Jul 2025 16:01:48 -0400 Subject: [PATCH 2/3] embeded templates --- embed/embed.go | 7 ++ {templates => embed/templates}/.envrc.tmpl | 0 {templates => embed/templates}/README.md.tmpl | 0 .../templates}/cmd/root.go.tmpl | 0 .../templates}/cmd/version.go.tmpl | 0 .../templates}/config/config.go.tmpl | 1 - {templates => embed/templates}/flake.nix.tmpl | 0 {templates => embed/templates}/go.mod.tmpl | 0 {templates => embed/templates}/go.sum.tmpl | 0 .../templates}/internal/logger/logger.go.tmpl | 0 {templates => embed/templates}/justfile.tmpl | 0 {templates => embed/templates}/main.go.tmpl | 0 .../templates}/tui/model.go.tmpl | 0 .../templates}/tui/run.go.tmpl | 0 .../templates}/tui/update.go.tmpl | 0 .../templates}/tui/view.go.tmpl | 0 internal/logger/logger.go | 9 +- internal/render/render.go | 104 +++++++++--------- 18 files changed, 63 insertions(+), 58 deletions(-) create mode 100644 embed/embed.go rename {templates => embed/templates}/.envrc.tmpl (100%) rename {templates => embed/templates}/README.md.tmpl (100%) rename {templates => embed/templates}/cmd/root.go.tmpl (100%) rename {templates => embed/templates}/cmd/version.go.tmpl (100%) rename {templates => embed/templates}/config/config.go.tmpl (98%) rename {templates => embed/templates}/flake.nix.tmpl (100%) rename {templates => embed/templates}/go.mod.tmpl (100%) rename {templates => embed/templates}/go.sum.tmpl (100%) rename {templates => embed/templates}/internal/logger/logger.go.tmpl (100%) rename {templates => embed/templates}/justfile.tmpl (100%) rename {templates => embed/templates}/main.go.tmpl (100%) rename {templates => embed/templates}/tui/model.go.tmpl (100%) rename {templates => embed/templates}/tui/run.go.tmpl (100%) rename {templates => embed/templates}/tui/update.go.tmpl (100%) rename {templates => embed/templates}/tui/view.go.tmpl (100%) diff --git a/embed/embed.go b/embed/embed.go new file mode 100644 index 0000000..0cae6e2 --- /dev/null +++ b/embed/embed.go @@ -0,0 +1,7 @@ +package embed + +import "embed" + +//go:embed templates/*.tmpl templates/**/* +var Templates embed.FS + diff --git a/templates/.envrc.tmpl b/embed/templates/.envrc.tmpl similarity index 100% rename from templates/.envrc.tmpl rename to embed/templates/.envrc.tmpl diff --git a/templates/README.md.tmpl b/embed/templates/README.md.tmpl similarity index 100% rename from templates/README.md.tmpl rename to embed/templates/README.md.tmpl diff --git a/templates/cmd/root.go.tmpl b/embed/templates/cmd/root.go.tmpl similarity index 100% rename from templates/cmd/root.go.tmpl rename to embed/templates/cmd/root.go.tmpl diff --git a/templates/cmd/version.go.tmpl b/embed/templates/cmd/version.go.tmpl similarity index 100% rename from templates/cmd/version.go.tmpl rename to embed/templates/cmd/version.go.tmpl diff --git a/templates/config/config.go.tmpl b/embed/templates/config/config.go.tmpl similarity index 98% rename from templates/config/config.go.tmpl rename to embed/templates/config/config.go.tmpl index 66e4d2c..7167440 100644 --- a/templates/config/config.go.tmpl +++ b/embed/templates/config/config.go.tmpl @@ -1,7 +1,6 @@ package config import ( - "fmt" "github.com/spf13/viper" "{{.ModName}}/internal/logger" ) diff --git a/templates/flake.nix.tmpl b/embed/templates/flake.nix.tmpl similarity index 100% rename from templates/flake.nix.tmpl rename to embed/templates/flake.nix.tmpl diff --git a/templates/go.mod.tmpl b/embed/templates/go.mod.tmpl similarity index 100% rename from templates/go.mod.tmpl rename to embed/templates/go.mod.tmpl diff --git a/templates/go.sum.tmpl b/embed/templates/go.sum.tmpl similarity index 100% rename from templates/go.sum.tmpl rename to embed/templates/go.sum.tmpl diff --git a/templates/internal/logger/logger.go.tmpl b/embed/templates/internal/logger/logger.go.tmpl similarity index 100% rename from templates/internal/logger/logger.go.tmpl rename to embed/templates/internal/logger/logger.go.tmpl diff --git a/templates/justfile.tmpl b/embed/templates/justfile.tmpl similarity index 100% rename from templates/justfile.tmpl rename to embed/templates/justfile.tmpl diff --git a/templates/main.go.tmpl b/embed/templates/main.go.tmpl similarity index 100% rename from templates/main.go.tmpl rename to embed/templates/main.go.tmpl diff --git a/templates/tui/model.go.tmpl b/embed/templates/tui/model.go.tmpl similarity index 100% rename from templates/tui/model.go.tmpl rename to embed/templates/tui/model.go.tmpl diff --git a/templates/tui/run.go.tmpl b/embed/templates/tui/run.go.tmpl similarity index 100% rename from templates/tui/run.go.tmpl rename to embed/templates/tui/run.go.tmpl diff --git a/templates/tui/update.go.tmpl b/embed/templates/tui/update.go.tmpl similarity index 100% rename from templates/tui/update.go.tmpl rename to embed/templates/tui/update.go.tmpl diff --git a/templates/tui/view.go.tmpl b/embed/templates/tui/view.go.tmpl similarity index 100% rename from templates/tui/view.go.tmpl rename to embed/templates/tui/view.go.tmpl diff --git a/internal/logger/logger.go b/internal/logger/logger.go index de1415c..1d09dcc 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -16,11 +16,4 @@ func init() { Level: log.InfoLevel, }) } -func initLogging() { - level, err := log.ParseLevel(logLevel) - if err != nil { - Log.Warn("Invalid log level; defaulting to info", "input", logLevel) - level = log.InfoLevel - } - Log.SetLevel(level) -} + diff --git a/internal/render/render.go b/internal/render/render.go index 4eea7c2..fb5462e 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -1,71 +1,77 @@ package render import ( - "os" - "path/filepath" - "strings" - "text/template" - "specCon18/bubblewand/internal/logger" + "io/fs" + "os" + "path/filepath" + "strings" + "text/template" + + "specCon18/bubblewand/embed" + "specCon18/bubblewand/internal/logger" ) // ProgramData holds user-supplied template values type ProgramData struct { - ModName string - PackageName string - ProgramVersion string - ProgramDesc string - OutputDir string + ModName string + PackageName string + ProgramVersion string + ProgramDesc string + OutputDir string } -// RenderTemplates renders all .tmpl files from the templates/ directory into outputDir +// RenderTemplates renders embedded .tmpl files into outputDir func RenderTemplates(data ProgramData, outputDir string, verbose bool) error { - var renderedFiles int + var renderedFiles int - err := filepath.Walk("templates", func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() || !strings.HasSuffix(info.Name(), ".tmpl") { - return nil - } + err := fs.WalkDir(embed.Templates, "templates", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || !strings.HasSuffix(d.Name(), ".tmpl") { + return nil + } - relPath, err := filepath.Rel("templates", path) - if err != nil { - return err - } - outputPath := filepath.Join(outputDir, strings.TrimSuffix(relPath, ".tmpl")) + // Get relative path inside templates/ + relPath := strings.TrimPrefix(path, "templates/") + outputPath := filepath.Join(outputDir, strings.TrimSuffix(relPath, ".tmpl")) - if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { - return err - } + if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { + return err + } - tmpl, err := template.ParseFiles(path) - if err != nil { - return err - } + tmplBytes, err := embed.Templates.ReadFile(path) + if err != nil { + return err + } - outFile, err := os.Create(outputPath) - if err != nil { - return err - } - defer outFile.Close() + tmpl, err := template.New(d.Name()).Parse(string(tmplBytes)) + if err != nil { + return err + } - if verbose { - logger.Log.Infof("Rendering %s → %s", path, outputPath) - } + outFile, err := os.Create(outputPath) + if err != nil { + return err + } + defer outFile.Close() - renderedFiles++ - return tmpl.Execute(outFile, data) - }) + if verbose { + logger.Log.Infof("Rendering %s → %s", path, outputPath) + } - if err != nil { - return err - } + renderedFiles++ + return tmpl.Execute(outFile, data) + }) - if !verbose && renderedFiles > 0 { - logger.Log.Info("Rendering templates") - } + if err != nil { + return err + } - return nil + if !verbose && renderedFiles > 0 { + logger.Log.Info("Rendering templates") + } + + return nil } From 69674ffaa736728b944d9616e9f45bad89fcad89 Mon Sep 17 00:00:00 2001 From: steven carpenter Date: Fri, 25 Jul 2025 17:22:38 -0400 Subject: [PATCH 3/3] added better error handling to render/render.go --- internal/render/render.go | 47 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/internal/render/render.go b/internal/render/render.go index fb5462e..730a867 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -7,71 +7,94 @@ import ( "strings" "text/template" - "specCon18/bubblewand/embed" - "specCon18/bubblewand/internal/logger" + "specCon18/bubblewand/embed" // Import embedded template files + "specCon18/bubblewand/internal/logger" // Import logger for logging output ) -// ProgramData holds user-supplied template values +// ProgramData holds user-supplied values for template substitution. type ProgramData struct { - ModName string - PackageName string - ProgramVersion string - ProgramDesc string - OutputDir string + ModName string // Module name + PackageName string // Go package name + ProgramVersion string // Version string + ProgramDesc string // Description of the program + OutputDir string // Target output directory for rendered files } // RenderTemplates renders embedded .tmpl files into outputDir func RenderTemplates(data ProgramData, outputDir string, verbose bool) error { - var renderedFiles int + var renderedFiles int // Count of successfully rendered files + // Walk through the embedded templates filesystem starting at "templates" err := fs.WalkDir(embed.Templates, "templates", func(path string, d fs.DirEntry, err error) error { if err != nil { + // Log and return error encountered while walking the filesystem + logger.Log.Errorf("Error walking path %q: %v", path, err) return err } + + // Skip directories and files that do not end with ".tmpl" if d.IsDir() || !strings.HasSuffix(d.Name(), ".tmpl") { return nil } - // Get relative path inside templates/ + // Strip the "templates/" prefix from path and remove ".tmpl" extension relPath := strings.TrimPrefix(path, "templates/") outputPath := filepath.Join(outputDir, strings.TrimSuffix(relPath, ".tmpl")) + // Ensure the parent directory for the output file exists if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { + logger.Log.Errorf("Failed to create directory for %s: %v", outputPath, err) return err } + // Read the embedded template file tmplBytes, err := embed.Templates.ReadFile(path) if err != nil { + logger.Log.Errorf("Failed to read template %s: %v", path, err) return err } + // Parse the template content tmpl, err := template.New(d.Name()).Parse(string(tmplBytes)) if err != nil { + logger.Log.Errorf("Failed to parse template %s: %v", path, err) return err } + // Create the output file for writing the rendered content outFile, err := os.Create(outputPath) if err != nil { + logger.Log.Errorf("Failed to create output file %s: %v", outputPath, err) return err } defer outFile.Close() + // Log the render operation if verbose mode is enabled if verbose { logger.Log.Infof("Rendering %s → %s", path, outputPath) } + // Execute the template using the provided data and write to file + if err := tmpl.Execute(outFile, data); err != nil { + logger.Log.Errorf("Failed to execute template %s: %v", path, err) + return err + } + renderedFiles++ - return tmpl.Execute(outFile, data) + return nil }) + // If any error occurred during the walk/render process, log and return it if err != nil { + logger.Log.Errorf("Template rendering failed: %v", err) return err } + // If not verbose and at least one file was rendered, print a summary log if !verbose && renderedFiles > 0 { logger.Log.Info("Rendering templates") } - return nil + return nil // Success }