Skip to content

Commit 0b4080f

Browse files
committed
Add append function and fix some issues
1 parent c68c6dd commit 0b4080f

9 files changed

+187
-103
lines changed

context.go

Lines changed: 14 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ type Context struct {
1515
data Value
1616
functions *FunctionCollection
1717
createWhenMissing bool
18-
filters []valueFilterFn
1918
}
2019

2120
func newContextWithFunctions(value interface{}, selector string, functions *FunctionCollection) *Context {
@@ -31,15 +30,22 @@ func newContextWithFunctions(value interface{}, selector string, functions *Func
3130
}
3231

3332
v = Value{
34-
Value: reflectVal,
35-
setFn: func(value Value) {
36-
fmt.Println("root set")
37-
reflectVal.Set(value.Value)
38-
},
33+
Value: reflectVal,
3934
metadata: map[string]interface{}{},
4035
}
4136
}
4237

38+
// Make sure we have an addressable root value.
39+
if !v.CanAddr() {
40+
pointerValue := reflect.New(v.Value.Type())
41+
pointerValue.Elem().Set(v.Value)
42+
v.Value = pointerValue
43+
}
44+
45+
v.setFn = func(value Value) {
46+
v.Unpack().Set(value.Value)
47+
}
48+
4349
if v.metadata == nil {
4450
v.metadata = map[string]interface{}{}
4551
}
@@ -67,11 +73,6 @@ func newContextWithFunctions(value interface{}, selector string, functions *Func
6773
}
6874
}
6975

70-
// NewContext returns a new query context.
71-
func NewContext(value interface{}, selector string) *Context {
72-
return newContextWithFunctions(value, selector, standardFunctions())
73-
}
74-
7576
func newSelectContext(value interface{}, selector string) *Context {
7677
return newContextWithFunctions(value, selector, standardFunctions())
7778
}
@@ -82,9 +83,7 @@ func newPutContext(value interface{}, selector string) *Context {
8283
}
8384

8485
func newDeleteContext(value interface{}, selector string) *Context {
85-
c := newContextWithFunctions(value, selector, standardFunctions())
86-
c.filters = append(c.filters, withoutDeletePlaceholders)
87-
return c
86+
return newContextWithFunctions(value, selector, standardFunctions())
8887
}
8988

9089
func Select(root interface{}, selector string) (Values, error) {
@@ -141,11 +140,7 @@ func (c *Context) CreateWhenMissing() bool {
141140
}
142141

143142
func (c *Context) Data() Value {
144-
if len(c.filters) == 0 {
145-
return c.data
146-
}
147-
changed, _ := rebuildWithFilter(c.data, c.filters...)
148-
return changed
143+
return c.data
149144
}
150145

151146
// Run calls Next repeatedly until no more steps are left.
@@ -199,73 +194,3 @@ func (c *Context) Step(i int) *Step {
199194
}
200195
return c.steps[i]
201196
}
202-
203-
// valueFilterFn represents a filter that can be used to remove values
204-
// from the output data.
205-
// If the filter returns true, the value is removed.
206-
type valueFilterFn func(value Value) bool
207-
208-
// withoutDeletePlaceholders implements valueFilterFn.
209-
func withoutDeletePlaceholders(value Value) bool {
210-
return value.IsDeletePlaceholder()
211-
}
212-
213-
func rebuildWithFilter(value Value, filters ...valueFilterFn) (Value, bool) {
214-
changes := 0
215-
216-
remove := func(v Value) bool {
217-
for _, f := range filters {
218-
if f(v) {
219-
return true
220-
}
221-
}
222-
return false
223-
}
224-
225-
var replacement reflect.Value
226-
changed := false
227-
228-
switch value.Kind() {
229-
230-
case reflect.Map:
231-
replacement = reflect.MakeMap(value.Type())
232-
233-
for _, key := range value.MapKeys() {
234-
v := value.MapIndex(key)
235-
if remove(v) {
236-
changes++
237-
} else {
238-
newV, c := rebuildWithFilter(v, filters...)
239-
if c {
240-
changed = true
241-
}
242-
replacement.SetMapIndex(key.Value, newV.Value)
243-
}
244-
}
245-
246-
case reflect.Slice:
247-
replacement = reflect.MakeSlice(value.Type(), 0, 0)
248-
for i := 0; i < value.Len(); i++ {
249-
v := value.Index(i)
250-
if remove(v) {
251-
changes++
252-
} else {
253-
newV, c := rebuildWithFilter(v, filters...)
254-
if c {
255-
changed = true
256-
}
257-
replacement = reflect.Append(replacement, newV.Value)
258-
}
259-
}
260-
}
261-
262-
if changes > 0 {
263-
changed = true
264-
}
265-
266-
if changed {
267-
return Value{Value: replacement}, true
268-
}
269-
270-
return value, false
271-
}

