Skip to content

Commit

Permalink
Add support for loading TOML
Browse files Browse the repository at this point in the history
Add functions to load TOML files as another supported type of document.

Fix TYPO of variable output processor.

Make binaries as static as possible.

Add Ginkgo Travis CI suggestions on which flags should be used.

Add example TOML file.
  • Loading branch information
HeavyWombat committed Nov 17, 2018
1 parent f83ffbc commit 9bd24e3
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 31 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ clean:
rm -rf binaries

test:
ginkgo -r --nodes 1 --randomizeAllSpecs --randomizeSuites --race --trace
ginkgo -r --randomizeAllSpecs --randomizeSuites --failOnPending --trace --race --nodes=2 --compilers=2

gobin:
go build -ldflags='-s -w -extldflags "-static"' -o ${GOPATH}/bin/ytbx cmd/ytbx/main.go

build: binaries/ytbx-linux-amd64 binaries/ytbx-darwin-amd64 binaries/ytbx-windows-amd64

binaries/ytbx-linux-amd64: $(sources)
GOOS=linux GOARCH=amd64 go build -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-linux-amd64 cmd/ytbx/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-linux-amd64 cmd/ytbx/main.go

binaries/ytbx-darwin-amd64: $(sources)
GOOS=darwin GOARCH=amd64 go build -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-darwin-amd64 cmd/ytbx/main.go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -a -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-darwin-amd64 cmd/ytbx/main.go

binaries/ytbx-windows-amd64: $(sources)
GOOS=windows GOARCH=amd64 go build -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-windows-amd64 cmd/ytbx/main.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -a -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/HeavyWombat/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-windows-amd64 cmd/ytbx/main.go
33 changes: 33 additions & 0 deletions assets/examples/sample.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This is a TOML document.

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates

