-
Notifications
You must be signed in to change notification settings - Fork 1
/
parse.rkt
122 lines (103 loc) · 3.07 KB
/
parse.rkt
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
#lang racket/base
(require racket/contract
racket/port
json)
(require http/request)
(require (only-in net/url-string
url->string))
(require net/url-structs)
(require web-server/http/response-structs)
(require (only-in racket/list
empty))
(module+ test
(require rackunit))
(define/contract (port->jsexpr in)
(-> input-port? jsexpr?)
(bytes->jsexpr (port->bytes in)))
(define (parse-json-port p)
(define (parse-fail err) (values #f #f))
(with-handlers ([exn:fail? parse-fail])
(let ([js (port->jsexpr p)])
(cond ((eof-object? js)
(values #f #f))
(else
(values js #t))))))
(provide parse-json-port)
;; string? -> jsexpr? boolean?
(define (parse-json-string str)
(define (parse-fail err) (values #f #f))
(with-handlers ([exn:fail? parse-fail])
(values (string->jsexpr str) #t)))
(module+ test
(let ([js "x"])
(let-values ([(whatever ok?) (parse-json-string js)])
(check-false ok?)))
(let ([js "{}"])
(let-values ([(whatever ok?) (parse-json-string js)])
(check-true ok?))))
(provide parse-json-string)
;; bytes? -> jsexpr? boolean?
(define (parse-json-bytes bstr)
(define (parse-fail err) (values #f #f))
(with-handlers ([exn:fail? parse-fail])
(values (bytes->jsexpr bstr) #t)))
(provide parse-json-bytes)
(define (parse-json-file f)
(let ([p (open-input-file f)])
(begin0
(parse-json-port p)
(close-input-port p))))
(provide parse-json-file)
(define (parse-json-url u)
(define url/str (url->string u))
(define-values (path header)
(uri&headers->path&header url/str (list)))
(define-values (in out)
(connect-uri url/str))
(define ok? (start-request in
out
"1.1"
"GET"
path
header))
(define h (purify-port/log-debug in))
(define js/bytes (read-entity/bytes in h))
(parse-json-bytes js/bytes))
(provide parse-json-url)
(define (parse-json-response r)
(parse-json-bytes (call-with-output-bytes (response-output r))))
(module+ test
(let ([r (response 301
#"OK"
(current-seconds)
#"text/html;charset=utf-8"
empty
(lambda (op) (write-bytes #"true" op)))])
(let-values ([(js ok?) (parse-json-response r)])
(check-true (jsexpr? js))
(check-true ok?)
(check-true js))))
(define (can-parse? x)
(or (and (path? x)
(file-exists? x))
(bytes? x)
(string? x)
(input-port? x)
(url? x)
(response? x)))
(define (parse-json js)
(cond ((not (can-parse? js))
(values #f #f))
((path? js)
(parse-json-file js))
((bytes? js)
(parse-json-bytes js))
((string? js)
(parse-json-string js))
((input-port? js)
(parse-json-port js))
((url? js)
(parse-json-url js))
(else
(error "Cannot parse as JSON:" js))))
(provide parse-json)