Compare commits

1 Commits

Author SHA1 Message Date
ed02b8aadb update url 2021-10-27 21:17:16 -04:00
3 changed files with 73 additions and 66 deletions

View File

@@ -5,7 +5,7 @@ is a Redis client library for Carp.
## Installation ## Installation
```clojure ```clojure
(load "https://veitheller.de/git/carpentry/redis.git@master") (load "https://git.veitheller.de/carpentry/redis.git@master")
``` ```
## Usage ## Usage

View File

@@ -4,10 +4,10 @@
(match (Redis.open "127.0.0.1") (match (Redis.open "127.0.0.1")
(Result.Success r) (Result.Success r)
(do (do
(Redis.send &r "PING" []) (Redis.send &r @"PING" &[])
(println* &(Redis.read &r)) (println* &(Redis.read &r))
(Redis.send &r "PING" [(Box (to-redis @"hiiiii"))]) (Redis.send &r @"PING" &[(to-redis @"hiiiii")])
(println* &(Redis.read &r)) (println* &(Redis.read &r))
(println* &(Redis.echo &r @"hi")) (println* &(Redis.echo &r @"hi"))

View File

@@ -5,97 +5,104 @@
(Str [String]) (Str [String])
(Err [String]) (Err [String])
(Integer [Int]) (Integer [Int])
(Arr [(Array (Box RESP))]) ;(Arr [(Array &RESP)])
(Arr [(Array String)])
) )
(defmodule RESP (defmodule RESP
(use-all Array Maybe Pattern Result) (use-all Array Maybe Pattern Result)
(hidden c)
(private c)
(def c (prn &@&[@""]))
(defn str [r] (defn str [r]
(match @r (match @r
(Null) @"$-1\r\n" (Null) @"$-1\r\n"
(Str s) (fmt "$%d\r\n%s\r\n" (String.length &s) &s) (Str s) (fmt "$%d\r\n%s\r\n" (String.length &s) &s)
(Err s) (fmt "-%s\r\n" &s) (Err s) (fmt "-%s\r\n" &s)
(Integer i) (fmt ":%d\r\n" i) (Integer i) (fmt ":%d\r\n" i)
(Arr a) (Arr a) (fmt "*%d\r\n%s" (Array.length &a) &(String.concat &a))))
(fmt "*%d\r\n%s"
(Array.length &a)
&(String.concat &(Array.copy-map &(fn [b] (str (unbox b))) &a)))))
(hidden decode-bulk-string) (hidden decode-bulk-string)
(private decode-bulk-string) (private decode-bulk-string)
(defn decode-bulk-string [s] (defn decode-bulk-string [s]
(if (starts-with? s "-1\r\n") (if (starts-with? s "-1\r\n")
(Success (Pair 4 (Null))) (Success (Null))
(let [splt (split #"\r\n" s) (let [splt (split #"\r\n" s)
l (unsafe-first &splt)] l (unsafe-first &splt)]
(match (from-string l) (match (from-string l)
(Nothing) (Error @"Error decoding bulk string: does not start with length!") (Nothing) (Error @"Error decoding bulk string: does not start with length!")
(Just il) (Just il)
(Success (Success (Str
(Pair (+ il 3) (String.prefix &(join "\r\n" &(suffix &splt 1)) il)))))))
(Str
(String.prefix &(join "\r\n" &(suffix &splt 1)) il))))))))
(private from-string-) (hidden agg)
(hidden from-string-) (private agg)
(sig from-string- (Fn [&String] (Result (Pair Int RESP) String))) (defn agg [els len]
(defn from-string- [s] (Error @"dummy")) (let-do [consumed 0
clen 0]
(for [i 0 len]
(let [el (unsafe-nth els i)]
(if (>= clen len)
(break)
(do
(set! consumed (inc consumed))
(set! clen (+ 2 (+ clen (String.length el))))))))
consumed))
(hidden decode-arr) (hidden decode-arr)
(private decode-arr) (private decode-arr)
(defn decode-arr [is] (defn decode-arr [s]
(if (starts-with? &is "0\r\n") (if (starts-with? s "*0\r\n")
(Success (Pair 4 (Arr []))) (Success (Null))
(let [splt (split #"\r\n" &(chomp &is)) (let [splt (split #"\r\n" &(chomp s))
sl (unsafe-first &splt) sl (unsafe-first &splt)]
s (join "\r\n" &(suffix &splt 1))]
(match (from-string sl) (match (from-string sl)
(Nothing) (Nothing)
(Error @"Error decoding array: does not start with length!") (Error @"Error decoding array: does not start with length!")
(Just l) (Just l)
(let-do [a (Array.allocate l) (let-do [a (Array.allocate l)
consumed 0 idx 0
err @""] err ""]
(for [i 0 (- l 1)] (for [i 0 (- (length &splt) 1)]
(match (from-string- &s) ; TODO: have nested structures
(Error msg) (let-do [el (unsafe-nth &splt (+ i 1))]
(do (case (head el)
(set! err msg) \$
(break)) (match (from-string &(tail el))
(Success p) (Maybe.Nothing)
(do (aset-uninitialized! &a idx @"")
(+= consumed @(Pair.a &p)) (Maybe.Just il)
(aset-uninitialized! &a i (Box @(Pair.b &p))) (let-do [rest (suffix &splt (+ i 2))]
(set! s (String.suffix &s @(Pair.a &p)))))) (aset-uninitialized! &a idx (String.prefix &(join "\r\n" &rest) il))
(if (= &err "") (set! i (+ i (agg &rest il)))))
(Success (Pair consumed (Arr a))) \*
(Error err))))))) (do
(set! err "TODO: cannot deal with nested arrays")
(defn from-string- [s] (break))
(if (empty? s) (aset-uninitialized! &a idx (chomp el)))
(Success (Pair 0 (Null))) (set! idx (inc idx))))
(case (head s) (if (= err "")
\+ (Success (Arr a))
(let [f (unsafe-first &(split #"\r\n" &(tail s)))] (Error @err)))))))
(Success (Pair (String.length f) (Str @f))))
\-
(let [f (unsafe-first &(split #"\r\n" &(tail s)))]
(Success (Pair (String.length f) (Err @f))))
\:
(let [f (unsafe-first &(split #"\r\n" &(tail s)))]
(match (from-string f)
(Maybe.Nothing)
(Error @"Could not parse integer in result.")
(Maybe.Just i)
(Success (Pair (String.length f) (Integer i)))))
\$ (decode-bulk-string &(tail s))
\* (decode-arr (tail s))
(Error (fmt "Malformed RESP data: got %s" s)))))
(doc from-string "converts a RESP string into a `RESP` data structure.") (doc from-string "converts a RESP string into a `RESP` data structure.")
(defn from-string [s] (defn from-string [s]
(Result.map (from-string- s) &(fn [p] @(Pair.b &p)))) (if (empty? s)
(Success (Null))
(case (head s)
\+ (Success (Str @(unsafe-first &(split #"\r\n" &(tail s)))))
\- (Success (Err @(unsafe-first &(split #"\r\n" &(tail s)))))
\:
(match (from-string (unsafe-first &(split #"\r\n" &(tail s))))
(Maybe.Nothing)
(Error @"Could not parse integer in result.")
(Maybe.Just i)
(Success (Integer i)))
\$ (decode-bulk-string &(tail s))
\* (decode-arr &(tail s))
(Error (fmt "Malformed RESP data: got %s" s)))))
(definterface to-redis (Fn [a] RESP)) (definterface to-redis (Fn [a] RESP))
) )
@@ -133,9 +140,9 @@ For variable port numbers please check out [`open-on`](#open-on).")
(defn read [r] (RESP.from-string &(Socket.read (sock r)))) (defn read [r] (RESP.from-string &(Socket.read (sock r))))
(doc send "sends the command `cmd` with the arguments `args` to Redis.") (doc send "sends the command `cmd` with the arguments `args` to Redis.")
(defn send [r cmd args] (defn send [r cmd args]
(if (empty? &args) (if (empty? args)
(Socket.send (sock r) &(fmt "%s\r\n" cmd)) (Socket.send (sock r) &(fmt "%s\r\n" &cmd))
(Socket.send (sock r) &(str &(RESP.Arr (concat &[[(Box (to-redis @cmd))] args])))))) (Socket.send (sock r) &(str &(RESP.Arr (concat &[[(str &(to-redis cmd))] (copy-map &RESP.str args)]))))))
(doc close "closes the connection to Redis.") (doc close "closes the connection to Redis.")
(defn close [r] (Socket.close @(sock &r))) (defn close [r] (Socket.close @(sock &r)))
@@ -150,7 +157,7 @@ For variable port numbers please check out [`open-on`](#open-on).")
(defndynamic rconv- [args] (defndynamic rconv- [args]
(if (= (length args) 0) (if (= (length args) 0)
(array) (array)
(cons (list 'Box (list 'to-redis (car args))) (rconv- (cdr args))))) (cons (list 'to-redis (car args)) (rconv- (cdr args)))))
(defmacro defredis [cmd :rest args] (defmacro defredis [cmd :rest args]
(eval (eval
@@ -163,7 +170,7 @@ It takes the same arguments as the [Redis command](https://redis.io/commands/"
])) ]))
(list 'defn cmd (collect-into (cons 'r args) array) (list 'defn cmd (collect-into (cons 'r args) array)
(list 'do (list 'do
(list 'Redis.send 'r (rtreat- (Symbol.str cmd)) (rconv- args)) (list 'Redis.send 'r (list 'copy (rtreat- (Symbol.str cmd))) (list 'ref (rconv- args)))
'(Redis.read r)))))) '(Redis.read r))))))
; these commands were scraped from redis.io on the 9th of Feb 2020 ; these commands were scraped from redis.io on the 9th of Feb 2020