Skip to content

Commit

Permalink
export NewModuleLoader for library users (close #27)
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Jun 25, 2020
1 parent b72580a commit e25a968
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func main() {
[`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#Compile) allows to configure the following compiler options.
- [`gojq.WithModuleLoader`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#WithModuleLoader) allows to load modules. By default, the module feature is disabled.
- [`gojq.WithModuleLoader`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#WithModuleLoader) allows to load modules. By default, the module feature is disabled. If you want to load modules from the filesystem, use [`gojq.NewModuleLoader`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#NewModuleLoader).
- [`gojq.WithEnvironLoader`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#WithEnvironLoader) allows to configure the environment variables referenced by `env` and `$ENV`. By default, OS environment variables are not accessible due to security reason. You can use `gojq.WithEnvironLoader(os.Environ)` if you want.
- [`gojq.WithVariables`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#WithVariables) allows to configure the variables which can be used in the query. Pass the values of the variables to [`code.Run`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#Code.Run) in the same order.
- [`gojq.WithInputIter`](https://pkg.go.dev/github.com/itchyny/gojq?tab=doc#WithInputIter) allows to use `input` and `inputs` functions. By default, these functions are disabled.
Expand Down
13 changes: 11 additions & 2 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ Synopsis:
iter := cli.createInputIter(args)
defer iter.Close()
code, err := gojq.Compile(query,
gojq.WithModuleLoader(&moduleLoader{modulePaths}),
gojq.WithModuleLoader(gojq.NewModuleLoader(modulePaths)),
gojq.WithEnvironLoader(os.Environ),
gojq.WithVariables(cli.argnames),
gojq.WithInputIter(iter),
Expand All @@ -209,7 +209,16 @@ Synopsis:
QueryParseError() (string, string, string, error)
}); ok {
typ, name, query, err := err.QueryParseError()
return &queryParseError{typ, fname + ":" + name, query, err}
if _, err := os.Stat(name); os.IsNotExist(err) {
name = fname + ":" + name
}
return &queryParseError{typ, name, query, err}
}
if err, ok := err.(interface {
JSONParseError() (string, string, error)
}); ok {
fname, query, err := err.JSONParseError()
return &compileError{&jsonParseError{fname, query, err}}
}
return &compileError{err}
}
Expand Down
2 changes: 1 addition & 1 deletion cli/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5119,7 +5119,7 @@
- 'import "4" as $x; $x'
input: 0
error: |
invalid json: testdata/4.json
compile error: invalid json: testdata/4.json
bar
^ invalid character 'b' looking for beginning of value
exit_code: 3
Expand Down
26 changes: 26 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@ func (err *getpathError) Error() string {
return fmt.Sprintf("cannot getpath with %s against: %s", previewValue(err.path), typeErrorPreview(err.v))
}

type queryParseError struct {
typ, fname, contents string
err error
}

func (err *queryParseError) QueryParseError() (string, string, string, error) {
return err.typ, err.fname, err.contents, err.err
}

func (err *queryParseError) Error() string {
return fmt.Sprintf("invalid %s: %s: %s", err.typ, err.fname, err.err)
}

type jsonParseError struct {
fname, contents string
err error
}

func (err *jsonParseError) JSONParseError() (string, string, error) {
return err.fname, err.contents, err.err
}

func (err *jsonParseError) Error() string {
return fmt.Sprintf("invalid json: %s: %s", err.fname, err.err)
}

func typeErrorPreview(v interface{}) string {
p := preview(v)
if p != "" {
Expand Down
51 changes: 37 additions & 14 deletions module_loader.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
package cli
package gojq

import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"

"github.com/itchyny/gojq"
)

type moduleLoader struct {
paths []string
}

func (l *moduleLoader) LoadInitModules() ([]*gojq.Module, error) {
var ms []*gojq.Module
// NewModuleLoader creates a new ModuleLoader reading local modules in the paths.
func NewModuleLoader(paths []string) ModuleLoader {
return &moduleLoader{paths}
}

func (l *moduleLoader) LoadInitModules() ([]*Module, error) {
var ms []*Module
for _, path := range l.paths {
if filepath.Base(path) != ".jq" {
continue
Expand Down Expand Up @@ -43,11 +49,11 @@ func (l *moduleLoader) LoadInitModules() ([]*gojq.Module, error) {
return ms, nil
}

func (l *moduleLoader) LoadModule(string) (*gojq.Module, error) {
panic("moduleLoader#LoadModule: unreachable")
func (l *moduleLoader) LoadModule(string) (*Module, error) {
panic("LocalModuleLoader#LoadModule: unreachable")
}

func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]interface{}) (*gojq.Module, error) {
func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]interface{}) (*Module, error) {
path, err := l.lookupModule(name, ".jq", meta)
if err != nil {
return nil, err
Expand All @@ -68,7 +74,25 @@ func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]interface{}
if err != nil {
return nil, err
}
return slurpFile(path)
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var vals []interface{}
var buf bytes.Buffer
dec := json.NewDecoder(io.TeeReader(f, &buf))
for {
var val interface{}
if err := dec.Decode(&val); err != nil {
if err == io.EOF {
break
}
return nil, &jsonParseError{path, buf.String(), err}
}
vals = append(vals, val)
}
return vals, nil
}

func (l *moduleLoader) lookupModule(name, extension string, meta map[string]interface{}) (string, error) {
Expand All @@ -90,9 +114,8 @@ func (l *moduleLoader) lookupModule(name, extension string, meta map[string]inte
}

// This is a dirty hack to implement the "search" field.
// Note that gojq package should not depend on the filesystem.
func parseModule(path, cnt string) (*gojq.Module, error) {
m, err := gojq.ParseModule(cnt)
func parseModule(path, cnt string) (*Module, error) {
m, err := ParseModule(cnt)
if err != nil {
return nil, err
}
Expand All @@ -102,9 +125,9 @@ func parseModule(path, cnt string) (*gojq.Module, error) {
}
i.Meta.KeyVals = append(
i.Meta.KeyVals,
gojq.ConstObjectKeyVal{
ConstObjectKeyVal{
Key: "$$path",
Val: &gojq.ConstTerm{Str: strconv.Quote(path)},
Val: &ConstTerm{Str: strconv.Quote(path)},
},
)
}
Expand Down
1 change: 1 addition & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package gojq
type CompilerOption func(*compiler)

// WithModuleLoader is a compiler option for module loader.
// If you want to load modules from the filesystem, use NewModuleLoader.
func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption {
return func(c *compiler) {
c.moduleLoader = moduleLoader
Expand Down

0 comments on commit e25a968

Please sign in to comment.