From d5762330da39d7a5af2ea57e658ee236c07b2e70 Mon Sep 17 00:00:00 2001 From: steven carpenter Date: Tue, 22 Jul 2025 02:55:00 -0400 Subject: [PATCH] MVP --- .envrc | 1 + .gitignore | 5 ++ cmd/root.go | 58 ++++++++++++++++++++++ cmd/tui.go | 48 +++++++++++++++++++ flake.lock | 40 ++++++++++++++++ flake.nix | 74 ++++++++++++++++++++++++++++ go.mod | 39 +++++++++++++++ go.sum | 85 +++++++++++++++++++++++++++++++++ justfile | 31 ++++++++++++ main.go | 10 ++++ render/render.go | 57 ++++++++++++++++++++++ templates/README.md.tmpl | 4 ++ templates/cmd/root.go.tmpl | 28 +++++++++++ templates/cmd/version.go.tmpl | 15 ++++++ templates/config/config.go.tmpl | 19 ++++++++ templates/flake.nix.tmpl | 75 +++++++++++++++++++++++++++++ templates/go.mod.tmpl | 45 +++++++++++++++++ templates/justfile.tmpl | 31 ++++++++++++ templates/main.go.tmpl | 7 +++ templates/tui/model.go.tmpl | 11 +++++ templates/tui/run.go.tmpl | 10 ++++ templates/tui/update.go.tmpl | 18 +++++++ templates/tui/view.go.tmpl | 7 +++ 23 files changed, 718 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 cmd/root.go create mode 100644 cmd/tui.go create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 go.mod create mode 100644 go.sum create mode 100644 justfile create mode 100644 main.go create mode 100644 render/render.go create mode 100644 templates/README.md.tmpl create mode 100644 templates/cmd/root.go.tmpl create mode 100644 templates/cmd/version.go.tmpl create mode 100644 templates/config/config.go.tmpl create mode 100644 templates/flake.nix.tmpl create mode 100644 templates/go.mod.tmpl create mode 100644 templates/justfile.tmpl create mode 100644 templates/main.go.tmpl create mode 100644 templates/tui/model.go.tmpl create mode 100644 templates/tui/run.go.tmpl create mode 100644 templates/tui/update.go.tmpl create mode 100644 templates/tui/view.go.tmpl diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6a058e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.direnv +bin +result +data +output diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..e9c52c3 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "log" + + "github.com/spf13/cobra" + "specCon18/wand-templater/render" +) + +// CLI flag variables +var ( + modName string + packageName string + programVersion string + programDesc string + outputDir string // NEW: output directory flag +) + +// rootCmd renders templates using CLI flags +var rootCmd = &cobra.Command{ + Use: "wand-templater", + Short: "A tool to generate a go project template for building a terminal application with bubbletea + cobra + viper + log + mango", + Run: func(cmd *cobra.Command, args []string) { + // Fill ProgramData from CLI input + data := render.ProgramData{ + ModName: modName, + PackageName: packageName, + ProgramVersion: programVersion, + ProgramDesc: programDesc, + } + + // Render templates to the specified output directory + if err := render.RenderTemplates(data, outputDir); err != nil { + log.Fatalf("rendering failed: %v", err) + } + }, +} + +// Execute starts the CLI application +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +func init() { + // Register flags + rootCmd.Flags().StringVar(&modName, "mod-name", "", "Module name (e.g. github.com/user/app)") + rootCmd.Flags().StringVar(&packageName, "package-name", "", "Package name") + rootCmd.Flags().StringVar(&programVersion, "program-version", "", "Program version") + rootCmd.Flags().StringVar(&programDesc, "program-desc", "", "Program description") + rootCmd.Flags().StringVarP(&outputDir, "output", "o", "output", "Output directory for rendered files") + + // Mark required + rootCmd.MarkFlagRequired("mod-name") + rootCmd.MarkFlagRequired("package-name") + rootCmd.MarkFlagRequired("program-version") + rootCmd.MarkFlagRequired("program-desc") +} + diff --git a/cmd/tui.go b/cmd/tui.go new file mode 100644 index 0000000..7446ff6 --- /dev/null +++ b/cmd/tui.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "log" + + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" + "specCon18/wand-templater/render" +) + +// tuiCmd renders templates interactively via a form +var tuiCmd = &cobra.Command{ + Use: "tui", + Short: "an interactive TUI for generating a go module template for terminal applications using bubbletea + cobra + viper + log + mango", + Run: func(cmd *cobra.Command, args []string) { + var data render.ProgramData + + // Build the input form + form := huh.NewForm( + huh.NewGroup( + huh.NewInput().Title("Module Name").Placeholder("e.g. github.com/you/project").Value(&data.ModName), + huh.NewInput().Title("Package Name").Placeholder("e.g. myapp").Value(&data.PackageName), + huh.NewInput().Title("Program Version").Placeholder("e.g. 1.0.0").Value(&data.ProgramVersion), + huh.NewInput().Title("Program Description").Placeholder("Describe your program").Value(&data.ProgramDesc), + ), + ) + + // Run the form and exit on cancel + if err := form.Run(); err != nil { + log.Fatalf("form cancelled or failed: %v", err) + } + + // Retrieve --output flag value + out, _ := cmd.Flags().GetString("output") + + // Render templates with user input + if err := render.RenderTemplates(data, out); err != nil { + log.Fatalf("rendering failed: %v", err) + } + }, +} + +func init() { + // Allow output dir override + tuiCmd.Flags().StringP("output", "o", "output", "Output directory for rendered files") + rootCmd.AddCommand(tuiCmd) +} + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c924b2e --- /dev/null +++ b/flake.lock @@ -0,0 +1,40 @@ +{ + "nodes": { + "flake-schemas": { + "locked": { + "lastModified": 1721999734, + "narHash": "sha256-G5CxYeJVm4lcEtaO87LKzOsVnWeTcHGKbKxNamNWgOw=", + "rev": "0a5c42297d870156d9c57d8f99e476b738dcd982", + "revCount": 75, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.1.5/0190ef2f-61e0-794b-ba14-e82f225e55e6/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%2A" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1752866191, + "narHash": "sha256-NV4S2Lf2hYmZQ3Qf4t/YyyBaJNuxLPyjzvDma0zPp/M=", + "rev": "f01fe91b0108a7aff99c99f2e9abbc45db0adc2a", + "revCount": 806668, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2505.806668%2Brev-f01fe91b0108a7aff99c99f2e9abbc45db0adc2a/0198265b-7f79-72d2-af6f-40a40431be71/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/NixOS/nixpkgs/%2A" + } + }, + "root": { + "inputs": { + "flake-schemas": "flake-schemas", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..61fb296 --- /dev/null +++ b/flake.nix @@ -0,0 +1,74 @@ +# This flake was initially generated by fh, the CLI for FlakeHub (version 0.1.22) +{ + # A helpful description of your flake + description = "a tool for managing Wand template repos"; + + # Flake inputs + inputs = { + flake-schemas.url = "https://flakehub.com/f/DeterminateSystems/flake-schemas/*"; + + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/*"; + }; + + # Flake outputs that other flakes can use + outputs = + { + self, + flake-schemas, + nixpkgs, + }: + let + # Helpers for producing system-specific outputs + supportedSystems = [ "x86_64-linux" ]; + forEachSupportedSystem = + f: + nixpkgs.lib.genAttrs supportedSystems ( + system: + f { + inherit (nixpkgs) lib; + pkgs = import nixpkgs { inherit system; }; + } + ); + in + { + # Schemas tell Nix about the structure of your flake's outputs + schemas = flake-schemas.schemas; + packages = forEachSupportedSystem ( + { pkgs, lib }: + { + default = pkgs.callPackage ( + { buildGoModule }: + buildGoModule { + pname = "wand-templater"; + version = "0.0.1"; + src = builtins.path { + name = "source"; + path = ./.; + }; + vendorHash = "sha256-SQHzChRwEx8VYJwXcZXTMDgILrhhRvGqrdQ1k3lFWgk="; + } + ) { }; + } + ); + # Development environments + devShells = forEachSupportedSystem ( + { pkgs, ... }: + { + default = pkgs.mkShell { + # Pinned packages available in the environment + packages = with pkgs; [ + just + gcc + go_1_23 + gopls + nixpkgs-fmt + ]; + env = { + CGO_ENABLED = "1"; + CC = "gcc"; + }; + }; + } + ); + }; +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..030bbcc --- /dev/null +++ b/go.mod @@ -0,0 +1,39 @@ +module specCon18/wand-templater + +go 1.23.11 + +require ( + github.com/charmbracelet/huh v0.7.0 + github.com/spf13/cobra v1.9.1 +) + +require ( + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/catppuccin/go v0.3.0 // indirect + github.com/charmbracelet/bubbles v0.21.0 // indirect + github.com/charmbracelet/bubbletea v1.3.4 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1ba451c --- /dev/null +++ b/go.sum @@ -0,0 +1,85 @@ +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= +github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= +github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= +github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/huh v0.7.0 h1:W8S1uyGETgj9Tuda3/JdVkc3x7DBLZYPZc4c+/rnRdc= +github.com/charmbracelet/huh v0.7.0/go.mod h1:UGC3DZHlgOKHvHC07a5vHag41zzhpPFj34U92sOmyuk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= +github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= +github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= +github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= +github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/justfile b/justfile new file mode 100644 index 0000000..5429c57 --- /dev/null +++ b/justfile @@ -0,0 +1,31 @@ + +# Set the shell +set shell := ["bash", "-cu"] + +# Build the Go project +build: + go build -o bin/app . + +# Run the Go project +run: + go run . + +# Run tests +test: + go test ./... + +# Clean build artifacts +clean: + rm -rf bin + +# Format the code +fmt: + go fmt ./... + +# Tidy up go.mod/go.sum +tidy: + go mod tidy + +# Init Go module (optional first step) +init name: + go mod init {{name}} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0880d8f --- /dev/null +++ b/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "specCon18/wand-templater/cmd" +) + +func main() { + cmd.Execute() +} + diff --git a/render/render.go b/render/render.go new file mode 100644 index 0000000..79077a1 --- /dev/null +++ b/render/render.go @@ -0,0 +1,57 @@ +package render + +import ( + "log" + "os" + "path/filepath" + "strings" + "text/template" +) + +// ProgramData holds user-supplied template values +type ProgramData struct { + ModName string + PackageName string + ProgramVersion string + ProgramDesc string +} + +// RenderTemplates renders all .tmpl files from the templates/ directory into outputDir +func RenderTemplates(data ProgramData, outputDir string) error { + return 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 + } + + // Create relative output path (preserving subdirs) + relPath, err := filepath.Rel("templates", path) + if err != nil { + return err + } + outputPath := filepath.Join(outputDir, strings.TrimSuffix(relPath, ".tmpl")) + + // Ensure parent directories exist + if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { + return err + } + + // Parse and execute template + tmpl, err := template.ParseFiles(path) + if err != nil { + return err + } + + outFile, err := os.Create(outputPath) + if err != nil { + return err + } + defer outFile.Close() + + log.Printf("Rendering %s → %s\n", path, outputPath) + return tmpl.Execute(outFile, data) + }) +} + diff --git a/templates/README.md.tmpl b/templates/README.md.tmpl new file mode 100644 index 0000000..cf09e00 --- /dev/null +++ b/templates/README.md.tmpl @@ -0,0 +1,4 @@ +# {{.PackageName}} + +## TLDR; +{{.ProgramDesc}} diff --git a/templates/cmd/root.go.tmpl b/templates/cmd/root.go.tmpl new file mode 100644 index 0000000..6115ae4 --- /dev/null +++ b/templates/cmd/root.go.tmpl @@ -0,0 +1,28 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "{{.ModName}}/tui" +) + +var rootCmd = &cobra.Command{ + Use: "{{.PackageName}}", + Short: "{{.PackageName}}, {{.ProgramDesc}}", + Run: func(cmd *cobra.Command, args []string) { + if err := tui.Run(); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + }, +} + +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/templates/cmd/version.go.tmpl b/templates/cmd/version.go.tmpl new file mode 100644 index 0000000..09a4ab7 --- /dev/null +++ b/templates/cmd/version.go.tmpl @@ -0,0 +1,15 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version number of {{.PackageName}}", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("{{.PackageName}} ~ {{.ProgramVersion}}") + }, +} diff --git a/templates/config/config.go.tmpl b/templates/config/config.go.tmpl new file mode 100644 index 0000000..318ba30 --- /dev/null +++ b/templates/config/config.go.tmpl @@ -0,0 +1,19 @@ +package config + +import ( + "fmt" + "github.com/spf13/viper" +) + +func Init() { + viper.SetConfigName("config") // config.yaml + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + err := viper.ReadInConfig() + if err != nil { + fmt.Println("No config file found; using defaults.") + } + + viper.SetDefault("app.theme", "dark") +} diff --git a/templates/flake.nix.tmpl b/templates/flake.nix.tmpl new file mode 100644 index 0000000..c4d1ead --- /dev/null +++ b/templates/flake.nix.tmpl @@ -0,0 +1,75 @@ +# This flake was initially generated by fh, the CLI for FlakeHub (version 0.1.22) +{ + # A helpful description of your flake + description = "{{.ProgramDesc}}"; + + # Flake inputs + inputs = { + flake-schemas.url = "https://flakehub.com/f/DeterminateSystems/flake-schemas/*"; + + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/*"; + }; + + # Flake outputs that other flakes can use + outputs = + { + self, + flake-schemas, + nixpkgs, + }: + let + # Helpers for producing system-specific outputs + supportedSystems = [ "x86_64-linux" ]; + forEachSupportedSystem = + f: + nixpkgs.lib.genAttrs supportedSystems ( + system: + f { + inherit (nixpkgs) lib; + pkgs = import nixpkgs { inherit system; }; + } + ); + in + { + # Schemas tell Nix about the structure of your flake's outputs + schemas = flake-schemas.schemas; + packages = forEachSupportedSystem ( + { pkgs, lib }: + { + default = pkgs.callPackage ( + { buildGoModule }: + buildGoModule { + pname = "{{.PackageName}}"; + version = "{{.ProgramVersion}}"; + src = builtins.path { + name = "source"; + path = ./.; + }; + vendorHash = ""; + } + ) { }; + } + ); + # Development environments + devShells = forEachSupportedSystem ( + { pkgs, ... }: + { + default = pkgs.mkShell { + # Pinned packages available in the environment + packages = with pkgs; [ + just + gcc + go_1_23 + nixpkgs-fmt + gopls + cobra-cli + ]; + env = { + CGO_ENABLED = "1"; + CC = "gcc"; + }; + }; + } + ); + }; +} diff --git a/templates/go.mod.tmpl b/templates/go.mod.tmpl new file mode 100644 index 0000000..3c5fa6c --- /dev/null +++ b/templates/go.mod.tmpl @@ -0,0 +1,45 @@ +module {{.ModName}} + +go 1.21 + +require ( + github.com/charmbracelet/bubbletea v0.25.0 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.17.0 +) + +require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.13.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/templates/justfile.tmpl b/templates/justfile.tmpl new file mode 100644 index 0000000..96813e3 --- /dev/null +++ b/templates/justfile.tmpl @@ -0,0 +1,31 @@ + +# Set the shell +set shell := ["bash", "-cu"] + +# Build the Go project +build: + go build -o bin/app . + +# Run the Go project +run: + go run . + +# Run tests +test: + go test ./... + +# Clean build artifacts +clean: + rm -rf bin + +# Format the code +fmt: + go fmt ./... + +# Tidy up go.mod/go.sum +tidy: + go mod tidy + +# Init Go module (optional first step) +init: + go mod init diff --git a/templates/main.go.tmpl b/templates/main.go.tmpl new file mode 100644 index 0000000..106ce3a --- /dev/null +++ b/templates/main.go.tmpl @@ -0,0 +1,7 @@ +package main + +import "{{.ModName}}/cmd" + +func main() { + cmd.Execute() +} diff --git a/templates/tui/model.go.tmpl b/templates/tui/model.go.tmpl new file mode 100644 index 0000000..806f35b --- /dev/null +++ b/templates/tui/model.go.tmpl @@ -0,0 +1,11 @@ +package tui + +import tea "github.com/charmbracelet/bubbletea" + +type model struct { + count int +} + +func (m model) Init() tea.Cmd { + return nil +} diff --git a/templates/tui/run.go.tmpl b/templates/tui/run.go.tmpl new file mode 100644 index 0000000..9007620 --- /dev/null +++ b/templates/tui/run.go.tmpl @@ -0,0 +1,10 @@ +package tui + +import tea "github.com/charmbracelet/bubbletea" + +func Run() error { + p := tea.NewProgram(model{}) + _, err := p.Run() + return err +} + diff --git a/templates/tui/update.go.tmpl b/templates/tui/update.go.tmpl new file mode 100644 index 0000000..47983bf --- /dev/null +++ b/templates/tui/update.go.tmpl @@ -0,0 +1,18 @@ +package tui + +import tea "github.com/charmbracelet/bubbletea" + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q", "ctrl+c": + return m, tea.Quit + case "up": + m.count++ + case "down": + m.count-- + } + } + return m, nil +} diff --git a/templates/tui/view.go.tmpl b/templates/tui/view.go.tmpl new file mode 100644 index 0000000..e988945 --- /dev/null +++ b/templates/tui/view.go.tmpl @@ -0,0 +1,7 @@ +package tui + +import "fmt" + +func (m model) View() string { + return "Counter: " + fmt.Sprintf("%d", m.count) + "\n[↑/↓ to change, q to quit]" +}