Use request.el to make http requests

This commit is contained in:
louie 2018-05-13 15:52:37 +08:00
parent 3cf37e7c80
commit 8a1cfd3bfe

View file

@ -6,7 +6,7 @@
;; Description: Make Anki Cards in Org-mode ;; Description: Make Anki Cards in Org-mode
;; Author: Louie Tan ;; Author: Louie Tan
;; Version: 0.2.1 ;; Version: 0.2.1
;; Package-Requires: ((emacs "25")) ;; Package-Requires: ((emacs "25") (request "0.3.0"))
;; URL: https://github.com/louietan/anki-editor ;; URL: https://github.com/louietan/anki-editor
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -63,6 +63,7 @@
(require 'json) (require 'json)
(require 'org-element) (require 'org-element)
(require 'ox) (require 'ox)
(require 'request)
(require 'seq) (require 'seq)
(defconst anki-editor-prop-note-type "ANKI_NOTE_TYPE") (defconst anki-editor-prop-note-type "ANKI_NOTE_TYPE")
@ -104,29 +105,23 @@ See https://apps.ankiweb.net/docs/manual.html#latex-conflicts.")
(if params (if params
(push `("params" . ,params) data) (push `("params" . ,params) data)
data))) data)))
(request-tempfile (make-temp-file "emacs-anki-editor"))) (request-backend 'curl)
(json-array-type 'list)
reply err)
(with-temp-file request-tempfile (request (format "http://%s:%s"
(setq buffer-file-coding-system 'utf-8) anki-editor-anki-connect-listening-address
(set-buffer-multibyte t) anki-editor-anki-connect-listening-port)
(insert request-body)) :type "POST"
:parser 'json-read
(let* ((raw-resp (shell-command-to-string :data (encode-coding-string request-body 'utf-8)
(format "curl %s:%s --silent -X POST --data-binary @%s" :success (cl-function (lambda (&key data &allow-other-keys)
(shell-quote-argument anki-editor-anki-connect-listening-address) (setq reply data)))
(shell-quote-argument anki-editor-anki-connect-listening-port) :error (cl-function (lambda (&key _ &key error-thrown &allow-other-keys)
(shell-quote-argument request-tempfile)))) (setq err (string-trim (cdr error-thrown)))))
resp error) :sync t)
(condition-case err (when err (error "Error communicating with AnkiConnect using cURL: %s" err))
(let ((json-array-type 'list)) (or reply (error "Got empty reply from AnkiConnect"))))
(setq resp (json-read-from-string raw-resp)
error (alist-get 'error resp)))
(error (setq error
(format "Unexpected error communicating with AnkiConnect: %s, the response was %s"
(error-message-string err)
(prin1-to-string raw-resp)))))
`((result . ,(alist-get 'result resp))
(error . ,error)))))
(defmacro anki-editor--anki-connect-invoke-result (&rest args) (defmacro anki-editor--anki-connect-invoke-result (&rest args)
"Invoke AnkiConnect with ARGS, return the result from response or raise an error." "Invoke AnkiConnect with ARGS, return the result from response or raise an error."
@ -148,16 +143,10 @@ See https://apps.ankiweb.net/docs/manual.html#latex-conflicts.")
;; to be type of list. ;; to be type of list.
(cons "tags" (vconcat .tags))))) (cons "tags" (vconcat .tags)))))
(defun anki-editor--anki-connect-heading-to-note (heading)
"Convert HEADING to a note in the form that AnkiConnect accepts."
(anki-editor--anki-connect-map-note
(anki-editor--heading-to-note heading)))
(defun anki-editor--anki-connect-store-media-file (path) (defun anki-editor--anki-connect-store-media-file (path)
"Store media file for PATH, which is an absolute file name. "Store media file for PATH, which is an absolute file name.
The result is the path to the newly stored media file." The result is the path to the newly stored media file."
(unless (and (executable-find "base64") (unless (every #'executable-find '("base64" "sha1sum"))
(executable-find "sha1sum"))
(error "Please make sure `base64' and `sha1sum' are available from your shell, which are required for storing media files")) (error "Please make sure `base64' and `sha1sum' are available from your shell, which are required for storing media files"))
(let* ((content (string-trim (let* ((content (string-trim
@ -238,14 +227,14 @@ of that heading."
(acc 0) (acc 0)
(failed 0)) (failed 0))
(message "Counting notes...") (message "Counting notes...")
(org-map-entries (lambda () (incf total)) match scope) (org-map-entries (lambda () (cl-incf total)) match scope)
(org-map-entries (lambda () (org-map-entries (lambda ()
(message "[%d/%d] Processing notes in buffer \"%s\", wait a moment..." (message "[%d/%d] Processing notes in buffer \"%s\", wait a moment..."
(incf acc) total (buffer-name)) (cl-incf acc) total (buffer-name))
(anki-editor--clear-failure-reason) (anki-editor--clear-failure-reason)
(condition-case err (condition-case err
(anki-editor--process-note-heading) (anki-editor--process-note-heading)
(error (incf failed) (error (cl-incf failed)
(anki-editor--set-failure-reason (error-message-string err))))) (anki-editor--set-failure-reason (error-message-string err)))))
match match
scope) scope)
@ -468,11 +457,11 @@ Do nothing when JUST-ALIGN is non-nil."
(defun anki-editor--heading-to-note (heading) (defun anki-editor--heading-to-note (heading)
"Construct an alist representing a note for HEADING." "Construct an alist representing a note for HEADING."
(let (deck note-id note-type tags fields) (let ((org-trust-scanner-tags t)
deck note-id note-type tags fields)
(setq deck (org-entry-get-with-inheritance anki-editor-prop-deck) (setq deck (org-entry-get-with-inheritance anki-editor-prop-deck)
note-id (org-entry-get nil anki-editor-prop-note-id) note-id (org-entry-get nil anki-editor-prop-note-id)
note-type (org-entry-get nil anki-editor-prop-note-type) note-type (org-entry-get nil anki-editor-prop-note-type)
;; TODO: use `org-scanner-tags' instead, which is said to be faster
tags (org-get-tags-at) tags (org-get-tags-at)
fields (mapcar #'anki-editor--heading-to-note-field (anki-editor--get-subheadings heading))) fields (mapcar #'anki-editor--heading-to-note-field (anki-editor--get-subheadings heading)))