gioinit

Bootstrap Gio programs with a simple template to get you started.
Log | Files | Refs | README | LICENSE

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:
AREADME.md | 37+++++++++++++++++++++++++++++++++++++
Ago.mod | 3+++
Amain.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) + }) +} +`)))