moth/cmd/transpile/main.go

146 lines
3.0 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"
"log"
"os"
"sort"
2020-09-08 17:49:02 -06:00
"github.com/dirtbags/moth/pkg/transpile"
2020-08-28 17:41:17 -06:00
"github.com/spf13/afero"
)
2020-08-28 17:41:17 -06:00
// T contains everything required for a transpilation invocation (across the nation).
type T struct {
// What action to take
w io.Writer
Cat string
Points int
Answer string
Filename string
Fs afero.Fs
}
2020-09-08 17:49:02 -06:00
// ParseArgs parses command-line arguments into T, returning the action to take.
// BUG(neale): CLI arguments are not related to how the CLI will be used.
2020-08-28 17:41:17 -06:00
func (t *T) ParseArgs() string {
action := flag.String("action", "inventory", "Action to take: must be 'inventory', 'open', 'answer', or 'mothball'")
flag.StringVar(&t.Cat, "cat", "", "Puzzle category")
flag.IntVar(&t.Points, "points", 0, "Puzzle point value")
flag.StringVar(&t.Answer, "answer", "", "Answer to check for correctness, for 'answer' action")
flag.StringVar(&t.Filename, "filename", "", "Filename, for 'open' action")
basedir := flag.String("basedir", ".", "Base directory containing all puzzles")
2020-09-08 17:49:02 -06:00
flag.Parse()
2019-09-02 19:47:24 -06:00
2020-08-28 17:41:17 -06:00
osfs := afero.NewOsFs()
t.Fs = afero.NewBasePathFs(osfs, *basedir)
2020-08-28 17:41:17 -06:00
return *action
}
2020-08-28 17:41:17 -06:00
// Handle performs the requested action
func (t *T) Handle(action string) error {
switch action {
case "inventory":
return t.PrintInventory()
case "open":
return t.Open()
2020-09-04 18:28:23 -06:00
case "mothball":
return t.Mothball()
2020-08-28 17:41:17 -06:00
default:
return fmt.Errorf("Unimplemented action: %s", action)
}
}
// PrintInventory prints a puzzle inventory to stdout
func (t *T) PrintInventory() error {
2020-09-04 15:29:06 -06:00
inv := make(map[string][]int)
2020-08-28 17:41:17 -06:00
dirEnts, err := afero.ReadDir(t.Fs, ".")
if err != nil {
return err
}
for _, ent := range dirEnts {
if ent.IsDir() {
c := t.NewCategory(ent.Name())
2020-09-03 20:04:43 -06:00
if puzzles, err := c.Inventory(); err != nil {
log.Print(err)
continue
2020-08-28 17:41:17 -06:00
} else {
sort.Ints(puzzles)
2020-09-04 15:29:06 -06:00
inv[ent.Name()] = puzzles
}
}
}
2020-09-04 15:29:06 -06:00
m := json.NewEncoder(t.w)
if err := m.Encode(inv); err != nil {
return err
}
2020-08-28 17:41:17 -06:00
return nil
}
// Open writes a file to the writer.
func (t *T) Open() error {
c := t.NewCategory(t.Cat)
switch t.Filename {
case "puzzle.json", "":
2020-09-04 18:28:23 -06:00
// BUG(neale): we need a way to tell the transpiler to strip answers
2020-09-03 20:04:43 -06:00
p, err := c.Puzzle(t.Points)
2020-09-01 20:12:57 -06:00
if err != nil {
return err
}
2020-08-28 17:41:17 -06:00
jp, err := json.Marshal(p)
if err != nil {
return err
}
t.w.Write(jp)
default:
2020-09-03 20:04:43 -06:00
f, err := c.Open(t.Points, t.Filename)
2020-08-28 17:41:17 -06:00
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(t.w, f); err != nil {
return err
}
}
return nil
}
2020-09-04 18:28:23 -06:00
// Mothball writes a mothball to the writer.
func (t *T) Mothball() error {
c := t.NewCategory(t.Cat)
2020-09-08 17:49:02 -06:00
mb, err := transpile.Mothball(c)
2020-09-04 18:28:23 -06:00
if err != nil {
return err
}
if _, err := io.Copy(t.w, mb); err != nil {
return err
}
return nil
}
2020-09-04 13:00:23 -06:00
// NewCategory returns a new Fs-backed category.
2020-09-08 17:49:02 -06:00
func (t *T) NewCategory(name string) transpile.Category {
return transpile.NewFsCategory(t.Fs, name)
2020-09-03 20:04:43 -06:00
}
2020-08-28 17:41:17 -06:00
func main() {
// XXX: Convert puzzle.py to standalone thingies
t := &T{
w: os.Stdout,
}
action := t.ParseArgs()
if err := t.Handle(action); err != nil {
log.Fatal(err)
}
}