2019-08-25 18:50:38 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-08-28 17:41:17 -06:00
|
|
|
"flag"
|
2019-08-25 18:50:38 -06:00
|
|
|
"fmt"
|
2020-08-28 17:41:17 -06:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/GoBike/envflag"
|
|
|
|
"github.com/spf13/afero"
|
2019-08-25 18:50:38 -06:00
|
|
|
)
|
|
|
|
|
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
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
|
|
|
|
2020-08-28 17:41:17 -06:00
|
|
|
// NewCategory returns a new Category as specified by cat.
|
|
|
|
func (t *T) NewCategory(cat string) Category {
|
|
|
|
return NewCategory(t.Fs, cat)
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
|
|
|
|
2020-08-28 17:41:17 -06:00
|
|
|
// ParseArgs parses command-line arguments into T, returning the action to take
|
|
|
|
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")
|
|
|
|
envflag.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)
|
2019-08-25 18:50:38 -06:00
|
|
|
|
2020-08-28 17:41:17 -06:00
|
|
|
return *action
|
|
|
|
}
|
2019-08-25 18:50:38 -06:00
|
|
|
|
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()
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unimplemented action: %s", action)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrintInventory prints a puzzle inventory to stdout
|
|
|
|
func (t *T) PrintInventory() error {
|
|
|
|
dirEnts, err := afero.ReadDir(t.Fs, ".")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, ent := range dirEnts {
|
|
|
|
if ent.IsDir() {
|
|
|
|
c := t.NewCategory(ent.Name())
|
|
|
|
if puzzles, err := c.Puzzles(); err != nil {
|
2019-08-25 18:50:38 -06:00
|
|
|
log.Print(err)
|
|
|
|
continue
|
2020-08-28 17:41:17 -06:00
|
|
|
} else {
|
|
|
|
fmt.Fprint(t.w, ent.Name())
|
|
|
|
sort.Ints(puzzles)
|
|
|
|
for _, points := range puzzles {
|
|
|
|
fmt.Fprint(t.w, " ")
|
|
|
|
fmt.Fprint(t.w, points)
|
|
|
|
}
|
|
|
|
fmt.Fprintln(t.w)
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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)
|
|
|
|
p, err := c.Puzzle(t.Points)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t.Filename {
|
|
|
|
case "puzzle.json", "":
|
|
|
|
jp, err := json.Marshal(p)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
t.w.Write(jp)
|
|
|
|
default:
|
|
|
|
f, err := p.Open(t.Filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if _, err := io.Copy(t.w, f); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2019-08-25 18:50:38 -06:00
|
|
|
}
|