@@ -19,7 +19,6 @@ package node
1919import (
2020 "fmt"
2121 "reflect"
22- "strings"
2322
2423 "github.com/go-juicedev/juice/driver"
2524 "github.com/go-juicedev/juice/eval"
@@ -79,65 +78,22 @@ type ForeachNode struct {
7978
8079// Accept accepts parameters and returns query and arguments.
8180func (f ForeachNode ) Accept (translator driver.Translator , p eval.Parameter ) (query string , args []any , err error ) {
82- builder := getStringBuilder ()
83- defer putStringBuilder (builder )
84-
85- // Estimation logic for pre-allocation
86- var totalPlaceholders int
87- for _ , node := range f .Nodes {
88- if t , ok := node .(* TextNode ); ok {
89- for _ , token := range t .tokens {
90- if ! token .isFormat {
91- totalPlaceholders ++
92- }
93- }
94- } else {
95- totalPlaceholders += 2
96- }
97- }
98-
99- // We don't know the exact length yet, but we can guess from p
100- coll , exists := p .Get (f .Collection )
101- var length int
102- if exists {
103- c := coll
104- for c .Kind () == reflect .Interface || c .Kind () == reflect .Ptr {
105- if c .IsNil () {
106- break
107- }
108- c = c .Elem ()
109- }
110- if c .Kind () == reflect .Slice || c .Kind () == reflect .Array || c .Kind () == reflect .Map {
111- length = c .Len ()
112- }
113- }
114-
115- argsList := make ([]any , 0 , length * totalPlaceholders )
116-
117- if err = f .AcceptTo (translator , p , builder , & argsList ); err != nil {
118- return "" , nil , err
119- }
120-
121- return builder .String (), argsList , nil
122- }
123-
124- func (f ForeachNode ) AcceptTo (translator driver.Translator , p eval.Parameter , builder * strings.Builder , args * []any ) error {
12581 p = f .BindNodes .ConvertParameter (p )
12682
12783 // if item already exists
12884 if _ , exists := p .Get (f .Item ); exists {
129- return fmt .Errorf ("item %s already exists" , f .Item )
85+ return "" , nil , fmt .Errorf ("item %s already exists" , f .Item )
13086 }
13187
13288 // one collection from parameter
13389 value , exists := p .Get (f .Collection )
13490 if ! exists {
135- return fmt .Errorf ("collection %s not found" , f .Collection )
91+ return "" , nil , fmt .Errorf ("collection %s not found" , f .Collection )
13692 }
13793
13894 // if valueItem can not be iterated
13995 if ! value .CanInterface () {
140- return fmt .Errorf ("collection %s can not be iterated" , f .Collection )
96+ return "" , nil , fmt .Errorf ("collection %s can not be iterated" , f .Collection )
14197 }
14298
14399 // if valueItem is not a slice
@@ -147,28 +103,33 @@ func (f ForeachNode) AcceptTo(translator driver.Translator, p eval.Parameter, bu
147103
148104 switch value .Kind () {
149105 case reflect .Array , reflect .Slice :
150- return f .acceptSliceTo (value , translator , p , builder , args )
106+ return f .acceptSlice (value , translator , p )
151107 case reflect .Map :
152- return f .acceptMapTo (value , translator , p , builder , args )
108+ return f .acceptMap (value , translator , p )
153109 default :
154- return fmt .Errorf ("collection %s is not a slice or map" , f .Collection )
110+ return "" , nil , fmt .Errorf ("collection %s is not a slice or map" , f .Collection )
155111 }
156112}
157113
158- func (f ForeachNode ) acceptSliceTo (value reflect.Value , translator driver.Translator , p eval.Parameter , builder * strings. Builder , args * []any ) error {
114+ func (f ForeachNode ) acceptSlice (value reflect.Value , translator driver.Translator , p eval.Parameter ) ( string , []any , error ) {
159115 sliceLength := value .Len ()
160116
161117 if sliceLength == 0 {
162- return nil
118+ return "" , nil , nil
163119 }
164120
121+ builder := getStringBuilder ()
122+ defer putStringBuilder (builder )
123+
165124 builder .WriteString (f .Open )
166125
167126 end := sliceLength - 1
168127
169128 // Create and reuse foreachParameter outside the loop to avoid allocations per iteration
170129 fp := eval .NewForeachParameter (p , f .Item , f .Index )
171130
131+ var args []any
132+
172133 for i := 0 ; i < sliceLength ; i ++ {
173134
174135 fp .ItemValue = value .Index (i )
@@ -177,21 +138,15 @@ func (f ForeachNode) acceptSliceTo(value reflect.Value, translator driver.Transl
177138 }
178139
179140 for _ , node := range f .Nodes {
180- if nw , ok := node .(NodeWriter ); ok {
181- if err := nw .AcceptTo (translator , fp , builder , args ); err != nil {
182- return err
183- }
184- } else {
185- q , a , err := node .Accept (translator , fp )
186- if err != nil {
187- return err
188- }
189- if len (q ) > 0 {
190- builder .WriteString (q )
191- }
192- if len (a ) > 0 {
193- * args = append (* args , a ... )
194- }
141+ q , a , err := node .Accept (translator , fp )
142+ if err != nil {
143+ return "" , nil , err
144+ }
145+ if len (q ) > 0 {
146+ builder .WriteString (q )
147+ }
148+ if len (a ) > 0 {
149+ args = append (args , a ... )
195150 }
196151 }
197152
@@ -201,19 +156,21 @@ func (f ForeachNode) acceptSliceTo(value reflect.Value, translator driver.Transl
201156 fp .Clear ()
202157 }
203158
204- // if sliceLength is not zero, add close
205159 builder .WriteString (f .Close )
206160
207- return nil
161+ return builder . String (), args , nil
208162}
209163
210- func (f ForeachNode ) acceptMapTo (value reflect.Value , translator driver.Translator , p eval.Parameter , builder * strings. Builder , args * []any ) error {
164+ func (f ForeachNode ) acceptMap (value reflect.Value , translator driver.Translator , p eval.Parameter ) ( string , []any , error ) {
211165 mapLength := value .Len ()
212166
213167 if mapLength == 0 {
214- return nil
168+ return "" , nil , nil
215169 }
216170
171+ builder := getStringBuilder ()
172+ defer putStringBuilder (builder )
173+
217174 builder .WriteString (f .Open )
218175
219176 end := mapLength - 1
@@ -225,27 +182,23 @@ func (f ForeachNode) acceptMapTo(value reflect.Value, translator driver.Translat
225182
226183 iter := value .MapRange ()
227184
185+ var args []any
186+
228187 for iter .Next () {
229188
230189 fp .ItemValue = iter .Value ()
231190 fp .IndexValue = iter .Key ()
232191
233192 for _ , node := range f .Nodes {
234- if nw , ok := node .(NodeWriter ); ok {
235- if err := nw .AcceptTo (translator , fp , builder , args ); err != nil {
236- return err
237- }
238- } else {
239- q , a , err := node .Accept (translator , fp )
240- if err != nil {
241- return err
242- }
243- if len (q ) > 0 {
244- builder .WriteString (q )
245- }
246- if len (a ) > 0 {
247- * args = append (* args , a ... )
248- }
193+ q , a , err := node .Accept (translator , fp )
194+ if err != nil {
195+ return "" , nil , err
196+ }
197+ if len (q ) > 0 {
198+ builder .WriteString (q )
199+ }
200+ if len (a ) > 0 {
201+ args = append (args , a ... )
249202 }
250203 }
251204
@@ -260,7 +213,7 @@ func (f ForeachNode) acceptMapTo(value reflect.Value, translator driver.Translat
260213
261214 builder .WriteString (f .Close )
262215
263- return nil
216+ return builder . String (), args , nil
264217}
265218
266- var _ NodeWriter = (* ForeachNode )(nil )
219+ var _ Node = (* ForeachNode )(nil )
0 commit comments