context_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func sameSlice(x, y []interface{}) bool {
3333

3434
func selectTest(selector string, original interface{}, exp []interface{}) func(t *testing.T) {
3535
return func(t *testing.T) {
36-
c := NewContext(original, selector)
36+
c := newSelectContext(original, selector)
3737

3838
values, err := c.Run()
3939
if err != nil {
@@ -54,7 +54,7 @@ func TestNewContext(t *testing.T) {
5454
orig := map[string]interface{}{
5555
"name": "Tom",
5656
}
57-
ctx := NewContext(orig, "property(name)")
57+
ctx := newPutContext(orig, "property(name)")
5858
s, err := ctx.Run()
5959
if err != nil {
6060
t.Errorf("unexpected error: %v", err)
@@ -81,7 +81,7 @@ func TestNewContext(t *testing.T) {
8181
"last": "Wright",
8282
},
8383
}
84-
ctx := NewContext(orig, "property(name).property(first)")
84+
ctx := newPutContext(orig, "property(name).property(first)")
8585
s, err := ctx.Run()
8686
if err != nil {
8787
t.Errorf("unexpected error: %v", err)
@@ -110,7 +110,7 @@ func TestNewContext(t *testing.T) {
110110
}
111111

112112
orig := User{Name: "Tom"}
113-
ctx := NewContext(&orig, "property(Name)")
113+
ctx := newPutContext(&orig, "property(Name)")
114114
s, err := ctx.Run()
115115
if err != nil {
116116
t.Errorf("unexpected error: %v", err)
@@ -138,7 +138,7 @@ func TestNewContext(t *testing.T) {
138138
}
139139

140140
orig := User{Name: Name{First: "Tom", Last: "Wright"}}
141-
ctx := NewContext(&orig, "property(Name).property(First)")
141+
ctx := newPutContext(&orig, "property(Name).property(First)")
142142
s, err := ctx.Run()
143143
if err != nil {
144144
t.Errorf("unexpected error: %v", err)
@@ -160,7 +160,7 @@ func TestNewContext(t *testing.T) {
160160
orig := []interface{}{
161161
"a", "b", "c",
162162
}
163-
ctx := NewContext(&orig, "index(0)")
163+
ctx := newPutContext(&orig, "index(0)")
164164
s, err := ctx.Run()
165165
if err != nil {
166166
t.Errorf("unexpected error: %v", err)

func.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func standardFunctions() *FunctionCollection {
4747
FirstFunc,
4848
LastFunc,
4949
PropertyFunc,
50+
AppendFunc,
5051

5152
// Filters
5253
FilterFunc,

func_append.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package dasel
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
var AppendFunc = BasicFunction{
9+
name: "append",
10+
runFn: func(c *Context, s *Step, args []string) (Values, error) {
11+
if err := requireNoArgs("append", args); err != nil {
12+
return nil, err
13+
}
14+
15+
input := s.inputs()
16+
17+
if c.CreateWhenMissing() {
18+
input = input.initEmptySlices()
19+
}
20+
21+
res := make(Values, 0)
22+
23+
for _, val := range input {
24+
switch val.Kind() {
25+
case reflect.Slice, reflect.Array:
26+
val = val.Append()
27+
28+
value := val.Index(val.Len() - 1)
29+
res = append(res, value)
30+
default:
31+
return nil, fmt.Errorf("cannot use append selector on non slice/array types")
32+
}
33+
}
34+
35+
return res, nil
36+
},
37+
alternativeSelectorFn: func(part string) *Selector {
38+
if part == "[]" {
39+
return &Selector{
40+
funcName: "append",
41+
funcArgs: []string{},
42+
}
43+
}
44+
return nil
45+
},
46+
}

func_index.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var IndexFunc = BasicFunction{
6565
return res, nil
6666
},
6767
alternativeSelectorFn: func(part string) *Selector {
68-
if strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
68+
if part != "[]" && strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
6969
return &Selector{
7070
funcName: "index",
7171
funcArgs: []string{

func_metadata_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func TestMetadataFunc(t *testing.T) {
1010
orig := []interface{}{
1111
"abc", true, false, 1, 1.1, []interface{}{1},
1212
}
13-
ctx := NewContext(&orig, "all().metadata(type)")
13+
ctx := newSelectContext(&orig, "all().metadata(type)")
1414
s, err := ctx.Run()
1515
if err != nil {
1616
t.Errorf("unexpected error: %v", err)

func_property.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var PropertyFunc = BasicFunction{
5151
if !c.CreateWhenMissing() {
5252
return nil, fmt.Errorf("could not access map index: %w", &ErrPropertyNotFound{Property: property})
5353
}
54+
index = index.asUninitialised()
5455
}
5556
res = append(res, index)
5657
case reflect.Struct:
@@ -63,7 +64,7 @@ var PropertyFunc = BasicFunction{
6364
}
6465
res = append(res, value)
6566
default:
66-
return nil, fmt.Errorf("cannot use property selector on non map/struct types")
67+
return nil, fmt.Errorf("cannot use property selector on non map/struct types: %s", val.Kind().String())
6768
}
6869
}
6970
}

internal/command/put_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,44 @@ func TestPutCommand(t *testing.T) {
8989
nil,
9090
nil,
9191
))
92+
93+
t.Run("SetStringOnExistingIndex", runTest(
94+
[]string{"put", "-r", "json", "-t", "string", "--pretty=false", "-v", "z", "[1]"},
95+
[]byte(`["a","b","c"]`),
96+
newline([]byte(`["a","z","c"]`)),
97+
nil,
98+
nil,
99+
))
100+
101+
t.Run("SetStringOnExistingNestedIndex", runTest(
102+
[]string{"put", "-r", "json", "-t", "string", "--pretty=false", "-v", "z", "[0].[1]"},
103+
[]byte(`[["a","b","c"],["d","e","f"]]`),
104+
newline([]byte(`[["a","z","c"],["d","e","f"]]`)),
105+
nil,
106+
nil,
107+
))
108+
109+
t.Run("AppendStringIndexToRoot", runTest(
110+
[]string{"put", "-r", "json", "-t", "string", "--pretty=false", "-v", "z", "[]"},
111+
[]byte(`[]`),
112+
newline([]byte(`["z"]`)),
113+
nil,
114+
nil,
115+
))
116+
117+
t.Run("AppendStringIndexToNestedSlice", runTest(
118+
[]string{"put", "-r", "json", "-t", "string", "--pretty=false", "-v", "z", "[0].[]"},
119+
[]byte(`[[]]`),
120+
newline([]byte(`[["z"]]`)),
121+
nil,
122+
nil,
123+
))
124+
125+
t.Run("AppendToChainOfMissingSlicesAndProperties", runTest(
126+
[]string{"put", "-r", "json", "-t", "string", "--pretty=false", "-v", "Tom", "users.[].name.first"},
127+
[]byte(`{}`),
128+
newline([]byte(`{"users":[{"name":{"first":"Tom"}}]}`)),
129+
nil,
130+
nil,
131+
))
92132
}

0 commit comments

Comments
 (0)