1
1
mirror of https://github.com/wader/fq.git synced 2024-12-26 23:15:04 +03:00

Merge pull request #140 from wader/interp-abs-path-on-error

interp: Use absolute path in errors
This commit is contained in:
Mattias Wadman 2022-02-11 17:53:05 +01:00 committed by GitHub
commit 8e6e992c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 28 deletions

View File

@ -53,7 +53,7 @@ var functionRegisterFns []func(i *Interp) []Function
func init() {
functionRegisterFns = append(functionRegisterFns, func(i *Interp) []Function {
return []Function{
{"_readline", 0, 2, i.readline, nil},
{"_readline", 0, 2, i._readline, nil},
{"eval", 1, 2, nil, i.eval},
{"_stdin", 0, 0, nil, i.makeStdioFn(i.os.Stdin())},
{"_stdout", 0, 0, nil, i.makeStdioFn(i.os.Stdout())},
@ -324,10 +324,10 @@ type evalContext struct {
type Interp struct {
registry *registry.Registry
os OS
initFqQuery *gojq.Query
initQuery *gojq.Query
includeCache map[string]*gojq.Query
interruptStack *ctxstack.Stack
// global state, is ref as Interp i cloned per eval
// global state, is ref as Interp is cloned per eval
state *interface{}
// new for each run, other values are copied by value
@ -343,7 +343,7 @@ func New(os OS, registry *registry.Registry) (*Interp, error) {
}
i.includeCache = map[string]*gojq.Query{}
i.initFqQuery, err = gojq.Parse(initSource)
i.initQuery, err = gojq.Parse(initSource)
if err != nil {
return nil, fmt.Errorf("init:%s: %w", queryErrorPosition(initSource, err), err)
}
@ -414,7 +414,7 @@ func (i *Interp) Main(ctx context.Context, output Output, versionStr string) err
return nil
}
func (i *Interp) readline(c interface{}, a []interface{}) interface{} {
func (i *Interp) _readline(c interface{}, a []interface{}) interface{} {
var opts struct {
Complete string `mapstructure:"complete"`
Timeout float64 `mapstructure:"timeout"`
@ -616,47 +616,55 @@ func (i *Interp) _canDisplay(c interface{}, a []interface{}) interface{} {
type pathResolver struct {
prefix string
open func(filename string) (io.ReadCloser, error)
open func(filename string) (io.ReadCloser, string, error)
}
func (i *Interp) lookupPathResolver(filename string) (pathResolver, bool) {
func (i *Interp) lookupPathResolver(filename string) (pathResolver, error) {
configDir, err := i.os.ConfigDir()
if err != nil {
return pathResolver{}, err
}
resolvePaths := []pathResolver{
{
"@builtin/",
func(filename string) (io.ReadCloser, error) { return builtinFS.Open(filename) },
},
{
"@config/", func(filename string) (io.ReadCloser, error) {
configDir, err := i.os.ConfigDir()
if err != nil {
return nil, err
}
return i.os.FS().Open(path.Join(configDir, filename))
func(filename string) (io.ReadCloser, string, error) {
f, err := builtinFS.Open(filename)
return f, "@builtin/" + filename, err
},
},
{
"", func(filename string) (io.ReadCloser, error) {
"@config/", func(filename string) (io.ReadCloser, string, error) {
p := path.Join(configDir, filename)
f, err := i.os.FS().Open(p)
return f, p, err
},
},
{
"", func(filename string) (io.ReadCloser, string, error) {
if path.IsAbs(filename) {
return i.os.FS().Open(filename)
f, err := i.os.FS().Open(filename)
return f, filename, err
}
// TODO: jq $ORIGIN
for _, includePath := range append([]string{"./"}, i.includePaths()...) {
p := path.Join(includePath, filename)
if f, err := i.os.FS().Open(path.Join(includePath, filename)); err == nil {
return f, nil
return f, p, nil
}
}
return nil, &fs.PathError{Op: "open", Path: filename, Err: fs.ErrNotExist}
return nil, "", &fs.PathError{Op: "open", Path: filename, Err: fs.ErrNotExist}
},
},
}
for _, p := range resolvePaths {
if strings.HasPrefix(filename, p.prefix) {
return p, true
return p, nil
}
}
return pathResolver{}, false
return pathResolver{}, fmt.Errorf("could not resolve path: %s", filename)
}
func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilename string, output io.Writer) (gojq.Iter, error) {
@ -701,7 +709,7 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
compilerOpts = append(compilerOpts, gojq.WithVariables(variableNames))
compilerOpts = append(compilerOpts, gojq.WithModuleLoader(loadModule{
init: func() ([]*gojq.Query, error) {
return []*gojq.Query{i.initFqQuery}, nil
return []*gojq.Query{i.initQuery}, nil
},
load: func(name string) (*gojq.Query, error) {
if err := ctx.Err(); err != nil {
@ -719,9 +727,9 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
}
filename = filename + ".jq"
pr, ok := i.lookupPathResolver(filename)
if !ok {
return nil, fmt.Errorf("could not resolve path: %s", filename)
pr, err := i.lookupPathResolver(filename)
if err != nil {
return nil, err
}
if q, ok := ni.includeCache[filename]; ok {
@ -730,7 +738,7 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
filenamePart := strings.TrimPrefix(filename, pr.prefix)
f, err := pr.open(filenamePart)
f, absPath, err := pr.open(filenamePart)
if err != nil {
if !isTry {
return nil, err
@ -750,7 +758,7 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
return nil, compileError{
err: err,
what: "parse",
filename: filenamePart,
filename: absPath,
pos: p,
}
}

View File

@ -1,5 +1,7 @@
/library/a.jq:
def a: "a";
/config/has_error.jq:
)
$ fq -L /library -n 'include "a"; a'
"a"
$ fq --include-path /library -n 'include "a"; a'
@ -12,3 +14,13 @@ $ fq -L /wrong -n 'include "a"; a'
exitcode: 3
stderr:
error: arg:1:0: open a.jq: file does not exist
$ fq -n 'include "@config/a";'
exitcode: 3
stderr:
error: arg:1:0: open testdata/config/a.jq: no such file or directory
$ fq -n 'include "@config/missing?";'
null
$ fq -n 'include "@config/has_error?";'
exitcode: 3
stderr:
error: arg:1:0: /config/has_error.jq:1:1: parse: unexpected token ")"