feat: add prefix command

This commit is contained in:
matfax 2018-11-18 16:35:28 +01:00 committed by Mike Farah
parent d7040e3933
commit 48dcc15281
3 changed files with 327 additions and 0 deletions

View File

@ -341,6 +341,180 @@ func TestReadCmd_ToJsonLong(t *testing.T) {
assertResult(t, "2\n", result.Output)
}
func TestPrefixCmd(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s d", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `d:
b:
c: 3
`
assertResult(t, expectedOutput, result.Output)
}
func TestPrefixCmd_MultiLayer(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s d.e.f", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `d:
e:
f:
b:
c: 3
`
assertResult(t, expectedOutput, result.Output)
}
func TestPrefixMultiCmd(t *testing.T) {
content := `b:
c: 3
---
apples: great
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s -d 1 d", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `b:
c: 3
---
d:
apples: great
`
assertResult(t, expectedOutput, result.Output)
}
func TestPrefixInvalidDocumentIndexCmd(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s -df d", filename))
if result.Error == nil {
t.Error("Expected command to fail due to invalid path")
}
expectedOutput := `Document index f is not a integer or *: strconv.ParseInt: parsing "f": invalid syntax`
assertResult(t, expectedOutput, result.Error.Error())
}
func TestPrefixBadDocumentIndexCmd(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s -d 1 d", filename))
if result.Error == nil {
t.Error("Expected command to fail due to invalid path")
}
expectedOutput := `Asked to process document index 1 but there are only 1 document(s)`
assertResult(t, expectedOutput, result.Error.Error())
}
func TestPrefixMultiAllCmd(t *testing.T) {
content := `b:
c: 3
---
apples: great
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix %s -d * d", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `d:
b:
c: 3
---
d:
apples: great`
assertResult(t, expectedOutput, strings.Trim(result.Output, "\n "))
}
func TestPrefixCmd_Error(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "prefix")
if result.Error == nil {
t.Error("Expected command to fail due to missing arg")
}
expectedOutput := `Must provide <filename> <prefixed_path>`
assertResult(t, expectedOutput, result.Error.Error())
}
func TestPrefixCmd_ErrorUnreadableFile(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "prefix fake-unknown a.b")
if result.Error == nil {
t.Error("Expected command to fail due to unknown file")
}
expectedOutput := `open fake-unknown: no such file or directory`
assertResult(t, expectedOutput, result.Error.Error())
}
func TestPrefixCmd_Verbose(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("-v prefix %s x", filename))
if result.Error != nil {
t.Error(result.Error)
}
expectedOutput := `x:
b:
c: 3
`
assertResult(t, expectedOutput, result.Output)
}
func TestPrefixCmd_Inplace(t *testing.T) {
content := `b:
c: 3
`
filename := writeTempYamlFile(content)
defer removeTempYamlFile(filename)
cmd := getRootCommand()
result := runCmd(cmd, fmt.Sprintf("prefix -i %s d", filename))
if result.Error != nil {
t.Error(result.Error)
}
gotOutput := readTempYamlFile(filename)
expectedOutput := `d:
b:
c: 3`
assertResult(t, expectedOutput, strings.Trim(gotOutput, "\n "))
}
func TestNewCmd(t *testing.T) {
cmd := getRootCommand()
result := runCmd(cmd, "new b.c 3")

96
mkdocs/prefix.md Normal file
View File

@ -0,0 +1,96 @@
Paths can be prefixed using the 'prefix' command.
The complete yaml content will be nested inside the new prefix path.
```
yq p <yaml_file> <path>
```
### To Stdout
Given a data1.yaml file of:
```yaml
a: simple
b: [1, 2]
```
then
```bash
yq p data1.yaml c
```
will output:
```yaml
c:
a: simple
b: [1, 2]
```
### Arbitrary depth
Given a data1.yaml file of:
```yaml
a:
b: [1, 2]
```
then
```bash
yq p data1.yaml c.d
```
will output:
```yaml
c:
d:
a:
b: [1, 2]
```
### Updating files in-place
Given a data1.yaml file of:
```yaml
a: simple
b: [1, 2]
```
then
```bash
yq p -i data1.yaml c
```
will update the data1.yaml file so that the path 'c' is prefixed to all other paths.
### Multiple Documents - update a single document
Given a data1.yaml file of:
```yaml
something: else
---
a: simple
b: cat
```
then
```bash
yq p -d1 data1.yaml c
```
will output:
```yaml
something: else
---
c:
a: simple
b: cat
```
### Multiple Documents - update a single document
Given a data1.yaml file of:
```yaml
something: else
---
a: simple
b: cat
```
then
```bash
yq p -d'*' data1.yaml c
```
will output:
```yaml
c:
something: else
---
c:
a: simple
b: cat
```

57
yq.go
View File

@ -73,6 +73,7 @@ func newCommandCLI() *cobra.Command {
rootCmd.AddCommand(
createReadCmd(),
createWriteCmd(),
createPrefixCmd(),
createDeleteCmd(),
createNewCmd(),
createMergeCmd(),
@ -137,6 +138,28 @@ a.b.e:
return cmdWrite
}
func createPrefixCmd() *cobra.Command {
var cmdWrite = &cobra.Command{
Use: "prefix [yaml_file] [path]",
Aliases: []string{"p"},
Short: "yq p [--inplace/-i] [--doc/-d index] sample.yaml a.b.c",
Example: `
yq prefix things.yaml a.b.c
yq prefix --inplace things.yaml a.b.c
yq p -i things.yaml a.b.c
yq p --doc 2 things.yaml a.b.d
yq p -d2 things.yaml a.b.d
`,
Long: `Prefixes w.r.t to the yaml file at the given path.
Outputs to STDOUT unless the inplace flag is used, in which case the file is updated instead.
`,
RunE: prefixProperty,
}
cmdWrite.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the yaml file inplace")
cmdWrite.PersistentFlags().StringVarP(&docIndex, "doc", "d", "0", "process document index number (0 based, * for all documents)")
return cmdWrite
}
func createDeleteCmd() *cobra.Command {
var cmdDelete = &cobra.Command{
Use: "delete [yaml_file] [path]",
@ -394,6 +417,40 @@ func writeProperty(cmd *cobra.Command, args []string) error {
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
}
func prefixProperty(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return errors.New("Must provide <filename> <prefixed_path>")
}
var updateAll, docIndexInt, errorParsingDocIndex = parseDocumentIndex()
if errorParsingDocIndex != nil {
return errorParsingDocIndex
}
var paths = parsePath(args[1])
// Inverse order
for i := len(paths)/2 - 1; i >= 0; i-- {
opp := len(paths) - 1 - i
paths[i], paths[opp] = paths[opp], paths[i]
}
var updateData = func(dataBucket interface{}, currentIndex int) (interface{}, error) {
if updateAll || currentIndex == docIndexInt {
log.Debugf("Prefixing %v to doc %v", paths, currentIndex)
var mapDataBucket = dataBucket
for _, key := range paths {
nestedBucket := make(map[string]interface{})
nestedBucket[key] = mapDataBucket
mapDataBucket = nestedBucket
}
return mapDataBucket, nil
}
return dataBucket, nil
}
return readAndUpdate(cmd.OutOrStdout(), args[0], updateData)
}
func readAndUpdate(stdOut io.Writer, inputFile string, updateData updateDataFn) error {
var destination io.Writer
var destinationName string