Browse Source

Make a preprocessor

main
Amelia A 2 months ago
parent
commit
f2940bdec8
Signed by: amy GPG Key ID: 98E879BC3B6E1BF8
  1. 2
      .gitignore
  2. 2
      go.mod
  3. 48
      main.go
  4. 28
      main_test.go
  5. 1
      test_files/bad.txt.tmpl
  6. 0
      test_files/empty
  7. 10
      test_files/first.txt.tmpl
  8. 11
      test_files/many.txt.tmpl

2
.gitignore

@ -1 +1 @@
/bin/
bin/

2
go.mod

@ -1,3 +1,3 @@
module onlyhavecans.works/amy/fvv
go 1.15
go 1.16

48
main.go

@ -10,10 +10,9 @@ import (
)
const (
templateGlob = "*.tmpl"
exitFail = 1
usageFooter = `
Fusozay Var Var is a CLI application for quickly rendering out text templates.
exitFail = 1
usageFooter = `
Fusozay Var Var is a CLI pre-processor for rendering out golang txt templates.
I often write outfit and character descriptions that reuses a lot of elements.
This allows me to DRY up my descriptions and still quickly get results.
@ -23,44 +22,63 @@ Template requirements:
- The template you want to render must be "named"
https://golang.org/pkg/text/template/#hdr-Nested_template_definitions
Version 1.2.0
Version 2.0.0
`
)
func main() {
if err := run(os.Args, templateGlob, os.Stdout); err != nil {
if err := verifyCharDevice(os.Stdin); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(exitFail)
}
if err := run(os.Args, os.Stdin, os.Stdout); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(exitFail)
}
}
func verifyCharDevice(stdin *os.File) error {
fi, err := stdin.Stat() // get the FileInfo struct describing the standard input.
if err != nil {
return fmt.Errorf("doing stat on %p: %v", stdin, err)
}
if (fi.Mode() & os.ModeCharDevice) != 0 {
return errors.New("stdin was not passed to the app")
}
return nil
}
func run(args []string, fileGlob string, stdout io.Writer) error {
func run(args []string, stdin io.Reader, stdout io.Writer) error {
flags := flag.NewFlagSet(args[0], flag.ContinueOnError)
flags.Usage = func() {
fmt.Fprintf(flags.Output(), "Quick Usage: %s <template definition name>\n", args[0])
fmt.Fprintf(flags.Output(), "Quick Usage: %s < <template>\n", args[0])
flags.PrintDefaults()
fmt.Fprint(flags.Output(), usageFooter)
}
var (
glob = flags.String("t", fileGlob, "glob to use to find templates")
// glob = flags.String("t", fileGlob, "glob to use to find templates")
)
if err := flags.Parse(args[1:]); err != nil {
return err
}
if flags.NArg() != 1 {
if flags.NArg() != 0 {
flags.Usage()
return errors.New("template definition missing")
return errors.New("pass template in stdin")
}
nonFlagArg := flags.Args()[0]
f, err := io.ReadAll(stdin)
if err != nil {
return fmt.Errorf("reading from stdin: %v", err)
}
tmpl, err := template.ParseGlob(*glob)
tmpl, err := template.New("name").Parse(string(f))
if err != nil {
return fmt.Errorf("checking for templates: %v", err)
return fmt.Errorf("parsing template: %v", err)
}
err = tmpl.ExecuteTemplate(stdout, nonFlagArg, "no data needed")
err = tmpl.Execute(stdout, "no data needed")
if err != nil {
return fmt.Errorf("executing template %s: %v", args[1], err)
}

28
main_test.go

@ -2,13 +2,19 @@ package main
import (
"bytes"
"fmt"
"os"
"testing"
)
var (
testFiles = "test_files"
)
func TestRun(t *testing.T) {
type args struct {
args []string
fileGlob string
args []string
stdin string
}
var tests = []struct {
name string
@ -16,17 +22,21 @@ func TestRun(t *testing.T) {
wantStdout string
wantErr bool
}{
{"no args", args{[]string{"fvv"}, ""}, "", true},
{"missing template", args{[]string{"fvv", "t3"}, "*.tmpl"}, "", true},
{"definition does not exist", args{[]string{"fvv", "t20"}, "test_files/*.tmpl"}, "", true},
{"happy path", args{[]string{"fvv", "T3"}, "test_files/*.tmpl"}, "ONE\nStandard stuff\nTWO", false},
{"flag template", args{[]string{"fvv", "-t", "test_files/*.tmpl", "T3"}, "*.tmpl"}, "ONE\nStandard stuff\nTWO", false},
{"bad flag", args{[]string{"fvv", "-z", "test_files/*.tmpl"}, ""}, "", true},
{"no args no stdin", args{[]string{"fvv"}, "empty"}, "", true},
{"invalid template", args{[]string{"fvv"}, "bad.txt.tmpl"}, "", true},
{"bad flag", args{[]string{"fvv", "-z"}, "empty"}, "", true},
{"happy path", args{[]string{"fvv"}, "first.txt.tmpl"}, "ONE\nStandard stuff\nTWO\n", false},
{"multi path", args{[]string{"fvv"}, "many.txt.tmpl"}, "## Middle One\ntop\nmiddle_one\n\n## Middle Two\ntop\nmiddle_two\n", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stdout := &bytes.Buffer{}
err := run(tt.args.args, tt.args.fileGlob, stdout)
stdin, err := os.Open(fmt.Sprintf("%s/%s", testFiles, tt.args.stdin))
if err != nil {
t.Errorf("Failed opening test file %s: %v", tt.args.stdin, err)
}
err = run(tt.args.args, stdin, stdout)
if (err != nil) != tt.wantErr {
t.Errorf("run() error = %v, wantErr %v", err, tt.wantErr)
return

1
test_files/bad.txt.tmpl

@ -0,0 +1 @@
{{define "T1"}ONE{{end}}

0
test_files/empty

10
test_files/first.txt.tmpl

@ -1,6 +1,6 @@
{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}}
{{define "T1"}}ONE{{end -}}
{{define "T2"}}TWO{{end -}}
{{define "T3" -}}{{template "T1"}}
Standard stuff
{{template "T2"}}{{end}}
{{template "T3"}}
{{template "T2" -}}{{end -}}
{{template "T3"}}

11
test_files/many.txt.tmpl

@ -0,0 +1,11 @@
{{ define "top" -}}
top
{{end -}}
## Middle One
{{template "top" -}}
middle_one
## Middle Two
{{template "top" -}}
middle_two
Loading…
Cancel
Save