format command line command
This commit is contained in:
parent
76ebeb0948
commit
9d65878302
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.coverprofile
|
.coverprofile
|
||||||
.coverprofile-cmd
|
.coverprofile-cmd
|
||||||
.build
|
.build
|
||||||
|
treerack.test
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ sources = $(shell find . -name '*.go' \
|
|||||||
| grep -v .build/headexported.go \
|
| grep -v .build/headexported.go \
|
||||||
| grep -v internal/self/self.go \
|
| grep -v internal/self/self.go \
|
||||||
| grep -v .build/self.go)
|
| grep -v .build/self.go)
|
||||||
parsers = $(shell find . -name '*.treerack')
|
parsers = $(shell find . -name '*.treerack' | grep -v baz_test[.]treerack)
|
||||||
release_date = $(shell git show -s --format=%cs HEAD)
|
release_date = $(shell git show -s --format=%cs HEAD)
|
||||||
version = $(release_date)-$(shell git rev-parse --short HEAD)
|
version = $(release_date)-$(shell git rev-parse --short HEAD)
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
|
|||||||
2
cmd/treerack/baz_test.treerack
Normal file
2
cmd/treerack/baz_test.treerack
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# invalid comment
|
||||||
|
a = "42";
|
||||||
@ -21,6 +21,16 @@ func init() {
|
|||||||
docreflect.Register("main.errInvalidFilename", "")
|
docreflect.Register("main.errInvalidFilename", "")
|
||||||
docreflect.Register("main.errMultipleInputs", "")
|
docreflect.Register("main.errMultipleInputs", "")
|
||||||
docreflect.Register("main.errNoInput", "")
|
docreflect.Register("main.errNoInput", "")
|
||||||
|
docreflect.Register("main.format", "format input syntax. Accepts syntax from one or more files, inline syntax string or stdin. Use the --in-place\noption, when formatting files in place, or print the formatted syntax to stdout.\n\nfunc(o, stdin, stdout, syntax)")
|
||||||
|
docreflect.Register("main.formatFile", "\nfunc(name, inPlace, out)")
|
||||||
|
docreflect.Register("main.formatFiles", "\nfunc(files, inPlace, out)")
|
||||||
|
docreflect.Register("main.formatInline", "\nfunc(syntax, out)")
|
||||||
|
docreflect.Register("main.formatOptions", "")
|
||||||
|
docreflect.Register("main.formatOptions.InPlace", "")
|
||||||
|
docreflect.Register("main.formatOptions.Syntax", "")
|
||||||
|
docreflect.Register("main.formatOptions.SyntaxString", "")
|
||||||
|
docreflect.Register("main.formatStdin", "\nfunc(in, out)")
|
||||||
|
docreflect.Register("main.formatSyntax", "\nfunc(in, out)")
|
||||||
docreflect.Register("main.generate", "generate generates Go code that can parse arbitrary input with the provided syntax, and can be used embedded\nin an application.\n\nThe syntax may be provided via a file path (using an option or a positional argument), an\ninline string, or piped from standard input.\n\nfunc(o, stdin, stdout, args)")
|
docreflect.Register("main.generate", "generate generates Go code that can parse arbitrary input with the provided syntax, and can be used embedded\nin an application.\n\nThe syntax may be provided via a file path (using an option or a positional argument), an\ninline string, or piped from standard input.\n\nfunc(o, stdin, stdout, args)")
|
||||||
docreflect.Register("main.generateOptions", "")
|
docreflect.Register("main.generateOptions", "")
|
||||||
docreflect.Register("main.generateOptions.Export", "Export determines whether the generated parse function is exported (visible outside its package).\n")
|
docreflect.Register("main.generateOptions.Export", "Export determines whether the generated parse function is exported (visible outside its package).\n")
|
||||||
|
|||||||
116
cmd/treerack/format.go
Normal file
116
cmd/treerack/format.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"code.squareroundforest.org/arpio/treerack"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type formatOptions struct {
|
||||||
|
InPlace bool
|
||||||
|
SyntaxString *string
|
||||||
|
Syntax []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSyntax(in io.Reader, out io.Writer) error {
|
||||||
|
s := new(treerack.Syntax)
|
||||||
|
if err := s.ReadSyntax(in); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resetter, ok := out.(interface{ Reset() }); ok {
|
||||||
|
resetter.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Format(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFile(name string, inPlace bool, out io.Writer) error {
|
||||||
|
var (
|
||||||
|
inBytes []byte
|
||||||
|
buf *bytes.Buffer
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if inBytes, err = os.ReadFile(name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = bytes.NewBuffer(inBytes)
|
||||||
|
if err = formatSyntax(buf, buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !inPlace {
|
||||||
|
_, err = io.Copy(out, buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(buf.Bytes(), inBytes) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stderr, name)
|
||||||
|
return os.WriteFile(name, buf.Bytes(), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFiles(files []string, inPlace bool, out io.Writer) error {
|
||||||
|
for _, f := range files {
|
||||||
|
if err := formatFile(f, inPlace, out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatInline(syntax string, out io.Writer) error {
|
||||||
|
buf := bytes.NewBufferString(syntax)
|
||||||
|
return formatSyntax(buf, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatStdin(in io.Reader, out io.Writer) error {
|
||||||
|
return formatSyntax(in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// format input syntax. Accepts syntax from one or more files, inline syntax string or stdin. Use the --in-place
|
||||||
|
// option, when formatting files in place, or print the formatted syntax to stdout.
|
||||||
|
func format(o formatOptions, stdin io.Reader, stdout io.Writer, syntax ...string) error {
|
||||||
|
files := make([]string, 0, len(o.Syntax)+len(syntax))
|
||||||
|
files = append(files, o.Syntax...)
|
||||||
|
files = append(files, syntax...)
|
||||||
|
if o.SyntaxString != nil {
|
||||||
|
if len(files) > 0 {
|
||||||
|
return errors.New(
|
||||||
|
"accepted input: either inline syntax, or one or more syntax files, or stdin",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.InPlace {
|
||||||
|
if o.SyntaxString != nil {
|
||||||
|
return errors.New("cannot format inline syntax in place")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
return errors.New("cannot format stdin in place")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) > 0 {
|
||||||
|
return formatFiles(files, o.InPlace, stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.SyntaxString != nil {
|
||||||
|
return formatInline(*o.SyntaxString, stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSyntax(stdin, stdout)
|
||||||
|
}
|
||||||
162
cmd/treerack/format_test.go
Normal file
162
cmd/treerack/format_test.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormat(t *testing.T) {
|
||||||
|
t.Run("syntax string and file from option", func(t *testing.T) {
|
||||||
|
o := formatOptions{
|
||||||
|
SyntaxString: ptrto(`a = "42"`),
|
||||||
|
Syntax: []string{"foo_test.treerack"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := format(o, bytes.NewBuffer(nil), bytes.NewBuffer(nil)); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("syntax string and file from arg", func(t *testing.T) {
|
||||||
|
o := formatOptions{SyntaxString: ptrto(`a = "42"`)}
|
||||||
|
if err := format(o, bytes.NewBuffer(nil), bytes.NewBuffer(nil), "foo_test.treerack"); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("syntax string in place", func(t *testing.T) {
|
||||||
|
o := formatOptions{
|
||||||
|
SyntaxString: ptrto(`a = "42"`),
|
||||||
|
InPlace: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := format(o, bytes.NewBuffer(nil), bytes.NewBuffer(nil)); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stdin in place", func(t *testing.T) {
|
||||||
|
o := formatOptions{InPlace: true}
|
||||||
|
if err := format(o, bytes.NewBuffer(nil), bytes.NewBuffer(nil)); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("files:", func(t *testing.T) {
|
||||||
|
t.Run("read error", func(t *testing.T) {
|
||||||
|
if err := format(
|
||||||
|
formatOptions{},
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
"bar_test.treerack",
|
||||||
|
); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("syntax error", func(t *testing.T) {
|
||||||
|
if err := format(
|
||||||
|
formatOptions{},
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
"baz_test.treerack",
|
||||||
|
); err == nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("to stdout", func(t *testing.T) {
|
||||||
|
stdout := bytes.NewBuffer(nil)
|
||||||
|
if err := format(
|
||||||
|
formatOptions{},
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
stdout,
|
||||||
|
"foo_test.treerack",
|
||||||
|
); err != nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout.Len() == 0 {
|
||||||
|
t.Fatal("failed to write out contents")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("in place no change", func(t *testing.T) {
|
||||||
|
c, err := os.ReadFile("foo_test.treerack")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := formatOptions{InPlace: true}
|
||||||
|
if err := format(
|
||||||
|
o,
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
"foo_test.treerack",
|
||||||
|
); err != nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := os.ReadFile("foo_test.treerack")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(cc, c) {
|
||||||
|
t.Fatal("failed to leave the file intact")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("in place", func(t *testing.T) {
|
||||||
|
c, err := os.ReadFile("foo_test.treerack")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cc := c[:len(c)-1]
|
||||||
|
if err := os.WriteFile("foo_test.treerack", cc, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := formatOptions{InPlace: true}
|
||||||
|
if err := format(
|
||||||
|
o,
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
bytes.NewBuffer(nil),
|
||||||
|
"foo_test.treerack",
|
||||||
|
); err != nil {
|
||||||
|
t.Fatal("failed to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
ccc, err := os.ReadFile("foo_test.treerack")
|
||||||
|
if !bytes.Equal(ccc, c) {
|
||||||
|
t.Fatal("failed to format file")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("syntax string", func(t *testing.T) {
|
||||||
|
o := formatOptions{SyntaxString: ptrto(`a="42"`)}
|
||||||
|
stdout := bytes.NewBuffer(nil)
|
||||||
|
if err := format(o, bytes.NewBuffer(nil), stdout); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout.String() != `a = "42";`+"\n" {
|
||||||
|
t.Fatal("failed to format syntax string")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("stdin", func(t *testing.T) {
|
||||||
|
stdin := bytes.NewBufferString(`a="42"`)
|
||||||
|
stdout := bytes.NewBuffer(nil)
|
||||||
|
if err := format(formatOptions{}, stdin, stdout); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stdout.String() != `a = "42";`+"\n" {
|
||||||
|
t.Fatal("failed to format input")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -9,5 +9,6 @@ func main() {
|
|||||||
check := Args(Command("check", check), 0, 1)
|
check := Args(Command("check", check), 0, 1)
|
||||||
show := Args(Command("show", show), 0, 1)
|
show := Args(Command("show", show), 0, 1)
|
||||||
generate := Args(Command("generate", generate), 0, 1)
|
generate := Args(Command("generate", generate), 0, 1)
|
||||||
Exec(Version(Group("treerack", checkSyntax, check, show, generate), version))
|
format := Command("format", format)
|
||||||
|
Exec(Version(Group("treerack", checkSyntax, check, show, generate, format), version))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,6 +115,27 @@ piped from standard input.
|
|||||||
- --syntax-string string: specifies the syntax as an inline string.
|
- --syntax-string string: specifies the syntax as an inline string.
|
||||||
- --help: Show help.
|
- --help: Show help.
|
||||||
|
|
||||||
|
### treerack format
|
||||||
|
|
||||||
|
#### Synopsis:
|
||||||
|
|
||||||
|
```
|
||||||
|
treerack format [options]... [--] [syntax string]...
|
||||||
|
treerack format <subcommand>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Description:
|
||||||
|
|
||||||
|
input syntax. Accepts syntax from one or more files, inline syntax string or stdin. Use the --in-place option,
|
||||||
|
when formatting files in place, or print the formatted syntax to stdout.
|
||||||
|
|
||||||
|
#### Options:
|
||||||
|
|
||||||
|
- --in-place bool:
|
||||||
|
- --syntax string \[\*\]:
|
||||||
|
- --syntax-string string:
|
||||||
|
- --help: Show help.
|
||||||
|
|
||||||
### treerack version
|
### treerack version
|
||||||
|
|
||||||
Show version.
|
Show version.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user