diff options
| author | Artur Malabarba | 2015-11-14 14:51:54 +0000 |
|---|---|---|
| committer | Artur Malabarba | 2015-11-14 15:00:16 +0000 |
| commit | 08919524eb7a623cd383258e4ff26bb607a62ccb (patch) | |
| tree | 76c6e5331c172223121c46c0fb857b551fe66eab | |
| parent | be74f6a7cffe3fc80604549da5d024652f63fd26 (diff) | |
| download | emacs-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.el | 176 |
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) |
| 100 | UNWIND-FORM is run no matter what, and doesn't affect the return | 100 | "Access URL and run BODY in a buffer containing the resonse. |
| 101 | value." | 101 | Point is after the headers when BODY runs. |
| 102 | (declare (indent 2) | 102 | URL can be a local file name, which must be absolute. |
| 103 | |||
| 104 | UNWIND-FORM is run after BODY, even if there was an error during | ||
| 105 | or before the execution of BODY. ERROR-FORM is run only if an | ||
| 106 | error occurs. If NOERROR is non-nil, don't propagate errors | ||
| 107 | caused by the connection or by BODY. Errors signaled by | ||
| 108 | UNWIND-FORM or ERROR-FORM are not caught. | ||
| 109 | |||
| 110 | EXTRA-HEADERS is an alist of headers used in `url-request-extra-headers'. | ||
| 111 | ASYNC, 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. |
| 168 | The car of the list is nil, so this function can be used as the | 189 | The car of the list is nil, so this function can be used as the |
| 169 | AUTH-METHOD in `rest-action'." | 190 | AUTH-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 |