Skip to content

Commit 4677e66

Browse files
committed
feat: Import post-processing
Adds a second `interface` for importing, `ImportProcessor`. If set on a VM (non-nil), it is asked to perhaps convert the Importer result before importCache stores it. This can for example be used to enhance what `import` is capable of, so that it also reads `.yaml` files, etc. This is a feature we once had in Tanka, but had to disable as we were not able to differentiate between `import` and `importstr` based on the `Importer` interface. This change allows us to do that, as `ImportProcessor` is only ever considered for `import`.
1 parent d1c1457 commit 4677e66

File tree

2 files changed

+45
-17
lines changed

2 files changed

+45
-17
lines changed

imports.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ type Importer interface {
5050
Import(importedFrom, importedPath string) (contents Contents, foundAt string, err error)
5151
}
5252

53+
// ImportProcessor allows to dynamically modify the result returned by the
54+
// Importer, for example to convert other formats to a Jsonnet snippet before
55+
// evaluating.
56+
type ImportProcessor interface {
57+
Process(contents Contents, foundAt string) (Contents, error)
58+
}
59+
5360
// Contents is a representation of imported data. It is a simple
5461
// string wrapper, which makes it easier to enforce the caching policy.
5562
type Contents struct {
@@ -79,12 +86,14 @@ type importCache struct {
7986
astCache map[string]ast.Node
8087
codeCache map[string]potentialValue
8188
importer Importer
89+
processor ImportProcessor
8290
}
8391

8492
// makeImportCache creates an importCache using an Importer.
85-
func makeImportCache(importer Importer) *importCache {
93+
func makeImportCache(importer Importer, processor ImportProcessor) *importCache {
8694
return &importCache{
8795
importer: importer,
96+
processor: processor,
8897
foundAtVerification: make(map[string]Contents),
8998
astCache: make(map[string]ast.Node),
9099
codeCache: make(map[string]potentialValue),
@@ -115,9 +124,19 @@ func (cache *importCache) importAST(importedFrom, importedPath string) (ast.Node
115124
if err != nil {
116125
return nil, "", err
117126
}
127+
118128
if cachedNode, isCached := cache.astCache[foundAt]; isCached {
119129
return cachedNode, foundAt, nil
120130
}
131+
132+
if cache.processor != nil {
133+
c, err := cache.processor.Process(contents, foundAt)
134+
if err != nil {
135+
return nil, "", err
136+
}
137+
contents = c
138+
}
139+
121140
node, err := program.SnippetToAST(foundAt, contents.String())
122141
cache.astCache[foundAt] = node
123142
return node, foundAt, err

vm.go

+25-16
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ import (
3131
// VM is the core interpreter and is the touchpoint used to parse and execute
3232
// Jsonnet.
3333
type VM struct {
34-
MaxStack int
35-
ext vmExtMap
36-
tla vmExtMap
37-
nativeFuncs map[string]*NativeFunction
38-
importer Importer
39-
ErrorFormatter ErrorFormatter
40-
StringOutput bool
41-
importCache *importCache
34+
MaxStack int
35+
ext vmExtMap
36+
tla vmExtMap
37+
nativeFuncs map[string]*NativeFunction
38+
importer Importer
39+
importProcessor ImportProcessor
40+
ErrorFormatter ErrorFormatter
41+
StringOutput bool
42+
importCache *importCache
4243
}
4344

4445
// External variable or top level argument provided before execution
@@ -56,20 +57,21 @@ type vmExtMap map[string]vmExt
5657
func MakeVM() *VM {
5758
defaultImporter := &FileImporter{}
5859
return &VM{
59-
MaxStack: 500,
60-
ext: make(vmExtMap),
61-
tla: make(vmExtMap),
62-
nativeFuncs: make(map[string]*NativeFunction),
63-
ErrorFormatter: &termErrorFormatter{pretty: false, maxStackTraceSize: 20},
64-
importer: &FileImporter{},
65-
importCache: makeImportCache(defaultImporter),
60+
MaxStack: 500,
61+
ext: make(vmExtMap),
62+
tla: make(vmExtMap),
63+
nativeFuncs: make(map[string]*NativeFunction),
64+
ErrorFormatter: &termErrorFormatter{pretty: false, maxStackTraceSize: 20},
65+
importer: &FileImporter{},
66+
importProcessor: nil,
67+
importCache: makeImportCache(defaultImporter, nil),
6668
}
6769
}
6870

6971
// Fully flush cache. This should be executed when we are no longer sure that the source files
7072
// didn't change, for example when the importer changed.
7173
func (vm *VM) flushCache() {
72-
vm.importCache = makeImportCache(vm.importer)
74+
vm.importCache = makeImportCache(vm.importer, vm.importProcessor)
7375
}
7476

7577
// Flush value cache. This should be executed when calculated values may no longer be up to date,
@@ -110,6 +112,13 @@ func (vm *VM) Importer(i Importer) {
110112
vm.flushCache()
111113
}
112114

115+
// ImportProcessor sets the ImportProcesor to use during evaluation
116+
// (post-processing callback)
117+
func (vm *VM) ImportProcessor(i ImportProcessor) {
118+
vm.importProcessor = i
119+
vm.flushCache()
120+
}
121+
113122
// NativeFunction registers a native function.
114123
func (vm *VM) NativeFunction(f *NativeFunction) {
115124
vm.nativeFuncs[f.Name] = f

0 commit comments

Comments
 (0)