moth/cmd/transpile/main.go

228 lines
4.4 KiB
Go
Raw Normal View History

package main
import (
"encoding/json"
2020-08-28 17:41:17 -06:00
"flag"
"fmt"
2020-08-28 17:41:17 -06:00
"io"
2022-05-10 13:20:54 -06:00
"io/fs"
2020-08-28 17:41:17 -06:00
"log"
"os"
"sort"
2022-05-10 13:20:54 -06:00
"github.com/dirtbags/moth/pkg/namesubfs"
2020-09-08 17:49:02 -06:00
"github.com/dirtbags/moth/pkg/transpile"
)
2020-09-11 13:03:19 -06:00
// T represents the state of things
2020-08-28 17:41:17 -06:00
type T struct {
2021-02-25 15:56:23 -07:00
Stdin io.Reader
2020-10-12 17:44:44 -06:00
Stdout io.Writer
Stderr io.Writer
Args []string
2022-05-10 13:20:54 -06:00
BaseFs fs.FS
fs fs.FS
}
2020-09-11 13:03:19 -06:00
// Command is a function invoked by the user
type Command func() error
func nothing() error {
return nil
2020-08-28 17:41:17 -06:00
}
2020-09-16 17:54:29 -06:00
func usage(w io.Writer) {
fmt.Fprintln(w, " Usage: transpile mothball [FLAGS] [MOTHBALL]")
fmt.Fprintln(w, " Compile a mothball")
fmt.Fprintln(w, " Usage: inventory [FLAGS]")
fmt.Fprintln(w, " Show category inventory")
fmt.Fprintln(w, " Usage: puzzle [FLAGS]")
fmt.Fprintln(w, " Print puzzle JSON")
fmt.Fprintln(w, " Usage: file [FLAGS] FILENAME")
fmt.Fprintln(w, " Open a file for a puzzle")
fmt.Fprintln(w, " Usage: answer [FLAGS] ANSWER")
fmt.Fprintln(w, " Check correctness of an answer")
fmt.Fprintln(w, " Usage: markdown [FLAGS]")
fmt.Fprintln(w, " Format stdin with markdown")
2020-09-16 17:54:29 -06:00
fmt.Fprintln(w, "")
fmt.Fprintln(w, "-dir DIRECTORY")
fmt.Fprintln(w, " Use puzzle in DIRECTORY")
2020-09-16 17:54:29 -06:00
}
2020-09-11 13:03:19 -06:00
// ParseArgs parses arguments and runs the appropriate action.
func (t *T) ParseArgs() (Command, error) {
var cmd Command
if len(t.Args) == 1 {
2020-09-16 17:54:29 -06:00
usage(t.Stderr)
2020-09-11 13:03:19 -06:00
return nothing, nil
}
flags := flag.NewFlagSet(t.Args[1], flag.ContinueOnError)
2020-10-12 17:44:44 -06:00
flags.SetOutput(t.Stderr)
2020-09-11 13:03:19 -06:00
directory := flags.String("dir", "", "Work directory")
switch t.Args[1] {
case "mothball":
cmd = t.DumpMothball
2020-08-28 17:41:17 -06:00
case "inventory":
2020-09-11 13:03:19 -06:00
cmd = t.PrintInventory
case "puzzle":
cmd = t.DumpPuzzle
case "file":
2020-09-11 13:03:19 -06:00
cmd = t.DumpFile
case "answer":
cmd = t.CheckAnswer
2021-02-25 15:56:23 -07:00
case "markdown":
cmd = t.Markdown
2020-09-16 17:54:29 -06:00
case "help":
usage(t.Stderr)
return nothing, nil
2020-08-28 17:41:17 -06:00
default:
2020-09-18 12:52:04 -06:00
fmt.Fprintln(t.Stderr, "ERROR:", t.Args[1], "is not a valid command")
2020-09-16 17:54:29 -06:00
usage(t.Stderr)
2021-10-20 13:10:24 -06:00
return nothing, fmt.Errorf("invalid command")
2020-09-11 13:03:19 -06:00
}
if err := flags.Parse(t.Args[2:]); err != nil {
return nothing, err
2020-08-28 17:41:17 -06:00
}
2020-09-11 13:03:19 -06:00
if *directory != "" {
2022-05-10 13:20:54 -06:00
t.fs = namesubfs.Sub(t.BaseFs, *directory)
2020-09-11 13:03:19 -06:00
} else {
t.fs = t.BaseFs
}
t.Args = flags.Args()
2020-09-11 13:03:19 -06:00
return cmd, nil
2020-08-28 17:41:17 -06:00
}
// PrintInventory prints a puzzle inventory to stdout
func (t *T) PrintInventory() error {
c := transpile.NewFsCategory(t.fs, "")
inv, err := c.Inventory()
2020-08-28 17:41:17 -06:00
if err != nil {
return err
}
sort.Ints(inv)
jinv, err := json.Marshal(
transpile.InventoryResponse{
Puzzles: inv,
},
)
if err != nil {
return err
2020-09-04 15:29:06 -06:00
}
t.Stdout.Write(jinv)
2020-08-28 17:41:17 -06:00
return nil
}
// DumpPuzzle writes a puzzle's JSON to the writer.
func (t *T) DumpPuzzle() error {
2022-05-10 13:20:54 -06:00
puzzle, err := transpile.NewFsPuzzle(t.fs)
if err != nil {
return err
}
p, err := puzzle.Puzzle()
if err != nil {
return err
}
jp, err := json.Marshal(p)
if err != nil {
return err
}
t.Stdout.Write(jp)
return nil
}
2020-09-11 13:03:19 -06:00
// DumpFile writes a file to the writer.
func (t *T) DumpFile() error {
filename := "puzzle.json"
if len(t.Args) > 0 {
filename = t.Args[0]
}
2022-05-10 13:20:54 -06:00
puzzle, err := transpile.NewFsPuzzle(t.fs)
if err != nil {
return err
}
2020-08-28 17:41:17 -06:00
f, err := puzzle.Open(filename)
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(t.Stdout, f); err != nil {
return err
2020-08-28 17:41:17 -06:00
}
return nil
}
2020-10-12 17:44:44 -06:00
// DumpMothball writes a mothball to the writer, or an output file if specified.
2020-09-11 13:03:19 -06:00
func (t *T) DumpMothball() error {
2020-10-12 17:44:44 -06:00
var w io.Writer
2020-09-11 13:03:19 -06:00
c := transpile.NewFsCategory(t.fs, "")
filename := ""
if len(t.Args) == 0 {
2020-10-12 17:44:44 -06:00
w = t.Stdout
} else {
filename = t.Args[0]
2022-05-10 13:20:54 -06:00
outf, err := os.Create(filename)
2020-10-12 17:44:44 -06:00
if err != nil {
return err
}
defer outf.Close()
w = outf
log.Println("Writing mothball to", filename)
2020-09-04 18:28:23 -06:00
}
2020-10-12 17:44:44 -06:00
if err := transpile.Mothball(c, w); err != nil {
if filename != "" {
2022-05-10 13:20:54 -06:00
os.Remove(filename)
}
2020-09-04 18:28:23 -06:00
return err
}
return nil
}
2020-09-11 13:03:19 -06:00
// CheckAnswer prints whether an answer is correct.
func (t *T) CheckAnswer() error {
answer := ""
if len(t.Args) > 0 {
answer = t.Args[0]
}
2022-05-10 13:20:54 -06:00
c, err := transpile.NewFsPuzzle(t.fs)
if err != nil {
return err
}
_, err = fmt.Fprintf(t.Stdout, `{"Correct":%v}`, c.Answer(answer))
return err
2020-09-03 20:04:43 -06:00
}
2021-02-25 15:56:23 -07:00
// Markdown runs stdin through a Markdown engine
func (t *T) Markdown() error {
return transpile.Markdown(t.Stdin, t.Stdout)
}
2020-08-28 17:41:17 -06:00
func main() {
t := &T{
2021-02-25 16:26:11 -07:00
Stdin: os.Stdin,
2020-09-11 13:03:19 -06:00
Stdout: os.Stdout,
Stderr: os.Stderr,
Args: os.Args,
2022-05-10 13:20:54 -06:00
BaseFs: os.DirFS(""),
2020-09-11 13:03:19 -06:00
}
cmd, err := t.ParseArgs()
if err != nil {
log.Fatal(err)
2020-08-28 17:41:17 -06:00
}
2020-09-11 13:03:19 -06:00
if err := cmd(); err != nil {
2020-08-28 17:41:17 -06:00
log.Fatal(err)
}
}