diff options
author | Artur Malabarba <bruce.connor.am@gmail.com> | 2015-11-14 14:51:54 +0000 |
---|---|---|
committer | Artur Malabarba <bruce.connor.am@gmail.com> | 2015-11-14 15:00:16 +0000 |
commit | 08919524eb7a623cd383258e4ff26bb607a62ccb (patch) | |
tree | 76c6e5331c172223121c46c0fb857b551fe66eab | |
parent | be74f6a7cffe3fc80604549da5d024652f63fd26 (diff) | |
download | emacs-scratch/api.el.tar.gz |
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." ;;; Requests -(cl-defmacro rest--with-response-buffer (method url &rest body &key async unwind-form - extra-headers &allow-other-keys) - "Run BODY in a Server request buffer. -UNWIND-FORM is run no matter what, and doesn't affect the return -value." - (declare (indent 2) +(cl-defmacro rest-with-response-buffer (url &rest body &key async (method :get) file + unwind-form error-form noerror + extra-headers &allow-other-keys) + "Access URL and run BODY in a buffer containing the resonse. +Point is after the headers when BODY runs. +URL can be a local file name, which must be absolute. + +UNWIND-FORM is run after BODY, even if there was an error during +or before the execution of BODY. ERROR-FORM is run only if an +error occurs. If NOERROR is non-nil, don't propagate errors +caused by the connection or by BODY. Errors signaled by +UNWIND-FORM or ERROR-FORM are not caught. + +EXTRA-HEADERS is an alist of headers used in `url-request-extra-headers'. +ASYNC, if non-nil, runs the request asynchronously." + (declare (indent defun) (debug t)) - (let ((call-name (make-symbol "callback"))) - (while (keywordp (car body)) - (setq body (cdr (cdr body)))) - `(let ((,call-name (lambda (status) - (unwind-protect - (progn (when-let ((er (plist-get status :error))) - (error "Error retrieving: %s %S" ,url er)) - ,@body) - ,unwind-form - (kill-buffer (current-buffer)))))) - (setq method (upcase (replace-regexp-in-string - "\\`:" "" (format "%s" method)))) - (let ((url-request-method ,method) - (url-request-extra-headers - (cons '("Content-Type" . "application/x-www-form-urlencoded") - ,extra-headers))) - (if ,async - (condition-case error-data - (url-retrieve ,url ,call-name nil 'silent) - (error ,unwind-form - (signal (car error-data) (cdr error-data)))) - (let ((buffer (condition-case error-data - (url-retrieve-synchronously ,url 'silent) - (error ,unwind-form - (signal (car error-data) (cdr error-data)))))) - (with-current-buffer buffer - (funcall ,call-name nil)))))))) + (while (keywordp (car body)) + (setq body (cdr (cdr body)))) + (macroexp-let2* nil ((url-1 url)) + `(cl-macrolet ((wrap-errors (&rest bodyforms) + (let ((err (make-symbol "err"))) + `(condition-case ,err + ,(macroexp-progn bodyforms) + ,(list 'error ',error-form ',unwind-form + (list 'unless ',noerror + `(signal (car ,err) (cdr ,err)))))))) + (if (string-match-p "\\`https?:" ,url-1) + (let* ((url-request-method (upcase (replace-regexp-in-string "\\`:" "" (format "%s" ,method)))) + (url-request-extra-headers (cons '("Content-Type" . "application/x-www-form-urlencoded") + ,extra-headers)) + (url (concat ,url-1 ,file)) + (callback (lambda (status) + (let ((b (current-buffer))) + (unwind-protect (wrap-errors + (when-let ((er (plist-get status :error))) + (error "Error retrieving: %s %S" url er)) + (unless (search-forward-regexp "^\r?$" nil 'noerror) + (rest-error 'rest-unintelligible-result)) + (prog1 ,(macroexp-progn body) + ,unwind-form)) + (when (buffer-live-p b) + (kill-buffer b))))))) + (if ,async + (wrap-errors (url-retrieve url callback nil 'silent)) + (let ((buffer (wrap-errors (url-retrieve-synchronously url 'silent)))) + (with-current-buffer buffer + (funcall callback nil))))) + (wrap-errors (with-temp-buffer + (let ((url (expand-file-name ,file ,url-1))) + (unless (file-name-absolute-p url) + (error "Location %s is not a url nor an absolute file name" url)) + (insert-file-contents url)) + (prog1 ,(macroexp-progn body) + ,unwind-form))))))) (defvar-local rest-url-root nil "Prepended to REST url when a full url is not given.") @@ -167,9 +188,9 @@ INFO is a plist returned by `auth-source-search'." "Return an alist containing an \"Authorization\" header. The car of the list is nil, so this function can be used as the AUTH-METHOD in `rest-action'." - `(nil . (("Authorization" . ,(concat "Basic " - (base64-encode-string - (concat user ":" password))))))) + `(nil . (("Authorization" . + ,(concat "Basic " (base64-encode-string + (concat user ":" password))))))) ;;; The function @@ -275,47 +296,48 @@ all of which inherit from `rest-error'. user pass))) (when new-url (setq url new-url)) (setq extra-headers (append headers extra-headers))))) - (rest--with-response-buffer method url - :extra-headers extra-headers - :-url-depth (cons url -url-history) - :async async - (pcase (rest-parse-response-code auth) - (`nil nil) - ((and (pred stringp) link) - (message "Redirected to %s" link) - (apply #'rest-action all-options)) - (`t - (let ((next-page - (when (pcase next-page-rule - (`(header ,name) (search-forward-regexp - (format "^%s: .*<\\([^>]+\\)>;" (regexp-quote name)) - nil t)) - (`(regexp ,rx) (search-forward-regexp rx nil t)) - (_ nil)) - (match-string-no-properties 1)))) - (goto-char (point-min)) - (search-forward-regexp "^\r?$") - (let* ((data (unless (eobp) (funcall reader)))) - (if (or (not next-page) - (< max-pages 2)) - (pcase return - (:simple (funcall callback data)) - (:rich `(,(funcall callback data) - (next-page . ,next-page) - ,@(rest--headers-alist)))) - (rest-action next-page - :auth auth - :method method - :reader reader - :next-page-rule next-page-rule - :return return - :async async - :max-pages (1- max-pages) - :callback (lambda (res) - (funcall callback - (if (listp res) - (append data res) - (vconcat data res)))))))))))) + (rest-with-response-buffer url + :method method + :extra-headers extra-headers + :-url-depth (cons url -url-history) + :async async + (pcase (rest-parse-response-code auth) + (`nil nil) + ((and (pred stringp) link) + (message "Redirected to %s" link) + (apply #'rest-action all-options)) + (`t + (let ((next-page + (when (pcase next-page-rule + (`(header ,name) (search-forward-regexp + (format "^%s: .*<\\([^>]+\\)>;" (regexp-quote name)) + nil t)) + (`(regexp ,rx) (search-forward-regexp rx nil t)) + (_ nil)) + (match-string-no-properties 1)))) + (goto-char (point-min)) + (search-forward-regexp "^\r?$") + (let* ((data (unless (eobp) (funcall reader)))) + (if (or (not next-page) + (< max-pages 2)) + (pcase return + (:simple (funcall callback data)) + (:rich `(,(funcall callback data) + (next-page . ,next-page) + ,@(rest--headers-alist)))) + (rest-action next-page + :auth auth + :method method + :reader reader + :next-page-rule next-page-rule + :return return + :async async + :max-pages (1- max-pages) + :callback (lambda (res) + (funcall callback + (if (listp res) + (append data res) + (vconcat data res)))))))))))) (provide 'rest) ;;; rest.el ends here |