-
Notifications
You must be signed in to change notification settings - Fork 5
/
main.go
149 lines (127 loc) · 4.08 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
import (
"bytes"
_ "embed"
"fmt"
"os"
"strings"
"text/template"
"github.com/traefik/yaegi/interp"
"github.com/traefik/yaegi/stdlib"
)
//go:embed program.go.tmpl
var programTmpl string
//go:embed gq/gq.go
var prelude string
func main() {
if err := run(); err != nil {
fmt.Println("Error: ", err)
fmt.Println(help)
os.Exit(1)
}
}
func run() error {
// Check for help invocation.
for _, a := range os.Args {
if a == "-h" || a == "--help" {
fmt.Println(help)
return nil
}
}
// prepare program
tmpl, err := template.New("tmpl").Parse(programTmpl)
if err != nil {
return fmt.Errorf("parse template: %w", err)
}
program := strings.Join(os.Args[1:], " ")
type Dot struct {
Program string
Prelude string
}
var runnable bytes.Buffer
err = tmpl.Execute(&runnable, Dot{Program: program, Prelude: strings.ReplaceAll(prelude, "package gq", "")})
if err != nil {
return fmt.Errorf("generating program: %w", err)
}
// execute
i := interp.New(interp.Options{})
i.Use(stdlib.Symbols)
p, err := i.Compile(runnable.String())
if err != nil {
return fmt.Errorf("compile generated program: %w", err)
}
_, err = i.Execute(p)
if err != nil {
return fmt.Errorf("execute program: %w", err)
}
/*
// save to disk
f, err := os.CreateTemp(os.TempDir(), "*.go")
if err != nil {
return fmt.Errorf("create temp file: %w", err)
}
defer func() {
f.Close()
os.Remove(f.Name())
}()
_, err = io.Copy(f, bytes.NewReader(runnable.Bytes()))
if err != nil {
return fmt.Errorf("write to temp file: %w", err)
}
err = f.Close()
if err != nil {
return fmt.Errorf("closing temp file: %w", err)
}
// execute new program
cmd := exec.Command("go", "run", f.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
if cmd.ProcessState.ExitCode() == 1 {
// the generated program compiled successfully, but had some error output. We should just exit immediately.
os.Exit(1)
}
return fmt.Errorf("running generated program: %w", err)
}
*/
return nil
}
const help = `gq executes go scripts against json.
Usage: cat f.json | gq 'j.G("hello").G("world")
The script you pass into 'gq' has access to a variable called 'j' which contains the parsed JSON. After your script is run, whatever remains in 'j' is printed. 'j' is of type *Node. The following functions are available to the script:
func (n *Node) Array() []interface{}
Fetches the current value of the node as an array, if possible. Otherwise,
sets the error for the node.
func (n *Node) Filter(f func(*Node) bool) *Node
Filter removes nodes from the interior of the given map or array node if
they fail the filter function.
func (n *Node) Float() float64
Fetches the current value of the node as float, if possible. Otherwise, sets
the error for the node.
func (n *Node) G(keys ...string) *Node
G fetches the values at the given keys in the map node. If there is only one
key, returns that key's value. If there are many keys, returns an array of
their non-null values. If this is not a map node, returns an error node. If
none of the keys are not found, returns null.
func (n *Node) I(is ...int) *Node
I fetches the value at the given array indices. If this is not an array
node, returns an error node. If none of the indices are found, returns null.
If there is only one index given, returns just that value. Otherwise returns
an array of values.
func (n *Node) Int() int
Fetches the current value of the node as an integer, if possible. Otherwise,
sets the error for the node.
func (n Node) IsMap() bool
Checks if this is a map node
func (n *Node) Map(f func(*Node) *Node) *Node
Map replaces nodes from the interior of the given map or array node with the
output of the function.
func (n *Node) MapValue() map[string]interface{}
Fetches the current value of the node as a map, if possible. Otherwise, sets
the error for the node.
func (n *Node) Str() string
Fetches the current value of the node as string, if possible. Otherwise,
sets the error for the node.
`