-
Notifications
You must be signed in to change notification settings - Fork 6
/
main.go
143 lines (122 loc) · 3.7 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
// Package main contains a trivial CLI that accepts JSON input either
// via stdin or via "-i path/to/input.json", and outputs JSON
// to stdout, or if "-o path/to/output.json" is set, outputs to that file.
// If -c (colorized) is true, output to stdout will be colorized if possible
// (but never colorized for file output).
//
// Examples:
//
// $ cat example.json | jc
// $ cat example.json | jc -c false
package main
import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/mattn/go-colorable"
json "github.com/neilotoole/jsoncolor"
)
var (
flagPretty = flag.Bool("p", true, "output pretty JSON")
flagColorize = flag.Bool("c", true, "output colorized JSON")
flagInputFile = flag.String("i", "", "path to input JSON file")
flagOutputFile = flag.String("o", "", "path to output JSON file")
)
func printUsage() {
const msg = `
jc (jsoncolor) is a trivial CLI to demonstrate the neilotoole/jsoncolor package.
It accepts JSON input, and outputs colorized, prettified JSON.
Example Usage:
# Pipe a JSON file, using defaults (colorized and prettified); print to stdout
$ cat testdata/sakila_actor.json | jc
# Read input from a JSON file, print to stdout, DO colorize but DO NOT prettify
$ jc -c -p=false -i ./testdata/sakila_actor.json
# Pipe a JSON input file to jc, outputting to a specified file; and DO NOT prettify
$ cat ./testdata/sakila_actor.json | jc -p=false -o /tmp/out.json`
fmt.Fprintln(os.Stderr, msg)
}
func main() {
flag.Parse()
if err := doMain(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
printUsage()
os.Exit(1)
}
}
func doMain() error {
var (
input []byte
err error
)
if flagInputFile != nil && *flagInputFile != "" {
// Read from file
var f *os.File
if f, err = os.Open(*flagInputFile); err != nil {
return err
}
defer f.Close()
if input, err = ioutil.ReadAll(f); err != nil {
return err
}
} else {
// Probably read from stdin...
var fi os.FileInfo
if fi, err = os.Stdin.Stat(); err != nil {
return err
}
if (fi.Mode() & os.ModeCharDevice) == 0 {
// Read from stdin
if input, err = ioutil.ReadAll(os.Stdin); err != nil {
return err
}
} else {
return errors.New("invalid args")
}
}
jsn := new(interface{}) // generic interface{} that will hold the parsed JSON
if err = json.Unmarshal(input, jsn); err != nil {
return fmt.Errorf("invalid input JSON: %w", err)
}
var out io.Writer
if flagOutputFile != nil && *flagOutputFile != "" {
// Output file is specified via -o flag
var fpath string
if fpath, err = filepath.Abs(*flagOutputFile); err != nil {
return fmt.Errorf("failed to get absolute path for -o %q: %w", *flagOutputFile, err)
}
// Ensure the parent dir exists
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return fmt.Errorf("failed to make parent dir for -o %q: %w", *flagOutputFile, err)
}
var f *os.File
if f, err = os.Create(fpath); err != nil {
return fmt.Errorf("failed to open output file specified by -o %q: %w", *flagOutputFile, err)
}
defer f.Close()
out = f
} else {
// Output file NOT specified via -o flag, use stdout.
out = os.Stdout
}
var enc *json.Encoder
if flagColorize != nil && *flagColorize && json.IsColorTerminal(out) {
out = colorable.NewColorable(out.(*os.File)) // colorable is needed for Windows
enc = json.NewEncoder(out)
clrs := json.DefaultColors()
enc.SetColors(clrs)
} else {
// We are NOT doing color output: either flag not set, or we
// could be outputting to a file etc.
// Therefore DO NOT call enc.SetColors.
enc = json.NewEncoder(out)
}
if flagPretty != nil && *flagPretty {
// Pretty-print, i.e. set indent
enc.SetIndent("", " ")
}
return enc.Encode(jsn)
}