Skip to content

Commit 6077559

Browse files
authored
feat(arg): support recursive from (#284)
Adds support for recursive from resolving, with no inf recursion prevention. Callers should assume that inf recursion will NOT be supported in the future (not to mention it wouldn't work anyways...) A future PR will add from strack tracing to limit the depth and prevent inf recurison. Closes #283.
1 parent 3c335a4 commit 6077559

File tree

2 files changed

+88
-9
lines changed

2 files changed

+88
-9
lines changed

internal/codegen/tpl_stencil_arg.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package codegen
2020

2121
import (
22-
"context"
2322
"fmt"
2423

2524
"go.rgst.io/stencil/v2/internal/dotnotation"
@@ -34,10 +33,6 @@ func (s *TplStencil) Arg(pth string) (interface{}, error) {
3433
return nil, fmt.Errorf("path cannot be empty")
3534
}
3635

37-
// This is a TODO because I don't know if template functions
38-
// can even get a context passed to them
39-
ctx := context.TODO()
40-
4136
mf := s.t.Module.Manifest
4237
if _, ok := mf.Arguments[pth]; !ok {
4338
return "", fmt.Errorf("module %q doesn't list argument %q as an argument in its manifest", s.t.Module.Name, pth)
@@ -47,7 +42,7 @@ func (s *TplStencil) Arg(pth string) (interface{}, error) {
4742
// If there's a "from" we should handle that now before anything else,
4843
// so that its definition is used.
4944
if arg.From != "" {
50-
fromArg, err := s.resolveFrom(ctx, pth, &arg)
45+
fromArg, err := s.resolveFrom(pth, &arg)
5146
if err != nil {
5247
return "", err
5348
}
@@ -120,8 +115,8 @@ func (s *TplStencil) resolveDefault(pth string, arg *configuration.Argument) (in
120115
}
121116

122117
// resolveFrom resoles the "from" field of an argument
123-
func (s *TplStencil) resolveFrom(_ context.Context, pth string, arg *configuration.Argument) (*configuration.Argument, error) {
124-
foundModuleInDeps := false
118+
func (s *TplStencil) resolveFrom(pth string, arg *configuration.Argument) (*configuration.Argument, error) {
119+
var foundModuleInDeps bool
125120
// Ensure that the module imports the referenced module
126121
for _, m := range s.t.Module.Manifest.Modules {
127122
if m.Name == arg.From {
@@ -160,6 +155,17 @@ func (s *TplStencil) resolveFrom(_ context.Context, pth string, arg *configurati
160155
s.t.Module.Name, pth, arg.From,
161156
)
162157
}
158+
159+
// If we are, ourselves, a from then we need to resolve it again.
160+
if fromArg.From != "" {
161+
// Reusing 'pth' is safe because from key's must be equal.
162+
recurFromArg, err := s.resolveFrom(pth, &fromArg)
163+
if err != nil {
164+
return nil, fmt.Errorf("recursive from resolve failed for module %s -> %s: %w", arg.From, fromArg.From, err)
165+
}
166+
return recurFromArg, nil
167+
}
168+
163169
return &fromArg, nil
164170
}
165171

internal/codegen/tpl_stencil_arg_test.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,16 @@ func fakeTemplateMultipleModules(t *testing.T, manifestArgs map[string]interface
8282
continue
8383
}
8484

85+
// Depend on all modules that come after us to allow for from calls.
86+
var deps []*configuration.TemplateRepository
87+
for j := i; j < len(args); j++ {
88+
deps = append(deps, &configuration.TemplateRepository{Name: fmt.Sprintf("test-%d", j)})
89+
}
90+
8591
man := &configuration.TemplateRepositoryManifest{
8692
Name: fmt.Sprintf("test-%d", i),
8793
Arguments: args[i],
94+
Modules: deps,
8895
}
8996
m, err := modulestest.NewModuleFromTemplates(man, "testdata/args/test.tpl")
9097
if err != nil {
@@ -124,7 +131,7 @@ func fakeTemplateMultipleModules(t *testing.T, manifestArgs map[string]interface
124131
// which we've created earlier after loading the module in the
125132
// NewModuleFromTemplates call. This won't be used, but it's
126133
// enough to set up the correct environment for running template test functions.
127-
tpls, err := test.s.getTemplates(context.Background(), log)
134+
tpls, err := test.s.getTemplates(t.Context(), log)
128135
if err != nil {
129136
t.Fatal(err)
130137
}
@@ -304,6 +311,72 @@ func TestTplStencil_Arg(t *testing.T) {
304311
want: nil,
305312
wantErr: true,
306313
},
314+
{
315+
name: "should support recursive from",
316+
fields: fakeTemplateMultipleModules(t,
317+
map[string]interface{}{
318+
"hello": "world",
319+
},
320+
// test-0
321+
map[string]configuration.Argument{
322+
"hello": {
323+
From: "test-1",
324+
},
325+
},
326+
// test-1
327+
map[string]configuration.Argument{
328+
"hello": {
329+
From: "test-2",
330+
},
331+
},
332+
// test-2
333+
map[string]configuration.Argument{
334+
"hello": {
335+
Schema: map[string]interface{}{
336+
"type": "string",
337+
},
338+
},
339+
},
340+
),
341+
args: args{
342+
pth: "hello",
343+
},
344+
want: "world",
345+
wantErr: false,
346+
},
347+
{
348+
name: "should support recursive from schema fail",
349+
fields: fakeTemplateMultipleModules(t,
350+
map[string]interface{}{
351+
"hello": "world",
352+
},
353+
// test-0
354+
map[string]configuration.Argument{
355+
"hello": {
356+
From: "test-1",
357+
},
358+
},
359+
// test-1
360+
map[string]configuration.Argument{
361+
"hello": {
362+
From: "test-2",
363+
},
364+
},
365+
// test-2
366+
map[string]configuration.Argument{
367+
"hello": {
368+
Schema: map[string]interface{}{
369+
"type": "number",
370+
},
371+
},
372+
},
373+
),
374+
args: args{
375+
pth: "hello",
376+
},
377+
want: nil,
378+
wantErr: true,
379+
},
307380
}
308381
for _, tt := range tests {
309382
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)