Adds a proper CLI

This commit is contained in:
Miguel de la Cruz 2024-07-03 20:14:38 +02:00
parent 8213c61bf7
commit 943e9df086
5 changed files with 51 additions and 22 deletions

View file

@ -6,6 +6,7 @@ A simple CLI tool to generate a static website from a
## Roadmap ## Roadmap
- [X] Recursively traverse the `recipes` directory. - [X] Recursively traverse the `recipes` directory.
- [X] Add a proper CLI.
- [ ] Add a default CSS file. - [ ] Add a default CSS file.
- [ ] Improve the steps rendering logic to avoid doing all of it - [ ] Improve the steps rendering logic to avoid doing all of it
inside a template function. inside a template function.

12
cmd.go Normal file
View file

@ -0,0 +1,12 @@
package main
type generateCmd struct {
Path string `arg:"" help:"Path to the directory with the recipes."`
Output string `default:"dist" help:"Path to the directory where the files will be generated."`
}
var cli struct {
Debug bool `help:"Print debug information."`
Generate generateCmd `cmd:"" help:"Generate the web files for a directory of recipes."`
}

5
go.mod
View file

@ -2,4 +2,7 @@ module git.ctrlz.es/sandwitchboard
go 1.22.3 go 1.22.3
require github.com/aquilax/cooklang-go v0.1.7 require (
github.com/alecthomas/kong v0.9.0
github.com/aquilax/cooklang-go v0.1.7
)

8
go.sum
View file

@ -1,2 +1,10 @@
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA=
github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/aquilax/cooklang-go v0.1.7 h1:ynbGKI3o060m9NTlxnc1k7unjGOtJcfbxKWfiIOSqVo= github.com/aquilax/cooklang-go v0.1.7 h1:ynbGKI3o060m9NTlxnc1k7unjGOtJcfbxKWfiIOSqVo=
github.com/aquilax/cooklang-go v0.1.7/go.mod h1:1/65y8LN2Nhr6WNVh64ZqggvZmeymQHKavxUONeJmTk= github.com/aquilax/cooklang-go v0.1.7/go.mod h1:1/65y8LN2Nhr6WNVh64ZqggvZmeymQHKavxUONeJmTk=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=

View file

