summaryrefslogtreecommitdiff
path: root/src/wasp-db.el
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2024-03-23 02:38:12 -0400
committerLLLL Colonq <llll@colonq>2024-03-23 02:38:12 -0400
commit8e9db9303fc5d72ddfdc9ab4a9adaa8299e6e21a (patch)
tree4c516a7facdbb599649eec2b4e61ff70307bddd6 /src/wasp-db.el
Initial commit
Diffstat (limited to 'src/wasp-db.el')
-rw-r--r--src/wasp-db.el119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/wasp-db.el b/src/wasp-db.el
new file mode 100644
index 00000000..46999b5e
--- /dev/null
+++ b/src/wasp-db.el
@@ -0,0 +1,119 @@
+;;; wasp-db --- Redis protocol -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'cl-lib)
+(require 'queue)
+(require 'dash)
+(require 'wasp-utils)
+
+(defcustom w/db-process "wasp-db"
+ "Name of process connected to Redis."
+ :type '(string)
+ :group 'wasp)
+
+(defcustom w/db-buffer " *wasp-db*"
+ "Name of buffer used to store intermediate Redis connection data."
+ :type '(string)
+ :group 'wasp)
+
+(defcustom w/db-host "shiro"
+ "Hostname of the Redis server."
+ :type '(string)
+ :group 'wasp)
+
+(defcustom w/db-port 6379
+ "Port of the Redis server."
+ :type '(integer)
+ :group 'wasp)
+
+(defvar w/db-callback-queue (make-queue)
+ "Queue of callbacks to handle incoming responses.")
+
+(defun w/db-parse-rest ()
+ "Parse everything before the \r\n terminator (and consume the terminator)."
+ (let ((res (w/eat (lambda (c) (/= c ?\r)))))
+ (w/munch ?\r)
+ (w/munch ?\n)
+ res))
+
+(defun w/db-parse-value ()
+ "Parse a single RESP value from the current buffer."
+ (when-let ((c (char-after)))
+ (delete-char 1)
+ (cl-case c
+ (?+ (w/db-parse-rest))
+ (?: (string-to-number (w/db-parse-rest)))
+ (?$
+ (let* ((len (string-to-number (w/db-parse-rest)))
+ (ret (w/devour (point) (+ (point) len))))
+ (w/munch ?\r)
+ (w/munch ?\n)
+ ret))
+ (?*
+ (let ((len (string-to-number (w/db-parse-rest))))
+ (--map (w/db-parse-value) (-iota len))))
+ (otherwise (error (format "Unknown Redis sigil: %s" c))))))
+
+(defun w/db-parse-response ()
+ "Try to parse a single RESP value from the current process buffer.
+If successful, pass the value to the queued callback and return non-nil.
+If not, return nil."
+ (when-let ((v (w/db-parse-value)))
+ (when-let ((cb (queue-dequeue w/db-callback-queue)))
+ (funcall cb v))
+ t))
+
+(defun w/db-process-filter (proc data)
+ "Process filter for Redis connection on PROC and DATA."
+ (with-current-buffer (get-buffer-create w/db-buffer)
+ (when (not (marker-position (process-mark proc)))
+ (set-marker (process-mark proc) (point-max)))
+ (goto-char (process-mark proc))
+ (insert data)
+ (set-marker (process-mark proc) (point))
+ (goto-char (point-min))
+ (while (w/db-parse-response))))
+
+(defun w/db-encode (x)
+ "Encode X for Redis."
+ (cond
+ ((listp x) (format "*%d\r\n%s\r\n" (length x) (apply #'s-concat (-map #'w/db-encode x))))
+ ((stringp x) (format "$%d\r\n%s\r\n" (string-bytes x) x))))
+
+
+(defun w/db-send-raw (msg)
+ "Send MSG to Redis."
+ (process-send-string w/db-process msg))
+
+(defun w/db-cmd (cmd k)
+ "Run CMD in Redis and pass the result to K."
+ (let ((enc (w/db-encode cmd)))
+ (queue-enqueue w/db-callback-queue k)
+ (w/db-send-raw enc)))
+
+(defun w/db-disconnect ()
+ "Disconnect from Redis."
+ (when (process-live-p (get-process w/db-process))
+ (delete-process w/db-process)))
+
+(defun w/db-connect ()
+ "Connect to Redis."
+ (w/db-disconnect)
+ (make-network-process
+ :name w/db-process
+ :buffer nil
+ :host w/db-host
+ :service w/db-port
+ :filter #'w/db-process-filter))
+
+(defun w/db-set (key val)
+ "Set KEY to VAL in Redis."
+ (w/db-cmd `("SET" ,key ,val) (lambda (_) (message "ok"))))
+
+(defun w/db-get (key k)
+ "Get KEY from Redis and pass the corresponding value to K."
+ (w/db-cmd `("GET" ,key) k))
+
+(provide 'wasp-db)
+;;; wasp-db.el ends here