From de2e921d553df4aee02da68965d002ff28e19275 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Sun, 14 Aug 2016 13:50:18 +0200 Subject: [PATCH] TOML to JSON cli tool (#85) * Implement tomljson * Add note about tools in README --- README.md | 17 ++++++++ cmd/tomljson/main.go | 66 ++++++++++++++++++++++++++++++ cmd/tomljson/main_test.go | 84 +++++++++++++++++++++++++++++++++++++++ test.sh | 5 ++- 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 cmd/tomljson/main.go create mode 100644 cmd/tomljson/main_test.go diff --git a/README.md b/README.md index fa6d55e..162273d 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,23 @@ if err != nil { The documentation and additional examples are available at [godoc.org](http://godoc.org/github.com/pelletier/go-toml). +## Tools + +Go-toml provides two handy command line tools: + +* `tomll`: Reads TOML files and lint them. + + ``` + go install github.com/pelletier/go-toml/cmd/tomll + tomll --help + ``` +* `tomljson`: Reads a TOML file and outputs its JSON representation. + + ``` + go install github.com/pelletier/go-toml/cmd/tomjson + tomljson --help + ``` + ## Contribute Feel free to report bugs and patches using GitHub's pull requests system on diff --git a/cmd/tomljson/main.go b/cmd/tomljson/main.go new file mode 100644 index 0000000..809f688 --- /dev/null +++ b/cmd/tomljson/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "github.com/pelletier/go-toml" + "io" + "os" + "flag" + "fmt" + "encoding/json" +) + +func main() { + flag.Usage = func() { + fmt.Fprintln(os.Stderr, `tomljson can be used in two ways: +Writing to STDIN and reading from STDOUT: + cat file.toml | tomljson > file.json + +Reading from a file name: + tomljson file.toml +`) + } + flag.Parse() + os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr)) +} + +func processMain(files []string, defaultInput io.Reader, output io.Writer, errorOutput io.Writer) int { + // read from stdin and print to stdout + inputReader := defaultInput + + if len(files) > 0 { + var err error + inputReader, err = os.Open(files[0]) + if err != nil { + printError(err, errorOutput) + return -1 + } + } + s, err := reader(inputReader) + if err != nil { + printError(err, errorOutput) + return -1 + } + io.WriteString(output, s + "\n") + return 0 +} + +func printError(err error, output io.Writer) { + io.WriteString(output, err.Error() + "\n") +} + +func reader(r io.Reader) (string, error) { + tree, err := toml.LoadReader(r) + if err != nil { + return "", err + } + return mapToJson(tree) +} + +func mapToJson(tree *toml.TomlTree) (string, error) { + treeMap := tree.ToMap() + bytes, err := json.MarshalIndent(treeMap, "", " ") + if err != nil { + return "", err + } + return string(bytes[:]), nil +} \ No newline at end of file diff --git a/cmd/tomljson/main_test.go b/cmd/tomljson/main_test.go new file mode 100644 index 0000000..6260b2b --- /dev/null +++ b/cmd/tomljson/main_test.go @@ -0,0 +1,84 @@ +package main + +import ( + "testing" + "strings" + "bytes" + "os" + "io/ioutil" +) + +func expectBufferEquality(t *testing.T, name string, buffer *bytes.Buffer, expected string) { + output := buffer.String() + if output != expected { + t.Errorf("incorrect %s:\n%s\n\nexpected %s:\n%s", name, output, name, expected) + t.Log([]rune(output)) + t.Log([]rune(expected)) + } +} + +func expectProcessMainResults(t *testing.T, input string, args []string, exitCode int, expectedOutput string, expectedError string) { + inputReader := strings.NewReader(input) + outputBuffer := new(bytes.Buffer) + errorBuffer := new(bytes.Buffer) + + returnCode := processMain(args, inputReader, outputBuffer, errorBuffer) + + expectBufferEquality(t, "output", outputBuffer, expectedOutput) + expectBufferEquality(t, "error", errorBuffer, expectedError) + + if returnCode != exitCode { + t.Error("incorrect return code:", returnCode, "expected", exitCode) + } +} + + +func TestProcessMainReadFromStdin(t *testing.T) { + input := ` + [mytoml] + a = 42` + expectedOutput := `{ + "mytoml": { + "a": 42 + } +} +` + expectedError := `` + expectedExitCode := 0 + + expectProcessMainResults(t, input, []string{}, expectedExitCode, expectedOutput, expectedError) +} + +func TestProcessMainReadFromFile(t *testing.T) { + input := ` + [mytoml] + a = 42` + + + tmpfile, err := ioutil.TempFile("", "example.toml") + if err != nil { + t.Fatal(err) + } + if _, err := tmpfile.Write([]byte(input)); err != nil { + t.Fatal(err) + } + + defer os.Remove(tmpfile.Name()) + + expectedOutput := `{ + "mytoml": { + "a": 42 + } +} +` + expectedError := `` + expectedExitCode := 0 + + expectProcessMainResults(t, ``, []string{tmpfile.Name()}, expectedExitCode, expectedOutput, expectedError) +} + +func TestProcessMainReadFromMissingFile(t *testing.T) { + expectedError := `open /this/file/does/not/exist: no such file or directory +` + expectProcessMainResults(t, ``, []string{"/this/file/does/not/exist"}, -1, ``, expectedError) +} \ No newline at end of file diff --git a/test.sh b/test.sh index 33ae6df..15ac1e1 100755 --- a/test.sh +++ b/test.sh @@ -34,11 +34,12 @@ go build -o toml-test github.com/BurntSushi/toml-test # NOTE: this basically mocks an install without having to go back out to github for code mkdir -p src/github.com/pelletier/go-toml/cmd cp *.go *.toml src/github.com/pelletier/go-toml -cp cmd/*.go src/github.com/pelletier/go-toml/cmd +cp -R cmd/* src/github.com/pelletier/go-toml/cmd go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go # Run basic unit tests -go test -v github.com/pelletier/go-toml +go test github.com/pelletier/go-toml \ + github.com/pelletier/go-toml/cmd/tomljson # run the entire BurntSushi test suite if [[ $# -eq 0 ]] ; then