Skip to content

Instantly share code, notes, and snippets.

@bored-engineer
Created January 29, 2020 20:01
Show Gist options
  • Save bored-engineer/70283f03f83f6105e1a67144287bd95f to your computer and use it in GitHub Desktop.
Save bored-engineer/70283f03f83f6105e1a67144287bd95f to your computer and use it in GitHub Desktop.
jq but for MessagePack: go build mq.go -o mq
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
"github.com/tinylib/msgp/msgp"
)
// usage invokes jq but replaced jq with mq
func usage() {
out, err := exec.Command("jq", "--help").CombinedOutput()
if err != nil {
log.Fatalf("failed to execute jq: %v", err)
}
out = bytes.ReplaceAll(out, []byte("jq"), []byte("mq"))
out = bytes.ReplaceAll(out, []byte("JSON"), []byte("MessagePack"))
fmt.Print(string(out))
os.Exit(0)
}
// convert takes a input converts to json and returns the path
func convert(inputPath string) (string, error) {
output, err := ioutil.TempFile("", "mq*")
if err != nil {
return output.Name(), err
}
defer output.Close()
input, err := os.Open(inputPath)
if err != nil {
return output.Name(), err
}
defer input.Close()
if _, err := msgp.CopyToJSON(output, input); err != nil {
return output.Name(), err
}
return output.Name(), nil
}
// Entry point
func main() {
// Extract the jq args from the filenames
var files []string
var args []string
flags := true
for _, arg := range os.Args[1:] {
switch {
case arg == "--":
flags = false
case arg == "--help" || arg == "-h":
usage()
case flags && strings.HasPrefix(arg, "-"):
args = append(args, arg)
default:
files = append(files, arg)
}
}
cmd := exec.Command("jq", files[0])
files = files[1:]
// If 1 or less files, use stdin to pipe
if len(files) <= 1 {
src := os.Stdin
if len(files) == 1 {
f, err := os.Open(files[0])
if err != nil {
log.Fatalf("failed to open file: %v", err)
}
defer f.Close()
src = f
}
// Use a pipe for minimal i/o
var dst io.WriteCloser
cmd.Stdin, dst = io.Pipe()
go func() {
defer dst.Close()
if _, err := msgp.CopyToJSON(dst, src); err != nil {
log.Fatalf("failed to decode msgpack: %v", err)
}
}()
} else {
// Convert the files on disk with temporary files blocking until done
for _, file := range files {
tmp, err := convert(file)
defer os.Remove(tmp)
if err != nil {
log.Fatalf("failed to convert file: %v", err)
}
cmd.Args = append(cmd.Args, tmp)
}
}
cmd.Args = append(cmd.Args, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
os.Exit(exitError.ExitCode())
} else {
log.Fatalf("failed to execute jq: %v", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment