getting closer

This commit is contained in:
Mike Farah 2023-04-17 16:20:00 +10:00
parent c525fa21c4
commit ee8cc90307
10 changed files with 724 additions and 328 deletions

View File

@ -120,6 +120,9 @@ func (n *CandidateNode) GetNiceTag() string {
} }
func (n *CandidateNode) getParsedKey() interface{} { func (n *CandidateNode) getParsedKey() interface{} {
if n.IsMapKey {
return n.Value
}
if n.Key == nil { if n.Key == nil {
return nil return nil
} }
@ -172,6 +175,13 @@ func (n *CandidateNode) AsList() *list.List {
return elMap return elMap
} }
func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) {
value := rawValue.unwrapDocument()
value.Key = rawKey.unwrapDocument()
value.Key.IsMapKey = true
n.Content = append(n.Content, value.Key, value)
}
func (n *CandidateNode) GetValueRep() (interface{}, error) { func (n *CandidateNode) GetValueRep() (interface{}, error) {
log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value) log.Debugf("GetValueRep for %v value: %v", n.GetNicePath(), n.Value)
realTag := n.guessTagFromCustomType() realTag := n.guessTagFromCustomType()

View File

@ -9,3 +9,267 @@ Add behaves differently according to the type of the LHS:
Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`. Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`.
## Append to existing array
Note that the styling is copied from existing array elements
Given a sample.yml file of:
```yaml
a: ['dog']
```
then
```bash
yq '.a += "cat"' sample.yml
```
will output
```yaml
a: ['dog', 'cat']
```
## Prepend to existing array
Given a sample.yml file of:
```yaml
a:
- dog
```
then
```bash
yq '.a = ["cat"] + .a' sample.yml
```
will output
```yaml
a:
- cat
- dog
```
## Add new object to array
Given a sample.yml file of:
```yaml
a:
- dog: woof
```
then
```bash
yq '.a + {"cat": "meow"}' sample.yml
```
will output
```yaml
- dog: woof
- cat: meow
```
## Relative append
Given a sample.yml file of:
```yaml
a:
a1:
b:
- cat
a2:
b:
- dog
a3: {}
```
then
```bash
yq '.a[].b += ["mouse"]' sample.yml
```
will output
```yaml
a:
a1:
b:
- cat
- mouse
a2:
b:
- dog
- mouse
a3:
b:
- mouse
```
## String concatenation
Given a sample.yml file of:
```yaml
a: cat
b: meow
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a: catmeow
b: meow
```
## Number addition - float
If the lhs or rhs are floats then the expression will be calculated with floats.
Given a sample.yml file of:
```yaml
a: 3
b: 4.9
```
then
```bash
yq '.a = .a + .b' sample.yml
```
will output
```yaml
a: 7.9
b: 4.9
```
## Number addition - int
If both the lhs and rhs are ints then the expression will be calculated with ints.
Given a sample.yml file of:
```yaml
a: 3
b: 4
```
then
```bash
yq '.a = .a + .b' sample.yml
```
will output
```yaml
a: 7
b: 4
```
## Increment numbers
Given a sample.yml file of:
```yaml
a: 3
b: 5
```
then
```bash
yq '.[] += 1' sample.yml
```
will output
```yaml
a: 4
b: 6
```
## Date addition
You can add durations to dates. Assumes RFC3339 date time format, see [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.
Given a sample.yml file of:
```yaml
a: 2021-01-01T00:00:00Z
```
then
```bash
yq '.a += "3h10m"' sample.yml
```
will output
```yaml
a: 2021-01-01T03:10:00Z
```
## Date addition - custom format
You can add durations to dates. See [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.
Given a sample.yml file of:
```yaml
a: Saturday, 15-Dec-01 at 2:59AM GMT
```
then
```bash
yq 'with_dtf("Monday, 02-Jan-06 at 3:04PM MST", .a += "3h1m")' sample.yml
```
will output
```yaml
a: Saturday, 15-Dec-01 at 6:00AM GMT
```
## Add to null
Adding to null simply returns the rhs
Running
```bash
yq --null-input 'null + "cat"'
```
will output
```yaml
cat
```
## Add maps to shallow merge
Adding objects together shallow merges them. Use `*` to deeply merge.
Given a sample.yml file of:
```yaml
a:
thing:
name: Astuff
value: x
a1: cool
b:
thing:
name: Bstuff
legs: 3
b1: neat
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a:
thing:
name: Bstuff
legs: 3
a1: cool
b1: neat
b:
thing:
name: Bstuff
legs: 3
b1: neat
```
## Custom types: that are really strings
When custom tags are encountered, yq will try to decode the underlying type.
Given a sample.yml file of:
```yaml
a: !horse cat
b: !goat _meow
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a: !horse cat_meow
b: !goat _meow
```
## Custom types: that are really numbers
When custom tags are encountered, yq will try to decode the underlying type.
Given a sample.yml file of:
```yaml
a: !horse 1.2
b: !goat 2.3
```
then
```bash
yq '.a += .b' sample.yml
```
will output
```yaml
a: !horse 3.5
b: !goat 2.3
```

View File

@ -2,3 +2,95 @@
This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents. This is used to construct objects (or maps). This can be used against existing yaml, or to create fresh yaml documents.
## Collect empty object
Running
```bash
yq --null-input '{}'
```
will output
```yaml
{}
```
## Wrap (prefix) existing object
Given a sample.yml file of:
```yaml
name: Mike
```
then
```bash
yq '{"wrap": .}' sample.yml
```
will output
```yaml
wrap:
name: Mike
```
## Using splat to create multiple objects
Given a sample.yml file of:
```yaml
name: Mike
pets:
- cat
- dog
```
then
```bash
yq '{.name: .pets.[]}' sample.yml
```
will output
```yaml
Mike: cat
Mike: dog
```
## Working with multiple documents
Given a sample.yml file of:
```yaml
name: Mike
pets:
- cat
- dog
---
name: Rosey
pets:
- monkey
- sheep
```
then
```bash
yq '{.name: .pets.[]}' sample.yml
```
will output
```yaml
Mike: cat
Mike: dog
---
Rosey: monkey
Rosey: sheep
```
## Creating yaml from scratch
Running
```bash
yq --null-input '{"wrap": "frog"}'
```
will output
```yaml
wrap: frog
```
## Creating yaml from scratch with multiple objects
Running
```bash
yq --null-input '(.a.b = "foo") | (.d.e = "bar")'
```
will output
```yaml
a:
b: foo
d:
e: bar
```

View File

@ -159,9 +159,9 @@ func addDateTimes(layout string, target *CandidateNode, lhs *CandidateNode, rhs
func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error { func addSequences(target *CandidateNode, lhs *CandidateNode, rhs *CandidateNode) error {
log.Debugf("adding sequences! target: %v; lhs %v; rhs: %v", NodeToString(target), NodeToString(lhs), NodeToString(rhs)) log.Debugf("adding sequences! target: %v; lhs %v; rhs: %v", NodeToString(target), NodeToString(lhs), NodeToString(rhs))
target.Kind = SequenceNode target.Kind = SequenceNode
if len(lhs.Content) > 0 { if len(lhs.Content) == 0 {
log.Debugf("copy lhs style") log.Debugf("dont copy lhs style")
target.Style = lhs.Style target.Style = 0
} }
target.Tag = lhs.Tag target.Tag = lhs.Tag
@ -179,6 +179,11 @@ func addMaps(target *CandidateNode, lhsC *CandidateNode, rhsC *CandidateNode) {
lhs := lhsC lhs := lhsC
rhs := rhsC rhs := rhsC
if len(lhs.Content) == 0 {
log.Debugf("dont copy lhs style")
target.Style = 0
}
target.Content = make([]*CandidateNode, len(lhs.Content)) target.Content = make([]*CandidateNode, len(lhs.Content))
copy(target.Content, lhs.Content) copy(target.Content, lhs.Content)

View File

@ -82,272 +82,272 @@ var addOperatorScenarios = []expressionScenario{
// "D0, P[a], (!!seq)::[1, 2]\n", // "D0, P[a], (!!seq)::[1, 2]\n",
// }, // },
// }, // },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to empty array", description: "Concatenate to empty array",
// document: `{a: []}`, document: `{a: []}`,
// expression: `.a + "cat"`, expression: `.a + "cat"`,
// expected: []string{ expected: []string{
// "D0, P[a], (!!seq)::- cat\n", "D0, P[a], (!!seq)::- cat\n",
// }, },
// }, },
// { {
// description: "Append to existing array", description: "Append to existing array",
// subdescription: "Note that the styling is copied from existing array elements", subdescription: "Note that the styling is copied from existing array elements",
// dontFormatInputForDoc: true, dontFormatInputForDoc: true,
// document: `a: ['dog']`, document: `a: ['dog']`,
// expression: `.a += "cat"`, expression: `.a += "cat"`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: ['dog', 'cat']\n", "D0, P[], (doc)::a: ['dog', 'cat']\n",
// }, },
// }, },
// { {
// description: "Prepend to existing array", description: "Prepend to existing array",
// document: `a: [dog]`, document: `a: [dog]`,
// expression: `.a = ["cat"] + .a`, expression: `.a = ["cat"] + .a`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: [cat, dog]\n", "D0, P[], (doc)::a: [cat, dog]\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to existing array", description: "Concatenate to existing array",
// subdescription: "does not modify original", subdescription: "does not modify original",
// document: `{a: ['dog'], b: cat}`, document: `{a: ['dog'], b: cat}`,
// expression: `.a = .a + .b`, expression: `.a = .a + .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::{a: ['dog', 'cat'], b: cat}\n", "D0, P[], (doc)::{a: ['dog', 'cat'], b: cat}\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to empty array", description: "Concatenate to empty array",
// document: `a: []`, document: `a: []`,
// expression: `.a += "cat"`, expression: `.a += "cat"`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a:\n - cat\n", "D0, P[], (doc)::a:\n - cat\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to existing array", description: "Concatenate to existing array",
// document: `a: [dog]`, document: `a: [dog]`,
// expression: `.a += "cat"`, expression: `.a += "cat"`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: [dog, cat]\n", "D0, P[], (doc)::a: [dog, cat]\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to empty object", description: "Concatenate to empty object",
// document: `{a: {}}`, document: `{a: {}}`,
// expression: `.a + {"b": "cat"}`, expression: `.a + {"b": "cat"}`,
// expected: []string{ expected: []string{
// "D0, P[a], (!!map)::b: cat\n", "D0, P[a], (!!map)::b: cat\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to existing object", description: "Concatenate to existing object",
// document: `{a: {c: dog}}`, document: `{a: {c: dog}}`,
// expression: `.a + {"b": "cat"}`, expression: `.a + {"b": "cat"}`,
// expected: []string{ expected: []string{
// "D0, P[a], (!!map)::{c: dog, b: cat}\n", "D0, P[a], (!!map)::{c: dog, b: cat}\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to existing object", description: "Concatenate to existing object",
// subdescription: "matches stylig", subdescription: "matches stylig",
// document: "a:\n c: dog", document: "a:\n c: dog",
// expression: `.a + {"b": "cat"}`, expression: `.a + {"b": "cat"}`,
// expected: []string{ expected: []string{
// "D0, P[a], (!!map)::c: dog\nb: cat\n", "D0, P[a], (!!map)::c: dog\nb: cat\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to empty object in place", description: "Concatenate to empty object in place",
// document: `a: {}`, document: `a: {}`,
// expression: `.a += {"b": "cat"}`, expression: `.a += {"b": "cat"}`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a:\n b: cat\n", "D0, P[], (doc)::a:\n b: cat\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Concatenate to existing object in place", description: "Concatenate to existing object in place",
// document: `a: {c: dog}`, document: `a: {c: dog}`,
// expression: `.a += {"b": "cat"}`, expression: `.a += {"b": "cat"}`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: {c: dog, b: cat}\n", "D0, P[], (doc)::a: {c: dog, b: cat}\n",
// }, },
// }, },
// { {
// description: "Add new object to array", description: "Add new object to array",
// document: `a: [{dog: woof}]`, document: `a: [{dog: woof}]`,
// expression: `.a + {"cat": "meow"}`, expression: `.a + {"cat": "meow"}`,
// expected: []string{ expected: []string{
// "D0, P[a], (!!seq)::[{dog: woof}, {cat: meow}]\n", "D0, P[a], (!!seq)::[{dog: woof}, {cat: meow}]\n",
// }, },
// }, },
// { {
// description: "Relative append", description: "Relative append",
// document: `a: { a1: {b: [cat]}, a2: {b: [dog]}, a3: {} }`, document: `a: { a1: {b: [cat]}, a2: {b: [dog]}, a3: {} }`,
// expression: `.a[].b += ["mouse"]`, expression: `.a[].b += ["mouse"]`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: {a1: {b: [cat, mouse]}, a2: {b: [dog, mouse]}, a3: {b: [mouse]}}\n", "D0, P[], (doc)::a: {a1: {b: [cat, mouse]}, a2: {b: [dog, mouse]}, a3: {b: [mouse]}}\n",
// }, },
// }, },
// { {
// description: "String concatenation", description: "String concatenation",
// document: `{a: cat, b: meow}`, document: `{a: cat, b: meow}`,
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::{a: catmeow, b: meow}\n", "D0, P[], (doc)::{a: catmeow, b: meow}\n",
// }, },
// }, },
// { {
// description: "String concatenation - str + int", description: "String concatenation - str + int",
// skipDoc: true, skipDoc: true,
// document: `{a: !cool cat, b: meow}`, document: `{a: !cool cat, b: meow}`,
// expression: `.a + 3`, expression: `.a + 3`,
// expected: []string{ expected: []string{
// "D0, P[a], (!cool)::cat3\n", "D0, P[a], (!cool)::cat3\n",
// }, },
// }, },
// { {
// description: "String concatenation - int + str", description: "String concatenation - int + str",
// skipDoc: true, skipDoc: true,
// document: `{a: !cool cat, b: meow}`, document: `{a: !cool cat, b: meow}`,
// expression: `3 + .a`, expression: `3 + .a`,
// expected: []string{ expected: []string{
// "D0, P[], (!cool)::3cat\n", "D0, P[], (!cool)::3cat\n",
// }, },
// }, },
// { {
// description: "Number addition - float", description: "Number addition - float",
// subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.", subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
// document: `{a: 3, b: 4.9}`, document: `{a: 3, b: 4.9}`,
// expression: `.a = .a + .b`, expression: `.a = .a + .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::{a: 7.9, b: 4.9}\n", "D0, P[], (doc)::{a: 7.9, b: 4.9}\n",
// }, },
// }, },
// { {
// description: "Number addition - int", description: "Number addition - int",
// subdescription: "If both the lhs and rhs are ints then the expression will be calculated with ints.", subdescription: "If both the lhs and rhs are ints then the expression will be calculated with ints.",
// document: `{a: 3, b: 4}`, document: `{a: 3, b: 4}`,
// expression: `.a = .a + .b`, expression: `.a = .a + .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::{a: 7, b: 4}\n", "D0, P[], (doc)::{a: 7, b: 4}\n",
// }, },
// }, },
// { {
// description: "Increment numbers", description: "Increment numbers",
// document: `{a: 3, b: 5}`, document: `{a: 3, b: 5}`,
// expression: `.[] += 1`, expression: `.[] += 1`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::{a: 4, b: 6}\n", "D0, P[], (doc)::{a: 4, b: 6}\n",
// }, },
// }, },
// { {
// description: "Date addition", description: "Date addition",
// subdescription: "You can add durations to dates. Assumes RFC3339 date time format, see [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.", subdescription: "You can add durations to dates. Assumes RFC3339 date time format, see [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.",
// document: `a: 2021-01-01T00:00:00Z`, document: `a: 2021-01-01T00:00:00Z`,
// expression: `.a += "3h10m"`, expression: `.a += "3h10m"`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: 2021-01-01T03:10:00Z\n", "D0, P[], (doc)::a: 2021-01-01T03:10:00Z\n",
// }, },
// }, },
// { {
// description: "Date addition -date only", description: "Date addition -date only",
// skipDoc: true, skipDoc: true,
// document: `a: 2021-01-01`, document: `a: 2021-01-01`,
// expression: `.a += "24h"`, expression: `.a += "24h"`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: 2021-01-02T00:00:00Z\n", "D0, P[], (doc)::a: 2021-01-02T00:00:00Z\n",
// }, },
// }, },
// { {
// description: "Date addition - custom format", description: "Date addition - custom format",
// subdescription: "You can add durations to dates. See [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.", subdescription: "You can add durations to dates. See [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.",
// document: `a: Saturday, 15-Dec-01 at 2:59AM GMT`, document: `a: Saturday, 15-Dec-01 at 2:59AM GMT`,
// expression: `with_dtf("Monday, 02-Jan-06 at 3:04PM MST", .a += "3h1m")`, expression: `with_dtf("Monday, 02-Jan-06 at 3:04PM MST", .a += "3h1m")`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: Saturday, 15-Dec-01 at 6:00AM GMT\n", "D0, P[], (doc)::a: Saturday, 15-Dec-01 at 6:00AM GMT\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Date addition - custom format", description: "Date addition - custom format",
// subdescription: "You can add durations to dates. See [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.", subdescription: "You can add durations to dates. See [date-time operators](https://mikefarah.gitbook.io/yq/operators/date-time-operators) for more information.",
// document: `a: !cat Saturday, 15-Dec-01 at 2:59AM GMT`, document: `a: !cat Saturday, 15-Dec-01 at 2:59AM GMT`,
// expression: `with_dtf("Monday, 02-Jan-06 at 3:04PM MST", .a += "3h1m")`, expression: `with_dtf("Monday, 02-Jan-06 at 3:04PM MST", .a += "3h1m")`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: !cat Saturday, 15-Dec-01 at 6:00AM GMT\n", "D0, P[], (doc)::a: !cat Saturday, 15-Dec-01 at 6:00AM GMT\n",
// }, },
// }, },
// { {
// description: "Add to null", description: "Add to null",
// subdescription: "Adding to null simply returns the rhs", subdescription: "Adding to null simply returns the rhs",
// expression: `null + "cat"`, expression: `null + "cat"`,
// expected: []string{ expected: []string{
// "D0, P[], (!!str)::cat\n", "D0, P[], (!!str)::cat\n",
// }, },
// }, },
// { {
// description: "Add maps to shallow merge", description: "Add maps to shallow merge",
// subdescription: "Adding objects together shallow merges them. Use `*` to deeply merge.", subdescription: "Adding objects together shallow merges them. Use `*` to deeply merge.",
// document: "a: {thing: {name: Astuff, value: x}, a1: cool}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}", document: "a: {thing: {name: Astuff, value: x}, a1: cool}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: {thing: {name: Bstuff, legs: 3}, a1: cool, b1: neat}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}\n", "D0, P[], (doc)::a: {thing: {name: Bstuff, legs: 3}, a1: cool, b1: neat}\nb: {thing: {name: Bstuff, legs: 3}, b1: neat}\n",
// }, },
// }, },
// { {
// description: "Custom types: that are really strings", description: "Custom types: that are really strings",
// subdescription: "When custom tags are encountered, yq will try to decode the underlying type.", subdescription: "When custom tags are encountered, yq will try to decode the underlying type.",
// document: "a: !horse cat\nb: !goat _meow", document: "a: !horse cat\nb: !goat _meow",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: !horse cat_meow\nb: !goat _meow\n", "D0, P[], (doc)::a: !horse cat_meow\nb: !goat _meow\n",
// }, },
// }, },
// { {
// description: "Custom types: that are really numbers", description: "Custom types: that are really numbers",
// subdescription: "When custom tags are encountered, yq will try to decode the underlying type.", subdescription: "When custom tags are encountered, yq will try to decode the underlying type.",
// document: "a: !horse 1.2\nb: !goat 2.3", document: "a: !horse 1.2\nb: !goat 2.3",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: !horse 3.5\nb: !goat 2.3\n", "D0, P[], (doc)::a: !horse 3.5\nb: !goat 2.3\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// document: "a: !horse 2\nb: !goat 2.3", document: "a: !horse 2\nb: !goat 2.3",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: !horse 4.3\nb: !goat 2.3\n", "D0, P[], (doc)::a: !horse 4.3\nb: !goat 2.3\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// document: "a: 2\nb: !goat 2.3", document: "a: 2\nb: !goat 2.3",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: 4.3\nb: !goat 2.3\n", "D0, P[], (doc)::a: 4.3\nb: !goat 2.3\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Custom types: that are really ints", description: "Custom types: that are really ints",
// document: "a: !horse 2\nb: !goat 3", document: "a: !horse 2\nb: !goat 3",
// expression: `.a += .b`, expression: `.a += .b`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: !horse 5\nb: !goat 3\n", "D0, P[], (doc)::a: !horse 5\nb: !goat 3\n",
// }, },
// }, },
{ {
description: "Custom types: that are really arrays", description: "Custom types: that are really arrays",
skipDoc: true, skipDoc: true,
@ -358,29 +358,29 @@ var addOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: !horse [a, b]\nb: !goat [b]\n", "D0, P[], (doc)::a: !horse [a, b]\nb: !goat [b]\n",
}, },
}, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Keep anchors", description: "Keep anchors",
// document: "a: &horse [1]", document: "a: &horse [1]",
// expression: `.a += 2`, expression: `.a += 2`,
// expected: []string{ expected: []string{
// "D0, P[], (doc)::a: &horse [1, 2]\n", "D0, P[], (doc)::a: &horse [1, 2]\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Add sequence to map", description: "Add sequence to map",
// document: "a: {x: cool}", document: "a: {x: cool}",
// expression: `.a += [2]`, expression: `.a += [2]`,
// expectedError: "!!seq () cannot be added to a !!map (a)", expectedError: "!!seq () cannot be added to a !!map (a)",
// }, },
// { {
// skipDoc: true, skipDoc: true,
// description: "Add sequence to scalar", description: "Add sequence to scalar",
// document: "a: cool", document: "a: cool",
// expression: `.a += [2]`, expression: `.a += [2]`,
// expectedError: "!!seq () cannot be added to a !!str (a)", expectedError: "!!seq () cannot be added to a !!str (a)",
// }, },
} }
func TestAddOperatorScenarios(t *testing.T) { func TestAddOperatorScenarios(t *testing.T) {

View File

@ -22,6 +22,7 @@ func collectObjectOperator(d *dataTreeNavigator, originalContext Context, expres
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
candidate := &CandidateNode{Kind: MappingNode, Tag: "!!map", Value: "{}"} candidate := &CandidateNode{Kind: MappingNode, Tag: "!!map", Value: "{}"}
log.Debugf("-- collectObjectOperation - starting with empty map")
return context.SingleChildContext(candidate), nil return context.SingleChildContext(candidate), nil
} }
first := context.MatchingNodes.Front().Value.(*CandidateNode) first := context.MatchingNodes.Front().Value.(*CandidateNode)
@ -38,6 +39,7 @@ func collectObjectOperator(d *dataTreeNavigator, originalContext Context, expres
rotated[i].PushBack(candidateNode.Content[i]) rotated[i].PushBack(candidateNode.Content[i])
} }
} }
log.Debugf("-- collectObjectOperation, lenght of rotated is %v", len(rotated))
newObject := list.New() newObject := list.New()
for i := 0; i < len(first.Content); i++ { for i := 0; i < len(first.Content); i++ {
@ -58,6 +60,7 @@ func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List)
} }
candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode) candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode)
log.Debugf("-- collectObjectOperation - collect %v", NodeToString(candidate))
splatted, err := splat(context.SingleChildContext(candidate), splatted, err := splat(context.SingleChildContext(candidate),
traversePreferences{DontFollowAlias: true, IncludeMapKeys: false}) traversePreferences{DontFollowAlias: true, IncludeMapKeys: false})
@ -67,6 +70,7 @@ func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List)
} }
if context.MatchingNodes.Len() == 0 { if context.MatchingNodes.Len() == 0 {
log.Debugf("-- collectObjectOperation - collect context is empty, next")
return collect(d, splatted, remainingMatches) return collect(d, splatted, remainingMatches)
} }
@ -79,6 +83,7 @@ func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List)
newCandidate := aggCandidate.Copy() newCandidate := aggCandidate.Copy()
newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, context, newCandidate, splatCandidate) newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, context, newCandidate, splatCandidate)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -5,22 +5,22 @@ import (
) )
var collectObjectOperatorScenarios = []expressionScenario{ var collectObjectOperatorScenarios = []expressionScenario{
// { {
// skipDoc: true, skipDoc: true,
// document: `[{name: cat}, {name: dog}]`, document: `[{name: cat}, {name: dog}]`,
// expression: `.[] | {.name: "great"}`, expression: `.[] | {.name: "great"}`,
// expected: []string{ expected: []string{
// "D0, P[], (!!map)::cat: great\n", "D0, P[], (!!map)::cat: great\n",
// "D0, P[], (!!map)::dog: great\n", "D0, P[], (!!map)::dog: great\n",
// }, },
// }, },
// { {
// skipDoc: true, skipDoc: true,
// expression: `({} + {}) | (.b = 3)`, expression: `({} + {}) | (.b = 3)`,
// expected: []string{ expected: []string{
// "D0, P[], (!!map)::b: 3\n", "D0, P[], (!!map)::b: 3\n",
// }, },
// }, },
{ {
skipDoc: true, skipDoc: true,
document: "a: []", document: "a: []",
@ -62,22 +62,24 @@ var collectObjectOperatorScenarios = []expressionScenario{
}, },
}, },
{ {
skipDoc: true, skipDoc: true,
document: "{name: Mike}\n", description: "Two documents",
document2: "{name: Bob}\n", document: "{name: Mike}\n",
expression: `{"wrap": .}`, document2: "{name: Bob}\n",
expression: `{"wrap": .}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::wrap: {name: Mike}\n", "D0, P[], (!!map)::wrap: {name: Mike}\n",
"D0, P[], (!!map)::wrap: {name: Bob}\n", "D0, P[], (!!map)::wrap: {name: Bob}\n",
}, },
}, },
{ {
skipDoc: true, skipDoc: true,
document: "{name: Mike}\n---\n{name: Bob}", description: "two embedded documents",
expression: `{"wrap": .}`, document: "{name: Mike}\n---\n{name: Bob}",
expression: `{"wrap": .}`,
expected: []string{ expected: []string{
"D0, P[], (!!map)::wrap: {name: Mike}\n", "D0, P[], (!!map)::wrap: {name: Mike}\n",
"D0, P[], (!!map)::wrap: {name: Bob}\n", "D1, P[], (!!map)::wrap: {name: Bob}\n",
}, },
}, },
{ {
@ -105,8 +107,8 @@ var collectObjectOperatorScenarios = []expressionScenario{
expected: []string{ expected: []string{
"D0, P[], (!!map)::Mike: cat\n", "D0, P[], (!!map)::Mike: cat\n",
"D0, P[], (!!map)::Mike: dog\n", "D0, P[], (!!map)::Mike: dog\n",
"D0, P[], (!!map)::Rosey: monkey\n", "D1, P[], (!!map)::Rosey: monkey\n",
"D0, P[], (!!map)::Rosey: sheep\n", "D1, P[], (!!map)::Rosey: sheep\n",
}, },
}, },
{ {

View File

@ -51,12 +51,9 @@ func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateN
mapPairs, err := crossFunction(d, context.ChildContext(matches), expressionNode, mapPairs, err := crossFunction(d, context.ChildContext(matches), expressionNode,
func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
node := CandidateNode{Kind: MappingNode, Tag: "!!map"} node := CandidateNode{Kind: MappingNode, Tag: "!!map"}
log.Debugf("LHS:", NodeToString(lhs))
log.Debugf("RHS:", NodeToString(rhs)) node.AddKeyValueChild(lhs, rhs)
node.Content = []*CandidateNode{
lhs.unwrapDocument(),
rhs.unwrapDocument(),
}
node.Document = document node.Document = document
return &node, nil return &node, nil

View File

@ -12,6 +12,22 @@ var createMapOperatorScenarios = []expressionScenario{
"D0, P[], (!!seq)::- [{frog: jumps}]\n", "D0, P[], (!!seq)::- [{frog: jumps}]\n",
}, },
}, },
{
skipDoc: true,
description: "sets key properly",
expression: `("frog": "jumps") | .[0][0] | .frog`,
expected: []string{
"D0, P[frog], (!!str)::jumps\n",
},
},
{
skipDoc: true,
description: "sets key properly on map",
expression: `{"frog": "jumps"} | .frog`,
expected: []string{
"D0, P[frog], (!!str)::jumps\n",
},
},
{ {
document: `{name: Mike, age: 32}`, document: `{name: Mike, age: 32}`,
expression: `.name: .age`, expression: `.name: .age`,

View File

@ -54,8 +54,8 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, contex
leadingContent, headComment, footComment := getComments(lhs, rhs) leadingContent, headComment, footComment := getComments(lhs, rhs)
lhs = lhs.unwrapDocument() lhs = lhs.unwrapDocument()
rhs = rhs.unwrapDocument() rhs = rhs.unwrapDocument()
log.Debugf("Multiplying LHS: %v", lhs.Tag) log.Debugf("Multiplying LHS: %v", NodeToString(lhs))
log.Debugf("- RHS: %v", rhs.Tag) log.Debugf("- RHS: %v", NodeToString(rhs))
if rhs.Tag == "!!null" { if rhs.Tag == "!!null" {
return lhs.Copy(), nil return lhs.Copy(), nil
@ -93,7 +93,7 @@ func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, er
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") { } else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
return multiplyFloats(lhs, rhs, lhsIsCustom) return multiplyFloats(lhs, rhs, lhsIsCustom)
} }
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Tag, rhs.Tag) return nil, fmt.Errorf("cannot multiply %v with %v", lhs.Tag, rhs.Tag)
} }
func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode, lhsIsCustom bool) (*CandidateNode, error) { func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode, lhsIsCustom bool) (*CandidateNode, error) {
@ -152,10 +152,14 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
var pathIndexToStartFrom int var pathIndexToStartFrom int
if results.Front() != nil { if results.Front() != nil {
pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).GetPath()) pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).GetPath())
log.Debugf("pathIndexToStartFrom: %v", pathIndexToStartFrom)
} }
for el := results.Front(); el != nil; el = el.Next() { for el := results.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
log.Debugf("*** going to applied assignment to LHS: %v with RHS: %v", NodeToString(lhs), NodeToString(candidate))
if candidate.Tag == "!!merge" { if candidate.Tag == "!!merge" {
continue continue
} }
@ -164,13 +168,14 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debugf("*** applied assignment to LHS: %v", NodeToString(lhs))
} }
return lhs, nil return lhs, nil
} }
func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error { func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
shouldAppendArrays := preferences.AppendArrays shouldAppendArrays := preferences.AppendArrays
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", lhs.GetKey(), rhs.GetKey())
lhsPath := rhs.GetPath()[pathIndexToStartFrom:] lhsPath := rhs.GetPath()[pathIndexToStartFrom:]
log.Debugf("merge - lhsPath %v", lhsPath) log.Debugf("merge - lhsPath %v", lhsPath)