branch: externals/plz commit f1a89a8816f56c1560044bfd54ca3d56824ef2f8 Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Add: Download :as file --- README.org | 2 ++ plz.el | 39 +++++++++++++++++++++++++++++++++++++++ tests/test-plz.el | 30 ++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/README.org b/README.org index 9d9f8c259a..f96317c442 100644 --- a/README.org +++ b/README.org @@ -115,6 +115,8 @@ Synchronously download a JPEG file, then create an Emacs image object from the d - ~string~ to pass the response body as a decoded string. - ~response~ to pass a ~plz-response~ struct. - A function, to pass its return value; it is called in the response buffer, which is narrowed to the response body (suitable for, e.g. ~json-read~). + - ~file~ to pass a temporary filename to which the response body has been saved without decoding. + - ~(file FILENAME)~ to pass ~FILENAME~ after having saved the response body to it without decoding. ~FILENAME~ must be a non-existent file; if it exists, it will not be overwritten, and an error will be signaled. If ~DECODE~ is non-nil, the response body is decoded automatically. For binary content, it should be nil. When ~AS~ is ~binary~, ~DECODE~ is automatically set to nil. diff --git a/plz.el b/plz.el index 86a9a270b5..81c2112d84 100644 --- a/plz.el +++ b/plz.el @@ -231,9 +231,21 @@ THEN, or the kind of result to return for synchronous requests. It may be: - `buffer' to pass the response buffer. + - `binary' to pass the response body as an undecoded string. + - `string' to pass the response body as a decoded string. + - `response' to pass a `plz-response' struct. + +- `file' to pass a temporary filename to which the response body + has been saved without decoding. + +- `(file FILENAME)' to pass FILENAME after having saved the + response body to it without decoding. FILENAME must be a + non-existent file; if it exists, it will not be overwritten, + and an error will be signaled. + - A function, which is called in the response buffer with it narrowed to the response body (suitable for, e.g. `json-read'). @@ -330,6 +342,33 @@ NOQUERY is passed to `make-process', which see." (funcall then (current-buffer)))) ('response (lambda () (funcall then (plz--response :decode-p decode)))) + ('file (lambda () + (set-buffer-multibyte nil) + (plz--narrow-to-body) + (let ((filename (make-temp-file "plz-"))) + (condition-case err + (write-region (point-min) (point-max) filename) + ;; In case of an error writing to the file, delete the temp file + ;; and signal the error. Ignore any errors encountered while + ;; deleting the file, which would obscure the original error. + (error (ignore-errors + (delete-file filename)) + (signal (car err) (cdr err)))) + (funcall then filename)))) + (`(file ,(and (pred stringp) filename)) + (lambda () + (set-buffer-multibyte nil) + (plz--narrow-to-body) + (condition-case err + (write-region (point-min) (point-max) filename nil nil nil 'excl) + ;; Since we are creating the file, it seems sensible to delete it in case of an + ;; error while writing to it (e.g. a disk-full error). And we ignore any errors + ;; encountered while deleting the file, which would obscure the original error. + (error (ignore-errors + (when (file-exists-p filename) + (delete-file filename))) + (signal (car err) (cdr err)))) + (funcall then filename))) ((pred functionp) (lambda () (let ((coding-system (or (plz--coding-system) 'utf-8))) (plz--narrow-to-body) diff --git a/tests/test-plz.el b/tests/test-plz.el index 6cc3f3397f..49fd0271df 100644 --- a/tests/test-plz.el +++ b/tests/test-plz.el @@ -346,6 +346,36 @@ :as 'binary :then 'sync))) (should (equal 'jpeg (image-type-from-data jpeg))))) +;;;;; Downloading to files + +(ert-deftest plz-get-temp-file () + (let ((filename (plz 'get "https://httpbin.org/image/jpeg" + :as 'file :then 'sync))) + (unwind-protect + (let ((jpeg-data (with-temp-buffer + (insert-file-contents filename) + (buffer-string)))) + (should (equal 'jpeg (image-type-from-data jpeg-data)))) + ;; It's a temp file, so it should always be deleted. + (delete-file filename)))) + +(ert-deftest plz-get-named-file () + (let ((filename (make-temp-file "plz-"))) + ;; HACK: Delete the temp file and reuse its name, because + ;; `make-temp-name' is less convenient to use. + (delete-file filename) + (unwind-protect + (progn + (plz 'get "https://httpbin.org/image/jpeg" + :as `(file ,filename) :then 'sync) + (let ((jpeg-data (with-temp-buffer + (insert-file-contents filename) + (buffer-string)))) + (should (equal 'jpeg (image-type-from-data jpeg-data))))) + ;; It's a temp file, so it should always be deleted. + (when (file-exists-p filename) + (delete-file filename))))) + ;;;; Footer (provide 'test-plz)