[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

# Indentation (tabs and/or spaces) is allowed but not required
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"

[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"

[clients]
data = [ ["gamma", "delta"], [1, 2] ]

# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
6 changes: 3 additions & 3 deletions internal/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get",
Use: "get <file> <path>",
Args: cobra.ExactArgs(2),
Short: "Get the value at a given path",
Long: "Get the value at a given path in the file.\n" + getPathHelp(),
Expand All @@ -45,9 +45,9 @@ var getCmd = &cobra.Command{

boldKeys := true
useIndentLines := true
outoutProcessor := neat.NewOutputProcessor(useIndentLines, boldKeys, &neat.DefaultColorSchema)
outputProcessor := neat.NewOutputProcessor(useIndentLines, boldKeys, &neat.DefaultColorSchema)

output, err := outoutProcessor.ToYAML(obj)
output, err := outputProcessor.ToYAML(obj)
if err != nil {
exitWithError("Failed to render gathered data", err)
}
Expand Down
15 changes: 5 additions & 10 deletions pkg/v1/ytbx/getting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,28 @@ var _ = Describe("getting stuff test cases", func() {
Context("Grabing values by path", func() {
It("should return the value referenced by the path", func() {
example := yml("../../../assets/examples/types.yml")
Expect(example).ToNot(BeNil())

Expect(grab(example, "/yaml/map/before")).To(BeEquivalentTo("after"))
Expect(grab(example, "/yaml/map/intA")).To(BeEquivalentTo(42))
Expect(grab(example, "/yaml/map/mapA")).To(BeEquivalentTo(yml(`{ key0: A, key1: A }`)))
Expect(grab(example, "/yaml/map/listA")).To(BeEquivalentTo(list(`[ A, A, A ]`)))

Expect(grab(example, "/yaml/named-entry-list-using-name/name=B")).To(BeEquivalentTo(yml(`{ name: B }`)))
Expect(grab(example, "/yaml/named-entry-list-using-key/key=B")).To(BeEquivalentTo(yml(`{ key: B }`)))
Expect(grab(example, "/yaml/named-entry-list-using-id/id=B")).To(BeEquivalentTo(yml(`{ id: B }`)))

Expect(grab(example, "/yaml/simple-list/1")).To(BeEquivalentTo("B"))
Expect(grab(example, "/yaml/named-entry-list-using-key/3")).To(BeEquivalentTo(yml(`{ key: X }`)))

// --- --- ---

example = yml("../../../assets/bosh-yaml/manifest.yml")
Expect(example).ToNot(BeNil())

Expect(grab(example, "/instance_groups/name=web/networks/name=concourse/static_ips/0")).To(BeEquivalentTo("XX.XX.XX.XX"))
Expect(grab(example, "/instance_groups/name=worker/jobs/name=baggageclaim/properties")).To(BeEquivalentTo(yml(`{}`)))
})

It("should return the value referenced by the path", func() {
It("should return the whole tree if root is referenced", func() {
example := yml("../../../assets/examples/types.yml")
Expect(example).ToNot(BeNil())
Expect(grab(example, "/")).To(BeEquivalentTo(example))
})

It("should return useful error messages", func() {
example := yml("../../../assets/examples/types.yml")
Expect(grabError(example, "/yaml/simple-list/-1")).To(BeEquivalentTo("failed to traverse tree, provided list index -1 is not in range: 0..4"))
Expect(grabError(example, "/yaml/does-not-exist")).To(BeEquivalentTo("no key 'does-not-exist' found in map, available keys: map, simple-list, named-entry-list-using-name, named-entry-list-using-key, named-entry-list-using-id"))
Expect(grabError(example, "/yaml/0")).To(BeEquivalentTo("failed to traverse tree, expected a list but found type map at /yaml"))
Expand Down
37 changes: 32 additions & 5 deletions pkg/v1/ytbx/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ import (

"github.com/HeavyWombat/gonvenience/pkg/v1/bunt"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"

toml "github.com/BurntSushi/toml"
ordered "github.com/virtuald/go-ordered-json"
yaml "gopkg.in/yaml.v2"
)

// PreserveKeyOrderInJSON specifies whether a special library is used to decode
Expand Down Expand Up @@ -163,7 +164,8 @@ func LoadFiles(locationA string, locationB string) (InputFile, InputFile, error)
return from.result, to.result, nil
}

// LoadFile processes the provided input location to load a YAML (or JSON, or raw text)
// LoadFile processes the provided input location to load it as one of the
// supported document formats, or plain text if nothing else works.
func LoadFile(location string) (InputFile, error) {
var (
documents []interface{}
Expand All @@ -182,10 +184,20 @@ func LoadFile(location string) (InputFile, error) {
return InputFile{Location: location, Documents: documents}, nil
}

// LoadDocuments reads the provided input data slice as a YAML or JSON file with
// potential multiple documents. It only acts as a dispatcher and depending on
// the input will either use `LoadJSONDocuments` or `LoadYAMLDocuments`.
// LoadDocuments reads the provided input data slice as a YAML, JSON, or TOML
// file with potential multiple documents. It only acts as a dispatcher and
// depending on the input will either use `LoadTOMLDocuments`,
// `LoadJSONDocuments`, or `LoadYAMLDocuments`.
func LoadDocuments(input []byte) ([]interface{}, error) {
// There is no easy check whether the input data is TOML format, this is
// why there is currently no other option than simply trying to parse it.
if toml, err := LoadTOMLDocuments(input); err == nil {
return toml, err
}

// In case the input data set starts with either a map or list start
// symbol, it is assumed to be a JSON document. In any other case, use
// the YAML parser function which also covers plain text documents.
switch input[0] {
case '{', '[':
return LoadJSONDocuments(input)
Expand Down Expand Up @@ -314,6 +326,21 @@ func LoadYAMLDocuments(input []byte) ([]interface{}, error) {
return values, nil
}

// LoadTOMLDocuments reads the provided input data slice as a TOML file, which
// can only have one document. For the sake of having similiar sounding
// functions and the same signatures, the function uses the plural in its name
// and returns a list of results even though it will only contain one entry.
// All map entries inside the result document are converted into YAML MapSlice
// types to make it compatible with the rest of the package.
func LoadTOMLDocuments(input []byte) ([]interface{}, error) {
var data interface{}
if err := toml.Unmarshal(input, &data); err != nil {
return nil, err
}

return []interface{}{mapSlicify(data)}, nil
}

// mapSlicify makes sure that each occurrence of a map in the provided structure
// is changed to a YAML MapSlice.
//
Expand Down
17 changes: 8 additions & 9 deletions pkg/v1/ytbx/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,12 @@ func traverseTree(path Path, obj interface{}, leafFunc func(path Path, value int
// ParseGoPatchStylePathString returns a path by parsing a string representation
// which is assumed to be a GoPatch style path.
func ParseGoPatchStylePathString(path string) (Path, error) {
elements := make([]PathElement, 0)
// Special case for root path
if path == "/" {
return Path{DocumentIdx: 0, PathElements: nil}, nil
}

elements := make([]PathElement, 0)
for i, section := range strings.Split(path, "/") {
if i == 0 {
continue
Expand Down Expand Up @@ -289,14 +293,9 @@ func ParseDotStylePathString(path string, obj interface{}) (Path, error) {
// ParsePathString returns a path by parsing a string representation
// of a path, which can be one of the supported types.
func ParsePathString(pathString string, obj interface{}) (Path, error) {
if IsDotStylePath(pathString) {
return ParseDotStylePathString(pathString, obj)
if strings.HasPrefix(pathString, "/") {
return ParseGoPatchStylePathString(pathString)
}

return ParseGoPatchStylePathString(pathString)
}

// IsDotStylePath checks whether the path string is a Dot-Style path.
func IsDotStylePath(pathString string) bool {
return !strings.HasPrefix(pathString, "/")
return ParseDotStylePathString(pathString, obj)
}
6 changes: 6 additions & 0 deletions pkg/v1/ytbx/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,11 @@ var _ = Describe("path tests", func() {
{Idx: 1},
}}))
})

It("should parse an input string that points to the root of the tree structure", func() {
path, err := ParseGoPatchStylePathString("/")
Expect(err).To(BeNil())
Expect(path).To(BeEquivalentTo(Path{DocumentIdx: 0, PathElements: nil}))
})
})
})

0 comments on commit 9bd24e3

Please sign in to comment.