Skip to content

Commit 90aa9b7

Browse files
committed
Create a JqRepl struct to encapsualte state
This is the main guts of our repl and provides us a place to start adding methods and features.
1 parent cf7b132 commit 90aa9b7

File tree

2 files changed

+121
-54
lines changed

2 files changed

+121
-54
lines changed

jqrepl.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package jqrepl
2+
3+
import (
4+
"fmt"
5+
"io"
6+
7+
"github.com/ashb/jqrepl/jq"
8+
"gopkg.in/chzyer/readline.v1"
9+
)
10+
11+
type JqRepl struct {
12+
programCounter int
13+
promptTemplate string
14+
reader *readline.Instance
15+
libJq *jq.Jq
16+
input *jq.Jv
17+
}
18+
19+
func New() (*JqRepl, error) {
20+
repl := JqRepl{
21+
promptTemplate: "\033[0;36m%3d »\033[0m",
22+
}
23+
var err error
24+
repl.reader, err = readline.New(repl.currentPrompt())
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
repl.libJq, err = jq.New()
30+
if err != nil {
31+
repl.reader.Close()
32+
return nil, err
33+
}
34+
35+
return &repl, nil
36+
}
37+
38+
func (repl *JqRepl) Close() {
39+
repl.reader.Close()
40+
repl.libJq.Close()
41+
if repl.input != nil {
42+
repl.input.Free()
43+
}
44+
}
45+
46+
func (repl *JqRepl) currentPrompt() string {
47+
return fmt.Sprintf(repl.promptTemplate, repl.programCounter)
48+
}
49+
50+
// JvInput returns the current input the JQ program will operate on
51+
func (repl *JqRepl) JvInput() *jq.Jv {
52+
return repl.input
53+
}
54+
55+
func (repl *JqRepl) SetJvInput(input *jq.Jv) {
56+
if repl.input != nil {
57+
repl.input.Free()
58+
}
59+
repl.input = input
60+
}
61+
62+
func (repl *JqRepl) Loop() {
63+
for {
64+
repl.reader.SetPrompt(repl.currentPrompt())
65+
66+
line, err := repl.reader.Readline()
67+
if err == io.EOF {
68+
break
69+
} else if err == readline.ErrInterrupt {
70+
// Stop the streaming of any results - if we were
71+
continue
72+
} else if err != nil {
73+
panic(fmt.Errorf("%#v", err))
74+
}
75+
76+
repl.programCounter++
77+
repl.RunProgram(line)
78+
}
79+
}
80+
81+
func (repl *JqRepl) Error(err error) {
82+
fmt.Fprintf(repl.reader.Stderr(), "\033[0;31m%s\033[0m\n", err)
83+
}
84+
85+
func (repl *JqRepl) Output(o *jq.Jv) {
86+
fmt.Fprintln(repl.reader.Stdout(), o.Dump(jq.JvPrintPretty|jq.JvPrintSpace1|jq.JvPrintColour))
87+
}
88+
89+
func (repl *JqRepl) RunProgram(program string) {
90+
chanIn, chanOut, chanErr := repl.libJq.Start(program)
91+
inCopy := repl.JvInput().Copy()
92+
93+
// Run until the channels are closed
94+
for chanErr != nil && chanOut != nil {
95+
select {
96+
case e, ok := <-chanErr:
97+
if !ok {
98+
chanErr = nil
99+
} else {
100+
repl.Error(e)
101+
}
102+
case o, ok := <-chanOut:
103+
if !ok {
104+
chanOut = nil
105+
} else {
106+
repl.Output(o)
107+
}
108+
case chanIn <- inCopy:
109+
// We've sent our input, close the channel to tell Jq we're done
110+
close(chanIn)
111+
chanIn = nil
112+
}
113+
}
114+
}

main.go

Lines changed: 7 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,82 +3,35 @@
33
package main
44

55
import (
6-
"fmt"
7-
"io"
8-
6+
"github.com/ashb/jqrepl"
97
"github.com/ashb/jqrepl/jq"
10-
11-
"gopkg.in/chzyer/readline.v1"
128
)
139

1410
func main() {
15-
prompt := "\033[0;36m»\033[0m "
16-
l, err := readline.New(prompt)
11+
repl, err := jqrepl.New()
1712

1813
if err != nil {
1914
// TODO: don't use panic
2015
panic(err)
2116
}
2217

23-
libjq, err := jq.New()
24-
if err != nil {
25-
// TODO: don't use panic
26-
panic(err)
27-
}
28-
defer libjq.Close()
18+
defer repl.Close()
2919

3020
input, err := jq.JvFromJSONString(`
3121
{ "simple": 123,
3222
"nested": {
3323
"a": [1,2,"a"],
3424
"b": true,
3525
"c": null
36-
}
26+
},
27+
"non_printable": "\ud83c\uddec\ud83c\udde7"
3728
}`)
3829
if err != nil {
3930
// TODO: don't use panic
4031
panic(err)
4132
}
4233

43-
defer input.Free()
44-
45-
for {
46-
line, err := l.Readline()
47-
if err == io.EOF {
48-
break
49-
} else if err == readline.ErrInterrupt {
50-
// Stop the streaming.
51-
continue
52-
} else if err != nil {
53-
panic(fmt.Errorf("%#v", err))
54-
}
55-
56-
chanIn, chanOut, chanErr := libjq.Start(line)
57-
inCopy := input.Copy()
58-
59-
// Run until the channels are closed
60-
for chanErr != nil && chanOut != nil {
61-
select {
62-
case e, ok := <-chanErr:
63-
if !ok {
64-
chanErr = nil
65-
} else {
66-
fmt.Printf("\033[0;31m%s\033[0m\n", e)
67-
}
68-
case o, ok := <-chanOut:
69-
if !ok {
70-
chanOut = nil
71-
} else {
72-
fmt.Printf("// Read from output %v %#v\n", o.Kind(), ok)
73-
fmt.Println(o.ToGoVal())
74-
}
75-
case chanIn <- inCopy:
76-
// We've sent our input, close the channel to tell Jq we're done
77-
close(chanIn)
78-
chanIn = nil
79-
}
80-
}
81-
}
34+
repl.SetJvInput(input)
8235

83-
libjq.Close()
36+
repl.Loop()
8437
}

0 commit comments

Comments
 (0)