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-cmd
|
||||
.build
|
||||
treerack.test
|
||||
|
||||
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ sources = $(shell find . -name '*.go' \
|
||||
| grep -v .build/headexported.go \
|
||||
| grep -v internal/self/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)
|
||||
version = $(release_date)-$(shell git rev-parse --short HEAD)
|
||||
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.errMultipleInputs", "")
|
||||
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.generateOptions", "")
|
||||
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)
|
||||
show := Args(Command("show", show), 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.
|
||||
- --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
|
||||
|
||||
Show version.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user