commit c0ac147b09fb85c44786d3dfbd0b13a0bb3ab4c2
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Thu, 23 Sep 2021 22:42:24 +0800
gioinit: initial sketch
Three simple modes:
1. naked invocation pipes the template file to Stdout
2. provide a project path and perform the init ritual
3. provide a project and a module qualifer and perform the init ritual
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
| A | README.md | | | 37 | +++++++++++++++++++++++++++++++++++++ |
| A | go.mod | | | 3 | +++ |
| A | main.go | | | 192 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 232 insertions(+), 0 deletions(-)
diff --git a/README.md b/README.md
@@ -0,0 +1,37 @@
+# gioinit
+
+Simple tool that spits out a minimal Gio application with some common
+structure.
+
+Simply execute and pipe the results to a file.
+
+`gioinit > main.go`
+
+If you provide a qualified path the tool will perform the initialization
+ritual for you:
+
+`gioinit project git.sr.ht/~jackmordaunt/project`
+
+Is equivalent to:
+
+```sh
+mkdir project
+cd project
+go mod init git.sr.ht/~jackmordaunt/project
+gioinit > main.go
+go mod tidy
+go run .
+```
+
+`gioinit project`
+
+Is equivalent to:
+
+```sh
+mkdir project
+cd project
+go mod init project
+gioinit > main.go
+go mod tidy
+go run .
+```
diff --git a/go.mod b/go.mod
@@ -0,0 +1,3 @@
+module git.sr.ht/~jackmordaunt/gioinit
+
+go 1.17
diff --git a/main.go b/main.go
@@ -0,0 +1,192 @@
+// goinit spits out a templated gio program for bootstraping.
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "text/template"
+)
+
+func main() {
+ // By default just spit out the templated file. Let the user
+ // pipe it and handle setup.
+ if len(os.Args) == 1 {
+ if err := t.Execute(os.Stdout, nil); err != nil {
+ panic(fmt.Errorf("executing template: %w", err))
+ }
+ return
+ }
+ if err := func() error {
+ var (
+ // path to the new project. All intermediate
+ // directories will be created and the final
+ // element will be used as the project name.
+ path = os.Args[1]
+ // project is treated as the name for this new project.
+ //
+ // If provided, we can do some of the init ritual for
+ // them.
+ //
+ // This can be a full file sytsem path, but that
+ // will cause the qualifier to also be a file path
+ // unless a unique qualifer is specified.
+ project = filepath.Base(path)
+ // qualifer is the module path used in "go.mod".
+ // Defaults to the path if not explicitly specified.
+ qualifier = project
+ )
+ if len(os.Args) > 2 {
+ qualifier = os.Args[2]
+ }
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return fmt.Errorf("preparing directory: %w", err)
+ }
+ if err := writeTemplate(filepath.Join(path, "main.go")); err != nil {
+ return fmt.Errorf("writing template file: %w", err)
+ }
+ g := Go{Dir: path}
+ if err := g.Init(qualifier); err != nil {
+ return err
+ }
+ if err := g.Tidy(); err != nil {
+ return err
+ }
+ if err := g.Run(); err != nil {
+ return err
+ }
+ return nil
+ }(); err != nil {
+ fmt.Printf("error: %v", err)
+ }
+}
+
+// Go wraps the Go tool providing some common commands.
+type Go struct {
+ // Dir is the parent directory to execute the commands in.
+ Dir string
+}
+
+// Init runs "go mod init".
+func (g Go) Init(project string) error {
+ c := exec.Command("go", "mod", "init", project)
+ c.Dir = g.Dir
+ c.Stdout = os.Stdout
+ c.Stderr = os.Stderr
+ if err := c.Run(); err != nil {
+ return fmt.Errorf("go mod init %s: %w", project, err)
+ }
+ return nil
+}
+
+// Tidy runs "go mod tidy".
+func (g Go) Tidy() error {
+ c := exec.Command("go", "mod", "tidy")
+ c.Dir = g.Dir
+ c.Stdout = os.Stdout
+ c.Stderr = os.Stderr
+ if err := c.Run(); err != nil {
+ return fmt.Errorf("go mod tidy: %w", err)
+ }
+ return nil
+}
+
+// Run runs "go run .".
+func (g Go) Run() error {
+ c := exec.Command("go", "run", ".")
+ c.Dir = g.Dir
+ c.Stdout = os.Stdout
+ c.Stderr = os.Stderr
+ if err := c.Run(); err != nil {
+ return fmt.Errorf("go run: %w", err)
+ }
+ return nil
+}
+
+// writeTemplate writes the template source code out to disk.
+func writeTemplate(path string) error {
+ mainf, err := os.Create(path)
+ if err != nil {
+ return fmt.Errorf("creating file: %w", err)
+ }
+ defer mainf.Close()
+ if err := t.Execute(mainf, nil); err != nil {
+ return fmt.Errorf("executing template: %w", err)
+ }
+ return nil
+}
+
+var t = template.Must(template.New("").Parse(strings.TrimSpace(`
+package main
+
+import (
+ "log"
+ "os"
+
+ "gioui.org/app"
+ "gioui.org/font/gofont"
+ "gioui.org/io/system"
+ "gioui.org/layout"
+ "gioui.org/op"
+ "gioui.org/unit"
+ "gioui.org/widget/material"
+)
+
+func main() {
+ ui := UI{}
+ go func() {
+ if err := ui.Loop(); err != nil {
+ log.Printf("error: %v", err)
+ }
+ os.Exit(0)
+ }()
+ app.Main()
+}
+
+// UI maintains state for the entire user interface.
+type UI struct {
+ op.Ops
+ *app.Window
+}
+
+// Loop performs init and executes the event loop.
+func (ui *UI) Loop() error {
+ ui.Window = app.NewWindow(
+ app.Title("Gio"),
+ app.Size(unit.Dp(900), unit.Dp(500)),
+ )
+ for event := range ui.Window.Events() {
+ switch event := event.(type) {
+ case system.DestroyEvent:
+ return event.Err
+ case system.FrameEvent:
+ ui.Frame(event)
+ }
+ }
+ return nil
+}
+
+// Frame computes the frame and draws them into the window.
+func (ui *UI) Frame(event system.FrameEvent) {
+ ui.Layout(layout.NewContext(&ui.Ops, event))
+ event.Frame(&ui.Ops)
+}
+
+type (
+ // C convenience alias for "layout.Context".
+ C = layout.Context
+ // D convenience alias for "layout.Dimensions".
+ D = layout.Dimensions
+)
+
+var th = material.NewTheme(gofont.Collection())
+
+// Layout shows the UI.
+func (ui *UI) Layout(gtx C) D {
+ return layout.Center.Layout(gtx, func(gtx C) D {
+ return material.Label(th, unit.Sp(32), "Hello Gio").Layout(gtx)
+ })
+}
+`)))