Rework & polish doom-store library

+ Add doom-store-rem
+ Add real doom-store-member-p (to replace doom-store-exists alias)
+ Fix doom-store-clear not clearing in-memory store
+ Add doom-store-flush
+ Add deferred flushing through doom--inhibit-flush lexical var
+ Update doom-store-persist & doom-store-desist for new API
This commit is contained in:
Henrik Lissner 2020-05-02 19:26:34 -04:00
parent 5a54ef1f9f
commit 4b28e9ce86
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395

View file

@ -3,7 +3,7 @@
;; This little library abstracts the process of writing arbitrary elisp values ;; This little library abstracts the process of writing arbitrary elisp values
;; to a 2-tiered file store (in `doom-store-dir'/`doom-store-location'). ;; to a 2-tiered file store (in `doom-store-dir'/`doom-store-location').
(defvar doom-store-dir (concat doom-cache-dir "store/") (defvar doom-store-dir (concat doom-etc-dir "store/")
"Directory to look for and store data accessed through this API.") "Directory to look for and store data accessed through this API.")
(defvar doom-store-persist-alist '(t) (defvar doom-store-persist-alist '(t)
@ -16,15 +16,19 @@ name under `pcache-directory' (by default a subdirectory under
`doom-store-dir'). One file may contain multiple cache entries.") `doom-store-dir'). One file may contain multiple cache entries.")
(defvar doom--store-table (make-hash-table :test 'equal)) (defvar doom--store-table (make-hash-table :test 'equal))
(defvar doom--inhibit-flush nil)
(defun doom-save-persistent-store-h () (defun doom-save-persistent-store-h ()
"Hook to run when an Emacs session is killed. Saves all persisted variables "Hook to run when an Emacs session is killed. Saves all persisted variables
listed in `doom-store-persist-alist' to files." listed in `doom-store-persist-alist' to files."
(dolist (alist (butlast doom-store-persist-alist 1)) (let (locations)
(cl-loop with key = (car alist) (let ((doom--inhibit-flush t))
for var in (cdr alist) (dolist (alist (butlast doom-store-persist-alist 1))
if (symbol-value var) (cl-loop with location = (car alist)
do (doom-store-put var it nil key)))) for var in (cdr alist)
do (doom-store-put var (symbol-value var) nil location)
and do (cl-pushnew location locations))))
(mapc #'doom--store-flush locations)))
(add-hook 'kill-emacs-hook #'doom-save-persistent-store-h) (add-hook 'kill-emacs-hook #'doom-save-persistent-store-h)
@ -34,84 +38,105 @@ listed in `doom-store-persist-alist' to files."
;;;###autoload ;;;###autoload
(defun doom-store-persist (location variables) (defun doom-store-persist (location variables)
"Persist VARIABLES (list of symbols) in LOCATION (symbol). "Persist VARIABLES (list of symbols) in LOCATION (symbol).
This populates these variables with cached values, if one exists, and saves them This populates these variables with cached values, if one exists, and saves them
to file when Emacs quits. to file when Emacs quits. This cannot persist buffer-local variables."
Warning: this is incompatible with buffer-local variables."
(dolist (var variables) (dolist (var variables)
(when (doom-store-exists var location) (when (doom-store-member-p var location)
(set var (doom-store-get var location)))) (set var (doom-store-get var location))))
(setf (alist-get location doom-store-persist-alist) (setf (alist-get location doom-store-persist-alist)
(append variables (cdr (assq location doom-store-persist-alist))))) (append variables (alist-get location doom-store-persist-alist))))
;;;###autoload ;;;###autoload
(defun doom-store-desist (location &optional variables) (defun doom-store-desist (location &optional variables)
"Unregisters VARIABLES (list of symbols) in LOCATION (symbol) from "Unregisters VARIABLES (list of symbols) in LOCATION (symbol).
`doom-store-persist-alist', thus preventing them from being saved between sessions. Variables to persist are recorded in `doom-store-persist-alist'. Does not affect
Does not affect the actual variables themselves or their values." the actual variables themselves or their values."
(if variables (if variables
(setf (alist-get location doom-store-persist-alist) (setf (alist-get location doom-store-persist-alist)
(cl-set-difference (cdr (assq location doom-store-persist-alist)) (cl-set-difference (cdr (assq location doom-store-persist-alist))
variables)) variables))
(delq (assq location doom-store-persist-alist) (delq! location doom-store-persist-alist 'assoc)))
doom-store-persist-alist)))
(defun doom--store-init (location) (defun doom--store-init (location)
(or (gethash location doom--store-table) (or (gethash location doom--store-table)
(if (file-exists-p doom-store-dir) (let* ((file-name-handler-alist nil)
(let* ((store (expand-file-name location doom-store-dir)) (location-path (expand-file-name location doom-store-dir)))
(data (and (file-exists-p store) (if (file-exists-p location-path)
(with-temp-buffer (puthash location
(set-buffer-multibyte nil) (with-temp-buffer
(setq buffer-file-coding-system 'binary) (set-buffer-multibyte nil)
(let (file-name-handler-alist) (setq buffer-file-coding-system 'binary)
(insert-file-contents-literally store)) (insert-file-contents-literally location-path)
(read (current-buffer)))))) (read (current-buffer)))
(puthash location data doom--store-table) doom--store-table)
data) (puthash location (make-hash-table :test 'equal)
(make-hash-table :test #'equal)))) doom--store-table)))))
(defun doom--store-get (key location &optional default-value) (defun doom--store-get (key location &optional default-value)
(if-let* ((location-data (doom--store-init location)) (let* ((location-data (doom--store-init location))
(data (gethash location location-data)) (data (gethash key location-data default-value)))
(_ (or (null (car data)) (if (and (not (eq data default-value))
(not (time-less-p (car data) (current-time)))))) (or (null (car data))
(cdr data) (not (time-less-p (car data) (current-time)))))
default-value)) (cdr data)
default-value)))
(defun doom--store-put (key value location &optional ttl) (defun doom--store-put (key value location &optional ttl)
(let ((data (doom--store-init location))) (puthash key (cons (if ttl (time-add (current-time) ttl)) value)
(puthash location (cons (if ttl (time-add (current-time) ttl)) value) data) (doom--store-init location))
(let ((coding-system-for-write 'binary) (doom--store-flush location))
(defun doom--store-flush (location)
(unless doom--inhibit-flush
(let ((file-name-handler-alist nil)
(coding-system-for-write 'binary)
(write-region-annotate-functions nil) (write-region-annotate-functions nil)
(write-region-post-annotation-function nil)) (write-region-post-annotation-function nil)
(data (doom--store-init location)))
(make-directory doom-store-dir 'parents) (make-directory doom-store-dir 'parents)
(with-temp-file (expand-file-name location doom-store-dir) (with-temp-file (expand-file-name location doom-store-dir)
(prin1 data (current-buffer)))) (prin1 data (current-buffer)))
data)) data)))
;;;###autoload ;;;###autoload
(defun doom-store-get (key &optional location default-value) (defun doom-store-get (key &optional location default-value)
"Retrieve KEY from LOCATION (defaults to `doom-store-location'), if it exists "Retrieve KEY from LOCATION (defaults to `doom-store-location').
and hasn't expired." If it doesn't exist or has expired, DEFAULT_VALUE is returned."
(doom--store-get key (or location doom-store-location) default-value)) (doom--store-get key (or location doom-store-location) default-value))
;;;###autoload ;;;###autoload
(defun doom-store-put (key value &optional ttl location) (defun doom-store-put (key value &optional ttl location)
"Set KEY to VALUE in the cache. TTL is the time (in seconds) until this cache "Set KEY to VALUE in the store at LOCATION.
entry expires. LOCATION is the super-key to store this cache item under; the KEY can be any lisp object that is comparable with `equal'. TTL is the time (in
default is `doom-store-location'. " seconds) until this cache entry expires. LOCATION is the super-key to store this
cache item under. It defaults to `doom-store-location'."
(doom--store-put key value (or location doom-store-location) ttl)) (doom--store-put key value (or location doom-store-location) ttl))
;;;###autoload ;;;###autoload
(defalias 'doom-store-exists #'doom-store-get) (defun doom-store-rem (key &optional location)
"Clear a cache LOCATION (defaults to `doom-store-location')."
(let ((location (or location doom-store-location)))
(remhash key (doom--store-init location))
(let ((table (doom--store-init "default")))
(remhash 'test table)
table)
(doom--store-flush location)))
;;;###autoload
(defun doom-store-member-p (key &optional location)
"Return t if KEY in LOCATION exists.
LOCATION defaults to `doom-store-location'."
(let ((nil-value (format "--nilvalue%s--" (current-time))))
(not (equal (doom-store-get key location nil-value)
nil-value))))
;;;###autoload ;;;###autoload
(defun doom-store-clear (&optional location) (defun doom-store-clear (&optional location)
"Clear a cache LOCATION (defaults to `doom-store-location')." "Clear the store at LOCATION (defaults to `doom-store-location')."
(let ((path (expand-file-name (or location doom-store-location) doom-store-location))) (let* ((location (or location doom-store-location))
(path (expand-file-name location doom-store-dir)))
(remhash location doom--store-table)
(when (file-exists-p path) (when (file-exists-p path)
(delete-file path) (delete-file path)
t))) t)))