Skip to content

Commit 4b02ec3

Browse files
committed
Compile jq expression once, reuse in writer
PersistentPreRunE now caches the compiled *gojq.Code and passes it to newJQWriterWithCode, avoiding a redundant parse + compile cycle.
1 parent 9c8346b commit 4b02ec3

2 files changed

Lines changed: 29 additions & 18 deletions

File tree

internal/commands/jq.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ func newJQWriter(dest io.Writer, filter string) (*jqWriter, error) {
3131
return &jqWriter{dest: dest, code: code}, nil
3232
}
3333

34+
// newJQWriterWithCode creates a jqWriter using a pre-compiled *gojq.Code.
35+
// Used when the expression has already been validated and compiled (e.g. in PersistentPreRunE).
36+
func newJQWriterWithCode(dest io.Writer, code *gojq.Code) *jqWriter {
37+
return &jqWriter{dest: dest, code: code}
38+
}
39+
40+
// compileJQ parses and compiles a jq expression, returning the compiled code.
41+
func compileJQ(filter string) (*gojq.Code, error) {
42+
query, err := gojq.Parse(filter)
43+
if err != nil {
44+
return nil, errors.ErrJQValidation(err)
45+
}
46+
code, err := gojq.Compile(query, gojq.WithEnvironLoader(os.Environ))
47+
if err != nil {
48+
return nil, errors.ErrJQValidation(err)
49+
}
50+
return code, nil
51+
}
52+
3453
// Write intercepts JSON output, applies the jq filter, and writes filtered results.
3554
// String results print as plain text; everything else prints as compact single-line JSON.
3655
// Error envelopes (ok: false) pass through unfiltered so error messages are never hidden.

internal/commands/root.go

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,20 @@ Use fizzy to manage boards, cards, comments, and more from your terminal.`,
7777
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
7878
// Early jq validation: check flag conflicts first (actionable message),
7979
// then parse + compile before RunE so invalid expressions are rejected
80-
// with no side effects.
80+
// with no side effects. The compiled code is reused below to avoid
81+
// parsing the expression twice.
82+
var jqCode *gojq.Code
8183
if cfgJQ != "" {
8284
if cfgIDsOnly {
8385
return errors.ErrJQConflict("--ids-only")
8486
}
8587
if cfgCount {
8688
return errors.ErrJQConflict("--count")
8789
}
88-
q, err := gojq.Parse(cfgJQ)
90+
var err error
91+
jqCode, err = compileJQ(cfgJQ)
8992
if err != nil {
90-
return errors.ErrJQValidation(err)
91-
}
92-
if _, err := gojq.Compile(q, gojq.WithEnvironLoader(os.Environ)); err != nil {
93-
return errors.ErrJQValidation(err)
93+
return err
9494
}
9595
}
9696

@@ -103,23 +103,15 @@ Use fizzy to manage boards, cards, comments, and more from your terminal.`,
103103
// Test mode — preserve test buffer as writer.
104104
outWriter = &testBuf
105105
var w io.Writer = &testBuf
106-
if cfgJQ != "" {
107-
jw, err := newJQWriter(&testBuf, cfgJQ)
108-
if err != nil {
109-
return err
110-
}
111-
w = jw
106+
if jqCode != nil {
107+
w = newJQWriterWithCode(&testBuf, jqCode)
112108
}
113109
out = output.New(output.Options{Format: format, Writer: w})
114110
} else {
115111
outWriter = os.Stdout
116112
var w io.Writer = os.Stdout
117-
if cfgJQ != "" {
118-
jw, err := newJQWriter(os.Stdout, cfgJQ)
119-
if err != nil {
120-
return err
121-
}
122-
w = jw
113+
if jqCode != nil {
114+
w = newJQWriterWithCode(os.Stdout, jqCode)
123115
}
124116
out = output.New(output.Options{Format: format, Writer: w})
125117
}

0 commit comments

Comments
 (0)