mirror of
https://github.com/mikefarah/yq.git
synced 2025-01-13 03:45:37 +00:00
Custom path parsing
This commit is contained in:
parent
01845ea923
commit
c4af37ed68
18
README.md
18
README.md
@ -27,6 +27,18 @@ yaml sample.yaml b.c
|
|||||||
```
|
```
|
||||||
will output the value of '2'.
|
will output the value of '2'.
|
||||||
|
|
||||||
|
### Handling '.' in the yaml key
|
||||||
|
Given a sample.yaml file of:
|
||||||
|
```yaml
|
||||||
|
b.x:
|
||||||
|
c: 2
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yaml sample.yaml \"b.x\".c
|
||||||
|
```
|
||||||
|
will output the value of '2'.
|
||||||
|
|
||||||
### Arrays
|
### Arrays
|
||||||
You can give an index to access a specific element:
|
You can give an index to access a specific element:
|
||||||
e.g.: given a sample file of
|
e.g.: given a sample file of
|
||||||
@ -40,7 +52,7 @@ b:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```
|
```
|
||||||
yaml sample.yaml b.e.1.name
|
yaml sample.yaml b.e[1].name
|
||||||
```
|
```
|
||||||
will output 'sam'
|
will output 'sam'
|
||||||
|
|
||||||
@ -59,7 +71,3 @@ will output:
|
|||||||
b:
|
b:
|
||||||
c: cat
|
c: cat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
* Handling '.' in path names
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
// "fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
@ -65,8 +65,9 @@ func assertResult(t *testing.T, expectedValue interface{}, actualValue interface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, testDescription string) {
|
func assertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) {
|
||||||
if expectedValue != actualValue {
|
if expectedValue != actualValue {
|
||||||
t.Error(testDescription, ": expected <", expectedValue, "> but got <", actualValue, ">")
|
t.Error(context)
|
||||||
|
t.Error(": expected <", expectedValue, "> but got <", actualValue, ">")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
55
path_parser.go
Normal file
55
path_parser.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func parsePath(path string) []string {
|
||||||
|
return parsePathAccum([]string{}, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePathAccum(paths []string, remaining string) []string {
|
||||||
|
head, tail := nextYamlPath(remaining)
|
||||||
|
if tail == "" {
|
||||||
|
return append(paths, head)
|
||||||
|
}
|
||||||
|
return parsePathAccum(append(paths, head), tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextYamlPath(path string) (pathElement string, remaining string) {
|
||||||
|
switch path[0] {
|
||||||
|
case '[':
|
||||||
|
// e.g [0].blah.cat -> we need to return "0" and "blah.cat"
|
||||||
|
return search(path[1:len(path)], []uint8{']'}, true)
|
||||||
|
case '"':
|
||||||
|
// e.g "a.b".blah.cat -> we need to return "a.b" and "blah.cat"
|
||||||
|
return search(path[1:len(path)], []uint8{'"'}, true)
|
||||||
|
default:
|
||||||
|
// e.g "a.blah.cat" -> return "a" and "blah.cat"
|
||||||
|
return search(path[0:len(path)], []uint8{'.', '['}, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(path string, matchingChars []uint8, skipNext bool) (pathElement string, remaining string) {
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
var char = path[i]
|
||||||
|
if contains(matchingChars, char) {
|
||||||
|
var remainingStart = i + 1
|
||||||
|
if skipNext {
|
||||||
|
remainingStart = remainingStart + 1
|
||||||
|
} else if !skipNext && char != '.' {
|
||||||
|
remainingStart = i
|
||||||
|
}
|
||||||
|
if remainingStart > len(path) {
|
||||||
|
remainingStart = len(path)
|
||||||
|
}
|
||||||
|
return path[0:i], path[remainingStart:len(path)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(matchingChars []uint8, candidate uint8) bool {
|
||||||
|
for _, a := range matchingChars {
|
||||||
|
if a == candidate {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
42
path_parser_test.go
Normal file
42
path_parser_test.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var parsePathsTests = []struct {
|
||||||
|
path string
|
||||||
|
expectedPaths []string
|
||||||
|
}{
|
||||||
|
{"a.b", []string{"a", "b"}},
|
||||||
|
{"a.b[0]", []string{"a", "b", "0"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParsePath(t *testing.T) {
|
||||||
|
for _, tt := range parsePathsTests {
|
||||||
|
assertResultWithContext(t, tt.expectedPaths, parsePath(tt.path), tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextYamlPathTests = []struct {
|
||||||
|
path string
|
||||||
|
expectedElement string
|
||||||
|
expectedRemaining string
|
||||||
|
}{
|
||||||
|
{"a.b", "a", "b"},
|
||||||
|
{"a", "a", ""},
|
||||||
|
{"a.b.c", "a", "b.c"},
|
||||||
|
{"\"a.b\".c", "a.b", "c"},
|
||||||
|
{"a.\"b.c\".d", "a", "\"b.c\".d"},
|
||||||
|
{"[1].a.d", "1", "a.d"},
|
||||||
|
{"a[0].c", "a", "[0].c"},
|
||||||
|
{"[0]", "0", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNextYamlPath(t *testing.T) {
|
||||||
|
for _, tt := range nextYamlPathTests {
|
||||||
|
var element, remaining = nextYamlPath(tt.path)
|
||||||
|
assertResultWithContext(t, tt.expectedElement, element, tt)
|
||||||
|
assertResultWithContext(t, tt.expectedRemaining, remaining, tt)
|
||||||
|
}
|
||||||
|
}
|
6
yaml.go
6
yaml.go
@ -51,8 +51,7 @@ func readProperty(c *cli.Context) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = c.Args()[1]
|
var paths = parsePath(c.Args()[1])
|
||||||
var paths = strings.Split(path, ".")
|
|
||||||
|
|
||||||
printYaml(readMap(parsedData, paths[0], paths[1:len(paths)]), c.Bool("trim"))
|
printYaml(readMap(parsedData, paths[0], paths[1:len(paths)]), c.Bool("trim"))
|
||||||
}
|
}
|
||||||
@ -71,8 +70,7 @@ func writeProperty(c *cli.Context) {
|
|||||||
forceString = true
|
forceString = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = c.Args()[1]
|
var paths = parsePath(c.Args()[1])
|
||||||
var paths = strings.Split(path, ".")
|
|
||||||
|
|
||||||
write(parsedData, paths[0], paths[1:len(paths)], getValue(c.Args()[2], forceString))
|
write(parsedData, paths[0], paths[1:len(paths)], getValue(c.Args()[2], forceString))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user