@ -3,7 +3,6 @@ package main
import ( import (
"bytes" "bytes"
"embed" "embed"
"flag"
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
@ -13,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/alecthomas/kong"
"github.com/aquilax/cooklang-go" "github.com/aquilax/cooklang-go"
) )
@ -128,38 +128,41 @@ func copyFile(srcpath, dstpath string) error {
} }
func main() { func main() {
recipesFlag := flag.String("recipes", "recipes", "Path to the directory with the recipes.") ctx := kong.Parse(&cli)
outputFlag := flag.String("output", "dist", "Path to the directory where the files will be generated.") if cli.Debug {
debugFlag := flag.Bool("debug", false, "Enables debug information.")
flag.Parse()
if *debugFlag {
slog.SetLogLoggerLevel(slog.LevelDebug) slog.SetLogLoggerLevel(slog.LevelDebug)
} }
fi, err := os.Stat(*outputFlag) if err := ctx.Run(); err != nil {
fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(1)
}
}
func (g *generateCmd) Run() error {
fi, err := os.Stat(g.Output)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
panic(err) return fmt.Errorf("cannot stat %q: %w", g.Output, err)
} }
if !os.IsNotExist(err) && !fi.IsDir() { if !os.IsNotExist(err) && !fi.IsDir() {
panic(fmt.Errorf("path %q is not a directory", *outputFlag)) return fmt.Errorf("path %q is not a directory", g.Output)
} else { } else {
if err := os.RemoveAll(*outputFlag); err != nil { if err := os.RemoveAll(g.Output); err != nil {
panic(err) return fmt.Errorf("cannot remove %q: %w", g.Output, err)
} }
} }
if err := os.Mkdir(*outputFlag, 0755); err != nil { if err := os.Mkdir(g.Output, 0755); err != nil {
panic(err) return fmt.Errorf("cannot create directory on path %q: %w", g.Output, err)
} }
walkErr := filepath.WalkDir(*recipesFlag, func(path string, d fs.DirEntry, err error) error { walkErr := filepath.WalkDir(g.Path, func(path string, d fs.DirEntry, err error) error {
slog.Debug("Walking through file", "path", path) slog.Debug("Walking through file", "path", path)
relpath := filepath.Join(strings.Split(path, "/")[1:]...) relpath := filepath.Join(strings.Split(path, "/")[1:]...)
ext := filepath.Ext(d.Name()) ext := filepath.Ext(d.Name())
recipeName := strings.TrimSuffix(d.Name(), ext) recipeName := strings.TrimSuffix(d.Name(), ext)
if d.IsDir() { if d.IsDir() {
dirpath := filepath.Join(*outputFlag, relpath) dirpath := filepath.Join(g.Output, relpath)
slog.Debug("Directory found, creating output dir", "path", path, "dirpath", dirpath) slog.Debug("Directory found, creating output dir", "path", path, "dirpath", dirpath)
if err := os.MkdirAll(dirpath, 0755); err != nil { if err := os.MkdirAll(dirpath, 0755); err != nil {
return fmt.Errorf("cannot create directory %s: %w", dirpath, err) return fmt.Errorf("cannot create directory %s: %w", dirpath, err)
@ -168,7 +171,7 @@ func main() {
} }
if ext != ".cook" { if ext != ".cook" {
dstPath := filepath.Join(*outputFlag, relpath) dstPath := filepath.Join(g.Output, relpath)
slog.Debug("Non-recipe file found, copying", "src", path, "dst", dstPath) slog.Debug("Non-recipe file found, copying", "src", path, "dst", dstPath)
if err := copyFile(path, dstPath); err != nil { if err := copyFile(path, dstPath); err != nil {
return fmt.Errorf("cannot copy file from %q to %q: %w", path, dstPath, err) return fmt.Errorf("cannot copy file from %q to %q: %w", path, dstPath, err)
@ -180,7 +183,7 @@ func main() {
slog.Debug("Parsing file", "name", path) slog.Debug("Parsing file", "name", path)
recipe, err := cooklang.ParseFile(path) recipe, err := cooklang.ParseFile(path)
if err != nil { if err != nil {
panic(err) return fmt.Errorf("cannot parse file %q: %w", path, err)
} }
if len(recipe.Steps) == 0 { if len(recipe.Steps) == 0 {
@ -189,17 +192,19 @@ func main() {
} }
slog.Debug("Parsed file", "path", path, "steps", len(recipe.Steps)) slog.Debug("Parsed file", "path", path, "steps", len(recipe.Steps))
recipeDistPath := filepath.Join(*outputFlag, filepath.Dir(relpath), fmt.Sprintf("%s.html", recipeName)) recipeDistPath := filepath.Join(g.Output, filepath.Dir(relpath), fmt.Sprintf("%s.html", recipeName))
data := map[string]any{"name": recipeName, "recipe": recipe} data := map[string]any{"name": recipeName, "recipe": recipe}
slog.Debug("Executing template", "recipeName", recipeName, "recipeWebPath", recipeDistPath) slog.Debug("Executing template", "recipeName", recipeName, "recipeWebPath", recipeDistPath)
if err := executeTemplateToFile("recipe", data, recipeDistPath); err != nil { if err := executeTemplateToFile("recipe", data, recipeDistPath); err != nil {
panic(err) return fmt.Errorf("cannot execute template \"recipe\" to file %q: %w", recipeDistPath, err)
} }
return nil return nil
}) })
if walkErr != nil { if walkErr != nil {
panic(walkErr) return fmt.Errorf("error while walking directory %q: %w", g.Path, walkErr)
} }
return nil
} }