From 6243b782403a39f70d882eacbacb8e5765935755 Mon Sep 17 00:00:00 2001 From: Jeff Warner Date: Mon, 3 Feb 2025 11:31:07 -0800 Subject: [PATCH] adds a basic CLI --- cmd/jsonpath/main.go | 87 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 8 +++- go.sum | 8 ++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 cmd/jsonpath/main.go diff --git a/cmd/jsonpath/main.go b/cmd/jsonpath/main.go new file mode 100644 index 0000000..181752f --- /dev/null +++ b/cmd/jsonpath/main.go @@ -0,0 +1,87 @@ +// Package main implements a simple command-line utility that allows one to extract +// data from an arbitrary JSON body that has been piped into it. +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "runtime/debug" + + "github.com/theory/jsonpath" + "github.com/urfave/cli/v2" +) + +func main() { + app := &cli.App{ + Name: "jsonpath", + Usage: "extracting data from JSON according to RFC-9535", + UsageText: "jsonpath QUERY", + Version: gitrev(), + Action: parseAndPrint, + Args: true, + } + + if err := app.Run(os.Args); err != nil { + fmt.Fprint(os.Stderr, err.Error()+"\n") + os.Exit(1) + } +} + +func gitrev() string { + version := "(git revision unavailable)" + + if bi, ok := debug.ReadBuildInfo(); ok { + for _, kv := range bi.Settings { + if kv.Key == "vcs.revision" { + version = kv.Value + } + } + } + + return version +} + +func parseAndPrint(ctx *cli.Context) error { + // grab the provided jsonpath query + q := ctx.Args().First() + if q == "" { + cli.ShowAppHelpAndExit(ctx, 1) + } + p := jsonpath.NewParser().MustParse(q) + + m, err := jsonToMap(os.Stdin) + if err != nil { + return fmt.Errorf("could not read JSON body from stdin: %w", err) + } + + // apply q to map + result := p.Select(m) + + // dump to output + items, err := json.Marshal(result) + if err != nil { + return fmt.Errorf("could not marshal results to JSON: %w", err) + } + fmt.Printf("%s\n", items) //nolint:forbidigo + + return nil +} + +func jsonToMap(r io.Reader) (map[string]any, error) { + b, err := io.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("could not read JSON contents: %w", err) + } + + decoder := json.NewDecoder(bytes.NewReader(b)) + var m map[string]any + err = decoder.Decode(&m) + if err != nil { + return nil, fmt.Errorf("could not decode JSON to map: %w", err) + } + + return m, nil +} diff --git a/go.mod b/go.mod index f6a5912..fdebc1a 100644 --- a/go.mod +++ b/go.mod @@ -2,13 +2,19 @@ module github.com/theory/jsonpath go 1.23 -require github.com/stretchr/testify v1.10.0 +require ( + github.com/stretchr/testify v1.10.0 + github.com/urfave/cli/v2 v2.27.5 +) require ( + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e13ef19..df9debe 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,8 +16,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=