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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3455de5bf8ae23a61024c472d6299f87e7ff5a0fe11fd7e4ac29d9b182a343fe
4
- data.tar.gz: 9bb180a3b61581d468280650f186849199c32018fb048ee9081a00a381a5c5e9
3
+ metadata.gz: 89641014acab0632260843ff87e190604e7342d386109dfa77245ec343ff1ee4
4
+ data.tar.gz: 7fe4a6155fed431b9c64735fa3a8c133354c293a60d9a03c5786a15f3e31348d
5
5
  SHA512:
6
- metadata.gz: 6d8780f2c5b04c888a883b02b31805ba15624ae501b00758d880b96f0918df2faa4c72a42684aa9f6133aad7387de740b3a46c472da8bbe6a320811614b7725e
7
- data.tar.gz: befc010615d96350c633547943379330c27daf66dde2343a71eeeab746fa4369f35aec91cc5504bf69e87787e70395a63fb1093462df72c4db4e1af402da1a4d
6
+ metadata.gz: 17e783dbab3458c8c46ef6b9cb3f6065f6552eccbe65b6ba408003709bed7cbc92e98ab4f3d09750363fd2fbd7426df7accb2ea4716ec24f53b5c92e24789d14
7
+ data.tar.gz: d3b01927f851234aba103921e43b8db1cc2d07383ea73c825b78027cd6bfacf8b763be0112aff91ba8e08dd53a1b27d61129aa1dd10861a4267e438d3d7dd6e2
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
1
  *.gem
2
2
  pkg
3
3
  /Gemfile.lock
4
+
5
+ dist/
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
@@ -1,3 +1,7 @@
1
+ === 1.2.0 / 2021-12-30
2
+
3
+ * New version to correspond with go port
4
+
1
5
  === 1.1.2 / 2021-12-27
2
6
 
3
7
  * Fix regex typo, thanks Sami Samhuri
data/README.md CHANGED
@@ -42,4 +42,4 @@ brew gem install jsonpretty
42
42
 
43
43
  ## LICENSE
44
44
 
45
- See [LICENSE](LICENSE).
45
+ See [LICENSE](LICENSE.txt).
data/go.mod ADDED
@@ -0,0 +1,3 @@
1
+ module github.com/nicksieger/jsonpretty
2
+
3
+ go 1.17
@@ -1,3 +1,3 @@
1
1
  class Jsonpretty
2
- VERSION = "1.1.2"
2
+ VERSION = "1.2.0"
3
3
  end
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.1.2
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-27 00:00:00.000000000 Z
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