github.com/neilotoole/jsoncolor@v0.7.2-0.20231115150201-1637fae69be1/cmd/jc/main.go (about) 1 // Package main contains a trivial CLI that accepts JSON input either 2 // via stdin or via "-i path/to/input.json", and outputs JSON 3 // to stdout, or if "-o path/to/output.json" is set, outputs to that file. 4 // If -c (colorized) is true, output to stdout will be colorized if possible 5 // (but never colorized for file output). 6 // 7 // Examples: 8 // 9 // $ cat example.json | jc 10 // $ cat example.json | jc -c false 11 package main 12 13 import ( 14 "errors" 15 "flag" 16 "fmt" 17 "io" 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 22 "github.com/mattn/go-colorable" 23 json "github.com/neilotoole/jsoncolor" 24 ) 25 26 var ( 27 flagPretty = flag.Bool("p", true, "output pretty JSON") 28 flagColorize = flag.Bool("c", true, "output colorized JSON") 29 flagInputFile = flag.String("i", "", "path to input JSON file") 30 flagOutputFile = flag.String("o", "", "path to output JSON file") 31 ) 32 33 func printUsage() { 34 const msg = ` 35 jc (jsoncolor) is a trivial CLI to demonstrate the neilotoole/jsoncolor package. 36 It accepts JSON input, and outputs colorized, prettified JSON. 37 38 Example Usage: 39 40 # Pipe a JSON file, using defaults (colorized and prettified); print to stdout 41 $ cat testdata/sakila_actor.json | jc 42 43 # Read input from a JSON file, print to stdout, DO colorize but DO NOT prettify 44 $ jc -c -p=false -i ./testdata/sakila_actor.json 45 46 # Pipe a JSON input file to jc, outputting to a specified file; and DO NOT prettify 47 $ cat ./testdata/sakila_actor.json | jc -p=false -o /tmp/out.json` 48 fmt.Fprintln(os.Stderr, msg) 49 } 50 51 func main() { 52 flag.Parse() 53 if err := doMain(); err != nil { 54 fmt.Fprintf(os.Stderr, "error: %v\n", err) 55 printUsage() 56 os.Exit(1) 57 } 58 } 59 60 func doMain() error { 61 var ( 62 input []byte 63 err error 64 ) 65 66 if flagInputFile != nil && *flagInputFile != "" { 67 // Read from file 68 var f *os.File 69 if f, err = os.Open(*flagInputFile); err != nil { 70 return err 71 } 72 defer f.Close() 73 74 if input, err = ioutil.ReadAll(f); err != nil { 75 return err 76 } 77 } else { 78 // Probably read from stdin... 79 var fi os.FileInfo 80 if fi, err = os.Stdin.Stat(); err != nil { 81 return err 82 } 83 84 if (fi.Mode() & os.ModeCharDevice) == 0 { 85 // Read from stdin 86 if input, err = ioutil.ReadAll(os.Stdin); err != nil { 87 return err 88 } 89 } else { 90 return errors.New("invalid args") 91 } 92 } 93 94 jsn := new(interface{}) // generic interface{} that will hold the parsed JSON 95 if err = json.Unmarshal(input, jsn); err != nil { 96 return fmt.Errorf("invalid input JSON: %w", err) 97 } 98 99 var out io.Writer 100 if flagOutputFile != nil && *flagOutputFile != "" { 101 // Output file is specified via -o flag 102 var fpath string 103 if fpath, err = filepath.Abs(*flagOutputFile); err != nil { 104 return fmt.Errorf("failed to get absolute path for -o %q: %w", *flagOutputFile, err) 105 } 106 107 // Ensure the parent dir exists 108 if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 109 return fmt.Errorf("failed to make parent dir for -o %q: %w", *flagOutputFile, err) 110 } 111 112 var f *os.File 113 if f, err = os.Create(fpath); err != nil { 114 return fmt.Errorf("failed to open output file specified by -o %q: %w", *flagOutputFile, err) 115 } 116 defer f.Close() 117 out = f 118 } else { 119 // Output file NOT specified via -o flag, use stdout. 120 out = os.Stdout 121 } 122 123 var enc *json.Encoder 124 125 if flagColorize != nil && *flagColorize && json.IsColorTerminal(out) { 126 out = colorable.NewColorable(out.(*os.File)) // colorable is needed for Windows 127 enc = json.NewEncoder(out) 128 clrs := json.DefaultColors() 129 enc.SetColors(clrs) 130 } else { 131 // We are NOT doing color output: either flag not set, or we 132 // could be outputting to a file etc. 133 // Therefore DO NOT call enc.SetColors. 134 enc = json.NewEncoder(out) 135 } 136 137 if flagPretty != nil && *flagPretty { 138 // Pretty-print, i.e. set indent 139 enc.SetIndent("", " ") 140 } 141 142 return enc.Encode(jsn) 143 }