jsonpretty 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.goreleaser.yaml +30 -0
- data/History.txt +4 -0
- data/README.md +1 -1
- data/go.mod +3 -0
- data/lib/jsonpretty/version.rb +1 -1
- data/main.go +199 -0
- data/main_test.go +180 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89641014acab0632260843ff87e190604e7342d386109dfa77245ec343ff1ee4
|
4
|
+
data.tar.gz: 7fe4a6155fed431b9c64735fa3a8c133354c293a60d9a03c5786a15f3e31348d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17e783dbab3458c8c46ef6b9cb3f6065f6552eccbe65b6ba408003709bed7cbc92e98ab4f3d09750363fd2fbd7426df7accb2ea4716ec24f53b5c92e24789d14
|
7
|
+
data.tar.gz: d3b01927f851234aba103921e43b8db1cc2d07383ea73c825b78027cd6bfacf8b763be0112aff91ba8e08dd53a1b27d61129aa1dd10861a4267e438d3d7dd6e2
|
data/.goreleaser.yaml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This is an example .goreleaser.yml file with some sensible defaults.
|
2
|
+
# Make sure to check the documentation at https://goreleaser.com
|
3
|
+
before:
|
4
|
+
hooks:
|
5
|
+
# You may remove this if you don't use go modules.
|
6
|
+
- go mod tidy
|
7
|
+
builds:
|
8
|
+
- env:
|
9
|
+
- CGO_ENABLED=0
|
10
|
+
goos:
|
11
|
+
- linux
|
12
|
+
- windows
|
13
|
+
- darwin
|
14
|
+
archives:
|
15
|
+
- replacements:
|
16
|
+
darwin: Darwin
|
17
|
+
linux: Linux
|
18
|
+
windows: Windows
|
19
|
+
386: i386
|
20
|
+
amd64: x86_64
|
21
|
+
checksum:
|
22
|
+
name_template: 'checksums.txt'
|
23
|
+
snapshot:
|
24
|
+
name_template: "{{ incpatch .Version }}-next"
|
25
|
+
changelog:
|
26
|
+
sort: asc
|
27
|
+
filters:
|
28
|
+
exclude:
|
29
|
+
- '^docs:'
|
30
|
+
- '^test:'
|
data/History.txt
CHANGED
data/README.md
CHANGED
data/go.mod
ADDED
data/lib/jsonpretty/version.rb
CHANGED
data/main.go
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"bytes"
|
5
|
+
"encoding/json"
|
6
|
+
"fmt"
|
7
|
+
"io"
|
8
|
+
"os"
|
9
|
+
"regexp"
|
10
|
+
"strings"
|
11
|
+
)
|
12
|
+
|
13
|
+
var (
|
14
|
+
version = "dev"
|
15
|
+
commit = ""
|
16
|
+
date = ""
|
17
|
+
builtBy = ""
|
18
|
+
)
|
19
|
+
|
20
|
+
type JsonArg struct {
|
21
|
+
file *os.File
|
22
|
+
content []byte
|
23
|
+
}
|
24
|
+
|
25
|
+
func (j JsonArg) Close() error {
|
26
|
+
if j.file != nil {
|
27
|
+
return j.file.Close()
|
28
|
+
}
|
29
|
+
return nil
|
30
|
+
}
|
31
|
+
|
32
|
+
func usage(code int) {
|
33
|
+
fmt.Printf("usage: %s [args|@filename|@- (stdin)]\n", os.Args[0])
|
34
|
+
fmt.Printf("Parse and pretty-print JSON, either from stdin or from arguments concatenated together\n")
|
35
|
+
os.Exit(code)
|
36
|
+
}
|
37
|
+
|
38
|
+
func versionInfo() string {
|
39
|
+
if commit != "" && date != "" {
|
40
|
+
return fmt.Sprintf("%s (%s on %s)", version, commit[0:7], date[0:10])
|
41
|
+
}
|
42
|
+
return version
|
43
|
+
}
|
44
|
+
|
45
|
+
func checkFlags(arg string) {
|
46
|
+
if arg[0] == '-' && len(arg) > 1 {
|
47
|
+
code := 1
|
48
|
+
if arg == "-v" || strings.HasPrefix(arg, "--v") {
|
49
|
+
fmt.Printf("jsonpretty version %s\n", versionInfo())
|
50
|
+
os.Exit(0)
|
51
|
+
} else if arg == "-h" || strings.HasPrefix(arg, "--h") {
|
52
|
+
code = 0
|
53
|
+
}
|
54
|
+
if code == 1 {
|
55
|
+
fmt.Printf("unrecognized flag %s\n", arg)
|
56
|
+
}
|
57
|
+
usage(code)
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
func buildJsonSource(args []JsonArg) *bytes.Buffer {
|
62
|
+
buffer := bytes.NewBuffer(nil)
|
63
|
+
for _, arg := range args {
|
64
|
+
if arg.file != nil {
|
65
|
+
_, err := buffer.ReadFrom(arg.file)
|
66
|
+
if err != nil {
|
67
|
+
fmt.Fprintf(os.Stderr, "error reading file: %v\n", err)
|
68
|
+
}
|
69
|
+
} else {
|
70
|
+
_, err := buffer.Write(arg.content)
|
71
|
+
if err != nil {
|
72
|
+
fmt.Fprintf(os.Stderr, "error appending content: %v\n", err)
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
return buffer
|
77
|
+
}
|
78
|
+
|
79
|
+
func readArguments(args []string) *bytes.Buffer {
|
80
|
+
jsonArgs := []JsonArg{}
|
81
|
+
defer func() {
|
82
|
+
for _, j := range jsonArgs {
|
83
|
+
_ = j.Close()
|
84
|
+
}
|
85
|
+
}()
|
86
|
+
for _, arg := range args {
|
87
|
+
checkFlags(arg)
|
88
|
+
|
89
|
+
if arg == "-" || arg == "@-" {
|
90
|
+
jsonArgs = append(jsonArgs, JsonArg{file: os.Stdin})
|
91
|
+
} else if strings.HasPrefix(arg, "@") {
|
92
|
+
file, err := os.Open(arg[1:])
|
93
|
+
if err != nil {
|
94
|
+
fmt.Fprintf(os.Stderr, "skipping '%s' due to error: %v\n", arg, err)
|
95
|
+
continue
|
96
|
+
}
|
97
|
+
jsonArgs = append(jsonArgs, JsonArg{file: file})
|
98
|
+
} else {
|
99
|
+
jsonArgs = append(jsonArgs, JsonArg{content: []byte(arg)})
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
if len(jsonArgs) == 0 {
|
104
|
+
jsonArgs = append(jsonArgs, JsonArg{file: os.Stdin})
|
105
|
+
}
|
106
|
+
return buildJsonSource(jsonArgs)
|
107
|
+
}
|
108
|
+
|
109
|
+
var httpRegex *regexp.Regexp = regexp.MustCompile("^HTTP/\\d")
|
110
|
+
|
111
|
+
func parseHeaders(src *bytes.Buffer) ([]byte, []byte, error) {
|
112
|
+
headerBuffer := bytes.NewBuffer(nil)
|
113
|
+
line, err := src.ReadBytes('\n')
|
114
|
+
if err != nil && err != io.EOF {
|
115
|
+
return nil, nil, err
|
116
|
+
}
|
117
|
+
|
118
|
+
if httpRegex.Match(line) {
|
119
|
+
for {
|
120
|
+
_, err = headerBuffer.Write(line)
|
121
|
+
if err != nil {
|
122
|
+
return nil, nil, err
|
123
|
+
}
|
124
|
+
if (len(line) == 1 && line[0] == '\n') ||
|
125
|
+
(len(line) == 2 && line[0] == '\r' && line[1] == '\n') {
|
126
|
+
break
|
127
|
+
}
|
128
|
+
|
129
|
+
line, err = src.ReadBytes('\n')
|
130
|
+
|
131
|
+
if err == io.EOF {
|
132
|
+
_, err = headerBuffer.Write(line)
|
133
|
+
if err != nil {
|
134
|
+
return nil, nil, err
|
135
|
+
}
|
136
|
+
break
|
137
|
+
}
|
138
|
+
|
139
|
+
if err != nil && err != io.EOF {
|
140
|
+
return nil, nil, err
|
141
|
+
}
|
142
|
+
}
|
143
|
+
} else {
|
144
|
+
newsrc := bytes.NewBuffer(line)
|
145
|
+
_, err = newsrc.Write(src.Bytes())
|
146
|
+
if err != nil {
|
147
|
+
return nil, nil, err
|
148
|
+
}
|
149
|
+
src = newsrc
|
150
|
+
}
|
151
|
+
return headerBuffer.Bytes(), src.Bytes(), nil
|
152
|
+
}
|
153
|
+
|
154
|
+
var jsonpRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z$_][^ \t\r\n(]+)\\(")
|
155
|
+
var endingParen *regexp.Regexp = regexp.MustCompile("\\)[ \t\r\n]*$")
|
156
|
+
|
157
|
+
func cleanJsonp(jsonSrc []byte) ([]byte, string) {
|
158
|
+
match := jsonpRegexp.FindSubmatch(jsonSrc)
|
159
|
+
ending := endingParen.FindSubmatch(jsonSrc)
|
160
|
+
if match != nil && ending != nil {
|
161
|
+
return jsonSrc[len(match[0]) : len(jsonSrc)-len(ending[0])], string(match[1])
|
162
|
+
}
|
163
|
+
return jsonSrc, ""
|
164
|
+
}
|
165
|
+
|
166
|
+
func handleParseError(err error, jsonSrc []byte) {
|
167
|
+
if err != nil {
|
168
|
+
fmt.Fprintf(os.Stderr, "error parsing json: %v\ninput:\n%s\n", err, string(jsonSrc))
|
169
|
+
os.Exit(1)
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
func main() {
|
174
|
+
buffer := readArguments(os.Args[1:])
|
175
|
+
headers, jsonSrc, err := parseHeaders(buffer)
|
176
|
+
handleParseError(err, jsonSrc)
|
177
|
+
|
178
|
+
jsonSrc, jsonpName := cleanJsonp(jsonSrc)
|
179
|
+
|
180
|
+
if len(headers) > 0 {
|
181
|
+
fmt.Print(string(headers))
|
182
|
+
}
|
183
|
+
|
184
|
+
if jsonpName != "" {
|
185
|
+
fmt.Printf("jsonp method name: %s\n\n", jsonpName)
|
186
|
+
}
|
187
|
+
|
188
|
+
var jsonObj interface{}
|
189
|
+
err = json.Unmarshal(jsonSrc, &jsonObj)
|
190
|
+
handleParseError(err, jsonSrc)
|
191
|
+
|
192
|
+
enc := json.NewEncoder(os.Stdout)
|
193
|
+
enc.SetIndent("", " ")
|
194
|
+
err = enc.Encode(&jsonObj)
|
195
|
+
if err != nil {
|
196
|
+
fmt.Fprintf(os.Stderr, "error encoding json: %v\n", err)
|
197
|
+
os.Exit(1)
|
198
|
+
}
|
199
|
+
}
|
data/main_test.go
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"bytes"
|
5
|
+
"fmt"
|
6
|
+
"os"
|
7
|
+
"testing"
|
8
|
+
)
|
9
|
+
|
10
|
+
type Testfile struct {
|
11
|
+
name string
|
12
|
+
content string
|
13
|
+
file *os.File
|
14
|
+
}
|
15
|
+
|
16
|
+
var stdinSaved *os.File = os.Stdin
|
17
|
+
|
18
|
+
func Setup(contents []string, t *testing.T) []Testfile {
|
19
|
+
var err error
|
20
|
+
os.Stdin, err = os.CreateTemp("", "stdin")
|
21
|
+
if err != nil {
|
22
|
+
t.FailNow()
|
23
|
+
}
|
24
|
+
|
25
|
+
testfiles := make([]Testfile, len(contents))
|
26
|
+
for i, c := range contents {
|
27
|
+
testfiles[i] = Testfile{content: c}
|
28
|
+
file, err := os.CreateTemp("", fmt.Sprintf("tf%d", i))
|
29
|
+
if err != nil {
|
30
|
+
t.Logf("unable to create tempfile for '%s'", c)
|
31
|
+
t.FailNow()
|
32
|
+
}
|
33
|
+
_, err = file.Write([]byte(c))
|
34
|
+
if err != nil {
|
35
|
+
t.Logf("unable to write tempfile with '%s'", c)
|
36
|
+
t.FailNow()
|
37
|
+
}
|
38
|
+
err = file.Close()
|
39
|
+
if err != nil {
|
40
|
+
t.Logf("unable to close tempfile for '%s'", c)
|
41
|
+
t.FailNow()
|
42
|
+
}
|
43
|
+
testfiles[i].file = file
|
44
|
+
testfiles[i].name = file.Name()
|
45
|
+
}
|
46
|
+
return testfiles
|
47
|
+
}
|
48
|
+
|
49
|
+
func TearDown(testfiles []Testfile) {
|
50
|
+
for _, tf := range testfiles {
|
51
|
+
_ = os.Remove(tf.name)
|
52
|
+
}
|
53
|
+
_ = os.Stdin.Close()
|
54
|
+
os.Stdin = stdinSaved
|
55
|
+
}
|
56
|
+
|
57
|
+
func assertEqual(expect []byte, actual []byte, t *testing.T) {
|
58
|
+
if bytes.Compare(expect, actual) != 0 {
|
59
|
+
t.Errorf("bytes did not match:\n. expected:\n %s\n actual:\n %s\n",
|
60
|
+
string(expect), string(actual))
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
func TestReadArguments(t *testing.T) {
|
65
|
+
files := Setup([]string{"1", "2", "{\"foo\": true, \"bar\": false}"}, t)
|
66
|
+
defer TearDown(files)
|
67
|
+
|
68
|
+
tests := [][]Testfile{
|
69
|
+
{}, // nothing
|
70
|
+
{Testfile{content: "{\"hello\":\"world\"}"}},
|
71
|
+
{Testfile{content: "["}, files[0], Testfile{content: ","}, files[1], Testfile{content: "]"}},
|
72
|
+
{files[2]},
|
73
|
+
}
|
74
|
+
|
75
|
+
for i, test := range tests {
|
76
|
+
t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
|
77
|
+
expected := bytes.NewBuffer(nil)
|
78
|
+
args := make([]string, len(test))
|
79
|
+
for j, testfile := range test {
|
80
|
+
if testfile.name != "" {
|
81
|
+
args[j] = fmt.Sprintf("@%s", testfile.name)
|
82
|
+
} else {
|
83
|
+
args[j] = testfile.content
|
84
|
+
}
|
85
|
+
expected.WriteString(testfile.content)
|
86
|
+
}
|
87
|
+
expect := expected.Bytes()
|
88
|
+
actual := readArguments(args)
|
89
|
+
assertEqual(expect, actual.Bytes(), t)
|
90
|
+
})
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
func TestReadArgumentsStdin(t *testing.T) {
|
95
|
+
f := Setup([]string{}, t)
|
96
|
+
defer TearDown(f)
|
97
|
+
|
98
|
+
expect := []byte("{\"foo\": true, \"bar\": false}")
|
99
|
+
_, err := os.Stdin.Write(expect)
|
100
|
+
if err != nil {
|
101
|
+
t.Logf("unable to write to stdin\n")
|
102
|
+
t.FailNow()
|
103
|
+
}
|
104
|
+
_ = os.Stdin.Close()
|
105
|
+
|
106
|
+
reopen := func(t *testing.T) {
|
107
|
+
f, err := os.Open(os.Stdin.Name())
|
108
|
+
if err != nil {
|
109
|
+
t.Logf("unable to reopen stdin")
|
110
|
+
t.FailNow()
|
111
|
+
}
|
112
|
+
os.Stdin = f
|
113
|
+
}
|
114
|
+
|
115
|
+
t.Run("no args", func(t *testing.T) {
|
116
|
+
reopen(t)
|
117
|
+
actual := readArguments([]string{})
|
118
|
+
assertEqual(expect, actual.Bytes(), t)
|
119
|
+
})
|
120
|
+
|
121
|
+
t.Run("-", func(t *testing.T) {
|
122
|
+
reopen(t)
|
123
|
+
actual := readArguments([]string{"-"})
|
124
|
+
assertEqual(expect, actual.Bytes(), t)
|
125
|
+
})
|
126
|
+
|
127
|
+
t.Run("@-", func(t *testing.T) {
|
128
|
+
reopen(t)
|
129
|
+
actual := readArguments([]string{"@-"})
|
130
|
+
assertEqual(expect, actual.Bytes(), t)
|
131
|
+
})
|
132
|
+
}
|
133
|
+
|
134
|
+
func TestParseHeaders(t *testing.T) {
|
135
|
+
tests := [][]string{
|
136
|
+
{"", "", ""},
|
137
|
+
{"{}", "", "{}"},
|
138
|
+
{"not http\n\nhello\nworld", "", "not http\n\nhello\nworld"},
|
139
|
+
{"HTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 19\n\n{\"hello\":\"world\"}\n",
|
140
|
+
"HTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 19\n\n",
|
141
|
+
"{\"hello\":\"world\"}\n"},
|
142
|
+
{"HTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 0\n\n",
|
143
|
+
"HTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 0\n\n",
|
144
|
+
""},
|
145
|
+
{"myfunc(1,2,3)", "", "myfunc(1,2,3)"},
|
146
|
+
}
|
147
|
+
|
148
|
+
for i, test := range tests {
|
149
|
+
t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
|
150
|
+
buffer := bytes.NewBuffer(nil)
|
151
|
+
buffer.WriteString(test[0])
|
152
|
+
headers, jsonsrc, err := parseHeaders(buffer)
|
153
|
+
if err != nil {
|
154
|
+
t.Logf("error parseHeaders: %v", err)
|
155
|
+
t.FailNow()
|
156
|
+
}
|
157
|
+
assertEqual([]byte(test[1]), headers, t)
|
158
|
+
assertEqual([]byte(test[2]), jsonsrc, t)
|
159
|
+
})
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
func TestCleanJsonp(t *testing.T) {
|
164
|
+
tests := [][]string{
|
165
|
+
{"jsonp(true)", "true", "jsonp"},
|
166
|
+
{"true", "true", ""},
|
167
|
+
{"myJSFunc({\"hello\":\"world\"})", "{\"hello\":\"world\"}", "myJSFunc"},
|
168
|
+
{"[1,2,3]", "[1,2,3]", ""},
|
169
|
+
{"myfunc(1,2,3)", "1,2,3", "myfunc"},
|
170
|
+
{"myfunc(1,2,3)\n", "1,2,3", "myfunc"},
|
171
|
+
}
|
172
|
+
|
173
|
+
for i, test := range tests {
|
174
|
+
t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
|
175
|
+
jsonSrc, jsonpName := cleanJsonp([]byte(test[0]))
|
176
|
+
assertEqual([]byte(test[1]), jsonSrc, t)
|
177
|
+
assertEqual([]byte(test[2]), []byte(jsonpName), t)
|
178
|
+
})
|
179
|
+
}
|
180
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonpretty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sieger
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -47,6 +47,7 @@ extensions: []
|
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
49
|
- ".gitignore"
|
50
|
+
- ".goreleaser.yaml"
|
50
51
|
- CODE_OF_CONDUCT.md
|
51
52
|
- Gemfile
|
52
53
|
- History.txt
|
@@ -55,9 +56,12 @@ files:
|
|
55
56
|
- README.md
|
56
57
|
- Rakefile
|
57
58
|
- exe/jsonpretty
|
59
|
+
- go.mod
|
58
60
|
- jsonpretty.gemspec
|
59
61
|
- lib/jsonpretty.rb
|
60
62
|
- lib/jsonpretty/version.rb
|
63
|
+
- main.go
|
64
|
+
- main_test.go
|
61
65
|
homepage: http://github.com/nicksieger/jsonpretty
|
62
66
|
licenses:
|
63
67
|
- MIT
|