aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Malabarba2015-11-14 14:51:54 +0000
committerArtur Malabarba2015-11-14 15:00:16 +0000
commit08919524eb7a623cd383258e4ff26bb607a62ccb (patch)
tree76c6e5331c172223121c46c0fb857b551fe66eab
parentbe74f6a7cffe3fc80604549da5d024652f63fd26 (diff)
downloademacs-scratch/api.el.tar.gz
emacs-scratch/api.el.zip
Make rest-with-response-buffer more broadly usefulscratch/api.el
-rw-r--r--lisp/emacs-lisp/rest.el176
1 files changed, 99 insertions, 77 deletions
diff --git a/lisp/emacs-lisp/rest.el b/lisp/emacs-lisp/rest.el
index 08408c660de..b52e2f40a9b 100644
--- a/lisp/emacs-lisp/rest.el
+++ b/lisp/emacs-lisp/rest.el
@@ -94,40 +94,61 @@ Leave point at the return code on the first line."
94 94
95 95
96;;; Requests 96;;; Requests
97(cl-defmacro rest--with-response-buffer (method url &rest body &key async unwind-form 97(cl-defmacro rest-with-response-buffer (url &rest body &key async (method :get) file
98 extra-headers &allow-other-keys) 98 unwind-form error-form noerror
99 "Run BODY in a Server request buffer. 99 extra-headers &allow-other-keys)
100UNWIND-FORM is run no matter what, and doesn't affect the return 100 "Access URL and run BODY in a buffer containing the resonse.
101value." 101Point is after the headers when BODY runs.
102 (declare (indent 2) 102URL can be a local file name, which must be absolute.
103
104UNWIND-FORM is run after BODY, even if there was an error during
105or before the execution of BODY. ERROR-FORM is run only if an
106error occurs. If NOERROR is non-nil, don't propagate errors
107caused by the connection or by BODY. Errors signaled by
108UNWIND-FORM or ERROR-FORM are not caught.
109
110EXTRA-HEADERS is an alist of headers used in `url-request-extra-headers'.
111ASYNC, if non-nil, runs the request asynchronously."
112 (declare (indent defun)
103 (debug t)) 113 (debug t))
104 (let ((call-name (make-symbol "callback"))) 114 (while (keywordp (car body))
105 (while (keywordp (car body)) 115 (setq body (cdr (cdr body))))
106 (setq body (cdr (cdr body)))) 116 (macroexp-let2* nil ((url-1 url))
107 `(let ((,call-name (lambda (status) 117 `(cl-macrolet ((wrap-errors (&rest bodyforms)
108 (unwind-protect 118 (let ((err (make-symbol "err")))
109 (progn (when-let ((er (plist-get status :error))) 119 `(condition-case ,err
110 (error "Error retrieving: %s %S" ,url er)) 120 ,(macroexp-progn bodyforms)
111 ,@body) 121 ,(list 'error ',error-form ',unwind-form
112 ,unwind-form 122 (list 'unless ',noerror
113 (kill-buffer (current-buffer)))))) 123 `(signal (car ,err) (cdr ,err))))))))
114 (setq method (upcase (replace-regexp-in-string 124 (if (string-match-p "\\`https?:" ,url-1)
115 "\\`:" "" (format "%s" method)))) 125 (let* ((url-request-method (upcase (replace-regexp-in-string "\\`:" "" (format "%s" ,method))))
116 (let ((url-request-method ,method) 126 (url-request-extra-headers (cons '("Content-Type" . "application/x-www-form-urlencoded")
117 (url-request-extra-headers 127 ,extra-headers))
118 (cons '("Content-Type" . "application/x-www-form-urlencoded") 128 (url (concat ,url-1 ,file))
119 ,extra-headers))) 129 (callback (lambda (status)
120 (if ,async 130 (let ((b (current-buffer)))
121 (condition-case error-data 131 (unwind-protect (wrap-errors
122 (url-retrieve ,url ,call-name nil 'silent) 132 (when-let ((er (plist-get status :error)))
123 (error ,unwind-form 133 (error "Error retrieving: %s %S" url er))
124 (signal (car error-data) (cdr error-data)))) 134 (unless (search-forward-regexp "^\r?$" nil 'noerror)
125 (let ((buffer (condition-case error-data 135 (rest-error 'rest-unintelligible-result))
126 (url-retrieve-synchronously ,url 'silent) 136 (prog1 ,(macroexp-progn body)
127 (error ,unwind-form 137 ,unwind-form))
128 (signal (car error-data) (cdr error-data)))))) 138 (when (buffer-live-p b)
129 (with-current-buffer buffer 139 (kill-buffer b)))))))
130 (funcall ,call-name nil)))))))) 140 (if ,async
141 (wrap-errors (url-retrieve url callback nil 'silent))
142 (let ((buffer (wrap-errors (url-retrieve-synchronously url 'silent))))
143 (with-current-buffer buffer
144 (funcall callback nil)))))
145 (wrap-errors (with-temp-buffer
146 (let ((url (expand-file-name ,file ,url-1)))
147 (unless (file-name-absolute-p url)
148 (error "Location %s is not a url nor an absolute file name" url))
149 (insert-file-contents url))
150 (prog1 ,(macroexp-progn body)
151 ,unwind-form)))))))
131 152
132(defvar-local rest-url-root nil 153(defvar-local rest-url-root nil
133 "Prepended to REST url when a full url is not given.") 154 "Prepended to REST url when a full url is not given.")
@@ -167,9 +188,9 @@ INFO is a plist returned by `auth-source-search'."
167 "Return an alist containing an \"Authorization\" header. 188 "Return an alist containing an \"Authorization\" header.
168The car of the list is nil, so this function can be used as the 189The car of the list is nil, so this function can be used as the
169AUTH-METHOD in `rest-action'." 190AUTH-METHOD in `rest-action'."
170 `(nil . (("Authorization" . ,(concat "Basic " 191 `(nil . (("Authorization" .
171 (base64-encode-string 192 ,(concat "Basic " (base64-encode-string
172 (concat user ":" password))))))) 193 (concat user ":" password)))))))
173 194
174 195
175;;; The function 196;;; The function
@@ -275,47 +296,48 @@ all of which inherit from `rest-error'.
275 user pass))) 296 user pass)))
276 (when new-url (setq url new-url)) 297 (when new-url (setq url new-url))
277 (setq extra-headers (append headers extra-headers))))) 298 (setq extra-headers (append headers extra-headers)))))
278 (rest--with-response-buffer method url 299 (rest-with-response-buffer url
279 :extra-headers extra-headers 300 :method method
280 :-url-depth (cons url -url-history) 301 :extra-headers extra-headers
281 :async async 302 :-url-depth (cons url -url-history)
282 (pcase (rest-parse-response-code auth) 303 :async async
283 (`nil nil) 304 (pcase (rest-parse-response-code auth)
284 ((and (pred stringp) link) 305 (`nil nil)
285 (message "Redirected to %s" link) 306 ((and (pred stringp) link)
286 (apply #'rest-action all-options)) 307 (message "Redirected to %s" link)
287 (`t 308 (apply #'rest-action all-options))
288 (let ((next-page 309 (`t
289 (when (pcase next-page-rule 310 (let ((next-page
290 (`(header ,name) (search-forward-regexp 311 (when (pcase next-page-rule
291 (format "^%s: .*<\\([^>]+\\)>;" (regexp-quote name)) 312 (`(header ,name) (search-forward-regexp
292 nil t)) 313 (format "^%s: .*<\\([^>]+\\)>;" (regexp-quote name))
293 (`(regexp ,rx) (search-forward-regexp rx nil t)) 314 nil t))
294 (_ nil)) 315 (`(regexp ,rx) (search-forward-regexp rx nil t))
295 (match-string-no-properties 1)))) 316 (_ nil))
296 (goto-char (point-min)) 317 (match-string-no-properties 1))))
297 (search-forward-regexp "^\r?$") 318 (goto-char (point-min))
298 (let* ((data (unless (eobp) (funcall reader)))) 319 (search-forward-regexp "^\r?$")
299 (if (or (not next-page) 320 (let* ((data (unless (eobp) (funcall reader))))
300 (< max-pages 2)) 321 (if (or (not next-page)
301 (pcase return 322 (< max-pages 2))
302 (:simple (funcall callback data)) 323 (pcase return
303 (:rich `(,(funcall callback data) 324 (:simple (funcall callback data))
304 (next-page . ,next-page) 325 (:rich `(,(funcall callback data)
305 ,@(rest--headers-alist)))) 326 (next-page . ,next-page)
306 (rest-action next-page 327 ,@(rest--headers-alist))))
307 :auth auth 328 (rest-action next-page
308 :method method 329 :auth auth
309 :reader reader 330 :method method
310 :next-page-rule next-page-rule 331 :reader reader
311 :return return 332 :next-page-rule next-page-rule
312 :async async 333 :return return
313 :max-pages (1- max-pages) 334 :async async
314 :callback (lambda (res) 335 :max-pages (1- max-pages)
315 (funcall callback 336 :callback (lambda (res)
316 (if (listp res) 337 (funcall callback
317 (append data res) 338 (if (listp res)
318 (vconcat data res)))))))))))) 339 (append data res)
340 (vconcat data res))))))))))))
319 341
320(provide 'rest) 342(provide 'rest)
321;;; rest.el ends here 343;;; rest.el ends here