branch: externals/plz commit 725a8d4d214ca873420bbca7fe5d730e4ba2cad4 Merge: 106ef828ea ff8a1e9aaa Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Merge: Download ":as 'file" directly to files --- README.org | 1 + plz.el | 91 +++++++++++++++++++++++++++++++------------------------------- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/README.org b/README.org index 47c16ab5db..f70586c330 100644 --- a/README.org +++ b/README.org @@ -197,6 +197,7 @@ You may also clear a queue with ~plz-clear~, which cancels any active or queued *Changes* + Option ~plz-timeout~ is removed. (It was the default value for ~plz~'s ~:timeout~ argument, which is passed to Curl as its ~--max-time~ argument, limiting the total duration of a request operation. This argument should be unset by default, because larger or slower downloads might not finish within a certain duration, and it is surprising to the user to have this option set by default, potentially causing requests to timeout unnecessarily.) ++ Using arguments ~:as 'file~ or ~:as '(file FILENAME)~ now passes the filename to Curl, allowing it to write the data to the file itself (rather than receiving the data into an Emacs buffer and then writing it to a file. This improves performance when downloading large files, significantly reducing Emacs's CPU and memory usage). *Fixes* diff --git a/plz.el b/plz.el index 56ae9d4e04..4bcae3723e 100644 --- a/plz.el +++ b/plz.el @@ -419,7 +419,8 @@ into the process buffer. ;; the empty string. See <https://gms.tf/when-curl-sends-100-continue.html>. ;; TODO: Handle "100 Continue" responses and remove this workaround. (push (cons "Expect" "") headers) - (let* ((data-arg (pcase-exhaustive body-type + (let* (filename + (data-arg (pcase-exhaustive body-type ('binary "--data-binary") ('text "--data"))) (curl-command-line-args (append plz-curl-default-args @@ -443,21 +444,49 @@ into the process buffer. ;; method. (pcase method ('get - (list (cons "--dump-header" "-"))) + (append (list (cons "--dump-header" "-")) + (pcase as + ('file + (setf filename (make-temp-file "plz-")) + (list (cons "--output" filename))) + (`(file ,(and (pred stringp) as-filename)) + (when (file-exists-p as-filename) + (error "File exists, will not overwrite: %S" as-filename)) + (setf filename as-filename) + (list (cons "--output" filename)))))) ((or 'put 'post) - (list (cons "--dump-header" "-") - (cons "--request" (upcase (symbol-name method))) - ;; It appears that this must be the last argument - ;; in order to pass data on the rest of STDIN. - (pcase body - (`(file ,filename) - ;; Use `expand-file-name' because curl doesn't - ;; expand, e.g. "~" into "/home/...". - (cons "--upload-file" (expand-file-name filename))) - (_ (cons data-arg "@-"))))) + (append (list (cons "--dump-header" "-") + (cons "--request" (upcase (symbol-name method)))) + (pcase as + ('file + (setf filename (make-temp-file "plz-")) + (list (cons "--output" filename))) + (`(file ,(and (pred stringp) as-filename)) + (when (file-exists-p as-filename) + (error "File exists, will not overwrite: %S" as-filename)) + (setf filename as-filename) + (list (cons "--output" filename)))) + (list + ;; It appears that this must be the last argument + ;; in order to pass data on the rest of STDIN. + (pcase body + (`(file ,filename) + ;; Use `expand-file-name' because curl doesn't + ;; expand, e.g. "~" into "/home/...". + (cons "--upload-file" (expand-file-name filename))) + (_ (cons data-arg "@-")))))) ('delete - (list (cons "--dump-header" "-") - (cons "--request" (upcase (symbol-name method))))) + (append (list (cons "--dump-header" "-") + (cons "--request" (upcase (symbol-name method)))) + (pcase as + ('file + (setf filename (make-temp-file "plz-")) + (list (cons "--output" filename))) + (`(file ,(and (pred stringp) as-filename)) + (when (file-exists-p as-filename) + (error "File exists, will not overwrite: %S" as-filename)) + (setf filename as-filename) + (list (cons "--output" filename)))))) ('head (list (cons "--head" "") (cons "--request" "HEAD")))))) @@ -520,39 +549,11 @@ into the process buffer. (make-plz-error :message (format "response is nil for buffer:%S buffer-string:%S" process-buffer (buffer-string))))))) ('file (lambda () - (set-buffer-multibyte nil) - (plz--narrow-to-body) - (let ((filename (make-temp-file "plz-"))) - (condition-case err - (progn - ;; FIXME: Separate condition-case for writing the file. - (write-region (point-min) (point-max) filename) - (funcall then filename)) - (file-already-exists - (funcall then (make-plz-error :message (format "error while writing to file %S: %S" filename err)))) - ;; 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)) - (funcall then (make-plz-error :message (format "error while writing to file %S: %S" filename err)))))))) + (funcall then filename))) (`(file ,(and (pred stringp) filename)) + ;; This requires a separate clause due to the FILENAME binding. (lambda () - (set-buffer-multibyte nil) - (plz--narrow-to-body) - (condition-case err - (progn - (write-region (point-min) (point-max) filename nil nil nil 'excl) - (funcall then filename)) - (file-already-exists - (funcall then (make-plz-error :message (format "error while writing to file %S: %S" filename err)))) - ;; 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))) - (funcall then (make-plz-error :message (format "error while writing to file %S: %S" filename err))))))) + (funcall then filename))) ((pred functionp) (lambda () (let ((coding-system (or (plz--coding-system) 'utf-8))) (plz--narrow-to-body)