diff --git a/README.md b/README.md index 82f40c3..391428f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A simple CLI tool to generate a static website from a ## Roadmap - [X] Recursively traverse the `recipes` directory. +- [X] Add a proper CLI. - [ ] Add a default CSS file. - [ ] Improve the steps rendering logic to avoid doing all of it inside a template function. diff --git a/cmd.go b/cmd.go new file mode 100644 index 0000000..8df88dc --- /dev/null +++ b/cmd.go @@ -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."` +} diff --git a/go.mod b/go.mod index c7466ca..604ee45 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module git.ctrlz.es/sandwitchboard 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 +) diff --git a/go.sum b/go.sum index fabd8fb..f40ed2b 100644 --- a/go.sum +++ b/go.sum @@ -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/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= diff --git a/sandwitchboard.go b/sandwitchboard.go index f9771f8..5391d0a 100644 --- a/sandwitchboard.go +++ b/sandwitchboard.go @@ -3,7 +3,6 @@ package main import ( "bytes" "embed" - "flag" "fmt" "html/template" "io" @@ -13,6 +12,7 @@ import ( "path/filepath" "strings" + "github.com/alecthomas/kong" "github.com/aquilax/cooklang-go" ) @@ -128,38 +128,41 @@ func copyFile(srcpath, dstpath string) error { } func main() { - recipesFlag := flag.String("recipes", "recipes", "Path to the directory with the recipes.") - outputFlag := flag.String("output", "dist", "Path to the directory where the files will be generated.") - debugFlag := flag.Bool("debug", false, "Enables debug information.") - flag.Parse() - - if *debugFlag { + ctx := kong.Parse(&cli) + if cli.Debug { 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) { - panic(err) + return fmt.Errorf("cannot stat %q: %w", g.Output, err) } 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 { - if err := os.RemoveAll(*outputFlag); err != nil { - panic(err) + if err := os.RemoveAll(g.Output); err != nil { + return fmt.Errorf("cannot remove %q: %w", g.Output, err) } } - if err := os.Mkdir(*outputFlag, 0755); err != nil { - panic(err) + if err := os.Mkdir(g.Output, 0755); err != nil { + 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) relpath := filepath.Join(strings.Split(path, "/")[1:]...) ext := filepath.Ext(d.Name()) recipeName := strings.TrimSuffix(d.Name(), ext) 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) if err := os.MkdirAll(dirpath, 0755); err != nil { return fmt.Errorf("cannot create directory %s: %w", dirpath, err) @@ -168,7 +171,7 @@ func main() { } 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) if err := copyFile(path, dstPath); err != nil { 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) recipe, err := cooklang.ParseFile(path) if err != nil { - panic(err) + return fmt.Errorf("cannot parse file %q: %w", path, err) } if len(recipe.Steps) == 0 { @@ -189,17 +192,19 @@ func main() { } 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} slog.Debug("Executing template", "recipeName", recipeName, "recipeWebPath", recipeDistPath) 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 }) if walkErr != nil { - panic(walkErr) + return fmt.Errorf("error while walking directory %q: %w", g.Path, walkErr) } + + return nil }