Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed02b8aadb |
@@ -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
|
||||||
|
@@ -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"))
|
||||||
|
131
redis.carp
131
redis.carp
@@ -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)
|
|
||||||
(do
|
|
||||||
(+= consumed @(Pair.a &p))
|
|
||||||
(aset-uninitialized! &a i (Box @(Pair.b &p)))
|
|
||||||
(set! s (String.suffix &s @(Pair.a &p))))))
|
|
||||||
(if (= &err "")
|
|
||||||
(Success (Pair consumed (Arr a)))
|
|
||||||
(Error err)))))))
|
|
||||||
|
|
||||||
(defn from-string- [s]
|
|
||||||
(if (empty? s)
|
|
||||||
(Success (Pair 0 (Null)))
|
|
||||||
(case (head s)
|
|
||||||
\+
|
|
||||||
(let [f (unsafe-first &(split #"\r\n" &(tail s)))]
|
|
||||||
(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)
|
(Maybe.Nothing)
|
||||||
(Error @"Could not parse integer in result.")
|
(aset-uninitialized! &a idx @"")
|
||||||
(Maybe.Just i)
|
(Maybe.Just il)
|
||||||
(Success (Pair (String.length f) (Integer i)))))
|
(let-do [rest (suffix &splt (+ i 2))]
|
||||||
\$ (decode-bulk-string &(tail s))
|
(aset-uninitialized! &a idx (String.prefix &(join "\r\n" &rest) il))
|
||||||
\* (decode-arr (tail s))
|
(set! i (+ i (agg &rest il)))))
|
||||||
(Error (fmt "Malformed RESP data: got %s" s)))))
|
\*
|
||||||
|
(do
|
||||||
|
(set! err "TODO: cannot deal with nested arrays")
|
||||||
|
(break))
|
||||||
|
(aset-uninitialized! &a idx (chomp el)))
|
||||||
|
(set! idx (inc idx))))
|
||||||
|
(if (= err "")
|
||||||
|
(Success (Arr a))
|
||||||
|
(Error @err)))))))
|
||||||
|
|
||||||
(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
|
||||||
|
Reference in New Issue
Block a user