mirror of https://github.com/dirtbags/moth.git
transpiler can now create mothballs
This commit is contained in:
parent
4eb0f0a141
commit
854ef771b4
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -171,11 +172,12 @@ func (h *HTTPServer) MothballerHandler(mh MothRequestHandler, w http.ResponseWri
|
|||
// parts[0] == "mothballer"
|
||||
filename := parts[1]
|
||||
cat := strings.TrimSuffix(filename, ".mb")
|
||||
mothball, err := mh.Mothball(cat)
|
||||
if err != nil {
|
||||
mb := new(bytes.Buffer)
|
||||
if err := mh.Mothball(cat, mb); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeContent(w, req, filename, time.Now(), mothball)
|
||||
mbReader := bytes.NewReader(mb.Bytes())
|
||||
http.ServeContent(w, req, filename, time.Now(), mbReader)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -174,8 +173,8 @@ func (m *Mothballs) refresh() {
|
|||
}
|
||||
|
||||
// Mothball just returns an error
|
||||
func (m *Mothballs) Mothball(cat string) (*bytes.Reader, error) {
|
||||
return nil, fmt.Errorf("Can't repackage a compiled mothball")
|
||||
func (m *Mothballs) Mothball(cat string, w io.Writer) error {
|
||||
return fmt.Errorf("Refusing to repackage a compiled mothball")
|
||||
}
|
||||
|
||||
// Maintain performs housekeeping for Mothballs.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
@ -42,7 +41,7 @@ type PuzzleProvider interface {
|
|||
Open(cat string, points int, path string) (ReadSeekCloser, time.Time, error)
|
||||
Inventory() []Category
|
||||
CheckAnswer(cat string, points int, answer string) (bool, error)
|
||||
Mothball(cat string) (*bytes.Reader, error)
|
||||
Mothball(cat string, w io.Writer) error
|
||||
Maintainer
|
||||
}
|
||||
|
||||
|
@ -233,14 +232,16 @@ func (mh *MothRequestHandler) ExportState() *StateExport {
|
|||
}
|
||||
|
||||
// Mothball generates a mothball for the given category.
|
||||
func (mh *MothRequestHandler) Mothball(cat string) (r *bytes.Reader, err error) {
|
||||
func (mh *MothRequestHandler) Mothball(cat string, w io.Writer) error {
|
||||
var err error
|
||||
|
||||
if !mh.Config.Devel {
|
||||
return nil, fmt.Errorf("Cannot mothball in production mode")
|
||||
return fmt.Errorf("Cannot mothball in production mode")
|
||||
}
|
||||
for _, provider := range mh.PuzzleProviders {
|
||||
if r, err = provider.Mothball(cat); err == nil {
|
||||
return r, nil
|
||||
if err = provider.Mothball(cat, w); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -70,9 +70,9 @@ func (p TranspilerProvider) CheckAnswer(cat string, points int, answer string) (
|
|||
}
|
||||
|
||||
// Mothball packages up a category into a mothball.
|
||||
func (p TranspilerProvider) Mothball(cat string) (*bytes.Reader, error) {
|
||||
func (p TranspilerProvider) Mothball(cat string, w io.Writer) error {
|
||||
c := transpile.NewFsCategory(p.fs, cat)
|
||||
return transpile.Mothball(c)
|
||||
return transpile.Mothball(c, w)
|
||||
}
|
||||
|
||||
// Maintain performs housekeeping.
|
||||
|
|
|
@ -21,6 +21,8 @@ type T struct {
|
|||
Args []string
|
||||
BaseFs afero.Fs
|
||||
fs afero.Fs
|
||||
|
||||
// Arguments
|
||||
filename string
|
||||
answer string
|
||||
}
|
||||
|
@ -51,19 +53,21 @@ func (t *T) ParseArgs() (Command, error) {
|
|||
}
|
||||
|
||||
flags := flag.NewFlagSet(t.Args[1], flag.ContinueOnError)
|
||||
flags.SetOutput(t.Stderr)
|
||||
directory := flags.String("dir", "", "Work directory")
|
||||
|
||||
switch t.Args[1] {
|
||||
case "mothball":
|
||||
cmd = t.DumpMothball
|
||||
flags.StringVar(&t.filename, "out", "", "Path to create mothball (empty for stdout)")
|
||||
case "inventory":
|
||||
cmd = t.PrintInventory
|
||||
case "open":
|
||||
flags.StringVar(&t.filename, "file", "puzzle.json", "Filename to open")
|
||||
cmd = t.DumpFile
|
||||
flags.StringVar(&t.filename, "file", "puzzle.json", "Filename to open")
|
||||
case "answer":
|
||||
flags.StringVar(&t.answer, "answer", "", "Answer to check")
|
||||
cmd = t.CheckAnswer
|
||||
flags.StringVar(&t.answer, "answer", "", "Answer to check")
|
||||
case "help":
|
||||
usage(t.Stderr)
|
||||
return nothing, nil
|
||||
|
@ -73,7 +77,6 @@ func (t *T) ParseArgs() (Command, error) {
|
|||
return nothing, fmt.Errorf("Invalid command")
|
||||
}
|
||||
|
||||
flags.SetOutput(t.Stderr)
|
||||
if err := flags.Parse(t.Args[2:]); err != nil {
|
||||
return nothing, err
|
||||
}
|
||||
|
@ -140,14 +143,24 @@ func (t *T) DumpFile() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DumpMothball writes a mothball to the writer.
|
||||
// DumpMothball writes a mothball to the writer, or an output file if specified.
|
||||
func (t *T) DumpMothball() error {
|
||||
var w io.Writer
|
||||
|
||||
c := transpile.NewFsCategory(t.fs, "")
|
||||
mb, err := transpile.Mothball(c)
|
||||
if t.filename == "" {
|
||||
w = t.Stdout
|
||||
} else {
|
||||
log.Println("Writing to", t.filename, t.fs)
|
||||
outf, err := t.BaseFs.Create(t.filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(t.Stdout, mb); err != nil {
|
||||
defer outf.Close()
|
||||
w = outf
|
||||
}
|
||||
log.Println(t.fs)
|
||||
if err := transpile.Mothball(c, w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -165,8 +178,6 @@ func (t *T) CheckAnswer() error {
|
|||
}
|
||||
|
||||
func main() {
|
||||
// XXX: Convert puzzle.py to standalone thingies
|
||||
|
||||
t := &T{
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/dirtbags/moth/pkg/transpile"
|
||||
|
@ -83,16 +85,72 @@ func TestEverything(t *testing.T) {
|
|||
if stdout.String() != "Moo." {
|
||||
t.Error("Wrong file pulled", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMothballs(t *testing.T) {
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
tp := T{
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
BaseFs: newTestFs(),
|
||||
}
|
||||
|
||||
stdout.Reset()
|
||||
if err := tp.Run("mothball", "-dir=unbroken"); err != nil {
|
||||
t.Log(tp.fs)
|
||||
if err := tp.Run("mothball", "-dir=unbroken", "-out=unbroken.mb"); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// afero.WriteFile(tp.BaseFs, "unbroken.mb", []byte("moo"), 0644)
|
||||
fis, err := afero.ReadDir(tp.BaseFs, "/")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if stdout.Len() < 200 {
|
||||
t.Error("That's way too short to be a mothball")
|
||||
for _, fi := range fis {
|
||||
t.Log(fi.Name())
|
||||
}
|
||||
|
||||
mb, err := tp.BaseFs.Open("unbroken.mb")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer mb.Close()
|
||||
|
||||
info, err := mb.Stat()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
zmb, err := zip.NewReader(mb, info.Size())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
for _, zf := range zmb.File {
|
||||
f, err := zf.Open()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer f.Close()
|
||||
buf, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch zf.Name {
|
||||
case "answers.txt":
|
||||
if len(buf) == 0 {
|
||||
t.Error("answers.txt empty")
|
||||
}
|
||||
case "puzzles.txt":
|
||||
if len(buf) == 0 {
|
||||
t.Error("puzzles.txt empty")
|
||||
}
|
||||
}
|
||||
if stdout.String()[:2] != "PK" {
|
||||
t.Error("This mothball isn't a zip file!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,23 +10,16 @@ import (
|
|||
)
|
||||
|
||||
// Mothball packages a Category up for a production server run.
|
||||
func Mothball(c Category) (*bytes.Reader, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
zf := zip.NewWriter(buf)
|
||||
func Mothball(c Category, w io.Writer) error {
|
||||
zf := zip.NewWriter(w)
|
||||
|
||||
inv, err := c.Inventory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
puzzlesTxt, err := zf.Create("puzzles.txt")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
answersTxt, err := zf.Create("answers.txt")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
puzzlesTxt := new(bytes.Buffer)
|
||||
answersTxt := new(bytes.Buffer)
|
||||
|
||||
for _, points := range inv {
|
||||
fmt.Fprintln(puzzlesTxt, points)
|
||||
|
@ -34,11 +27,11 @@ func Mothball(c Category) (*bytes.Reader, error) {
|
|||
puzzlePath := fmt.Sprintf("%d/puzzle.json", points)
|
||||
pw, err := zf.Create(puzzlePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
puzzle, err := c.Puzzle(points)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Puzzle %d: %s", points, err)
|
||||
return fmt.Errorf("Puzzle %d: %s", points, err)
|
||||
}
|
||||
|
||||
// Record answers in answers.txt
|
||||
|
@ -55,7 +48,7 @@ func Mothball(c Category) (*bytes.Reader, error) {
|
|||
// Write out Puzzle object
|
||||
penc := json.NewEncoder(pw)
|
||||
if err := penc.Encode(puzzle); err != nil {
|
||||
return nil, fmt.Errorf("Puzzle %d: %s", points, err)
|
||||
return fmt.Errorf("Puzzle %d: %s", points, err)
|
||||
}
|
||||
|
||||
// Write out all attachments and scripts
|
||||
|
@ -64,20 +57,33 @@ func Mothball(c Category) (*bytes.Reader, error) {
|
|||
attPath := fmt.Sprintf("%d/%s", points, att)
|
||||
aw, err := zf.Create(attPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
ar, err := c.Open(points, att)
|
||||
if exerr, ok := err.(*exec.ExitError); ok {
|
||||
return nil, fmt.Errorf("Puzzle %d: %s: %s: %s", points, att, err, string(exerr.Stderr))
|
||||
return fmt.Errorf("Puzzle %d: %s: %s: %s", points, att, err, string(exerr.Stderr))
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Puzzle %d: %s: %s", points, att, err)
|
||||
return fmt.Errorf("Puzzle %d: %s: %s", points, att, err)
|
||||
}
|
||||
if _, err := io.Copy(aw, ar); err != nil {
|
||||
return nil, fmt.Errorf("Puzzle %d: %s: %s", points, att, err)
|
||||
return fmt.Errorf("Puzzle %d: %s: %s", points, att, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pf, err := zf.Create("puzzles.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
puzzlesTxt.WriteTo(pf)
|
||||
|
||||
af, err := zf.Create("answers.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
answersTxt.WriteTo(af)
|
||||
|
||||
zf.Close()
|
||||
|
||||
return bytes.NewReader(buf.Bytes()), nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package transpile
|
|||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -14,7 +15,8 @@ import (
|
|||
|
||||
func TestMothballsMemFs(t *testing.T) {
|
||||
static := NewFsCategory(newTestFs(), "cat1")
|
||||
if _, err := Mothball(static); err != nil {
|
||||
mb := new(bytes.Buffer)
|
||||
if err := Mothball(static, mb); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +27,15 @@ func TestMothballsOsFs(t *testing.T) {
|
|||
|
||||
fs := NewRecursiveBasePathFs(afero.NewOsFs(), "testdata")
|
||||
static := NewFsCategory(fs, "static")
|
||||
mb, err := Mothball(static)
|
||||
mb := new(bytes.Buffer)
|
||||
err := Mothball(static, mb)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
mbr, err := zip.NewReader(mb, int64(mb.Len()))
|
||||
mbReader := bytes.NewReader(mb.Bytes())
|
||||
mbr, err := zip.NewReader(mbReader, int64(mb.Len()))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -43,7 +47,7 @@ func TestMothballsOsFs(t *testing.T) {
|
|||
defer f.Close()
|
||||
if buf, err := ioutil.ReadAll(f); err != nil {
|
||||
t.Error(err)
|
||||
} else if string(buf) != "" {
|
||||
} else if string(buf) != "1\n2\n3\n" {
|
||||
t.Error("Bad puzzles.txt", string(buf))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue