packages/redis/src/resp.nu — RESP (REdis Serialization Protocol, RESP2) codec, pure and I/O-free so it can be unit-tested without a server.
A request is an array of bulk strings: *<n>\r\n ( $<len>\r\n<arg>\r\n ) x n
A reply is one of: simple string (+), error (-), integer (:), bulk string ($, with $-1 = nil) or array (, with -1 = nil). Arrays nest arbitrarily (pub/sub, XRANGE, COMMAND, …), so a reply is decoded into a flat node arena — a Vec RNode where array nodes hold the pool indices of their children. One Vec plus one String per node: no nested Vecs in structs beyond the child-index list, and a single linear free.
( resp_encode args ) → ( Vec u ) request bytes ( resp_parse buf start ) → RespParse one reply (or Incomplete) ( resp_reply_free reply ) → v free a parsed reply
Node walking (power users): resp_reply_root / resp_node_kind / resp_node_int / resp_node_str / resp_node_is_nil / resp_node_arr_len / resp_node_arr_at
: RNode: RNode {
i kind
i ival // integer value; for arrays, the element count
String sval // string / error text (empty otherwise)
( Vec i ) kids // child node indices (only populated for arrays)
}
RNode.kind: 0 nil · 1 integer · 2 string (simple or bulk) · 3 error · 4 array
: RedisReply: RedisReply {
( Vec RNode ) nodes
i root
}
A decoded reply: the node arena plus the index of the root node.
: RespParse: RespParse {
i status
RedisReply reply
i consumed
}
Result of one parse attempt over a byte buffer. status: 0 ok · 1 incomplete (need more bytes) · 2 malformed consumed: bytes consumed from start when status == 0
: RespBuilder: RespBuilder {
( Vec RNode ) nodes
i status
i cur
}
Mutable parse state threaded through the recursive descent (heap so the recursion shares one cursor / status / arena).
@ resp_encode ( Vec String ) args → ( Vec u )Encode a command — an array of bulk strings — into RESP request bytes.
@ resp_parse ( Vec u ) buf i start → RespParseParse one reply from buf starting at start. On Incomplete (status 1) the returned reply still owns a (partial) arena and must be freed by the caller before retrying with more bytes.
@ resp_reply_free RedisReply r → v@ resp_reply_root RedisReply r → i@ resp_node_kind RedisReply r i node → i@ resp_node_int RedisReply r i node → i@ resp_node_str RedisReply r i node → sBorrowed view of a string/error node's text (lifetime tied to r).
@ resp_node_is_nil RedisReply r i node → b@ resp_node_arr_len RedisReply r i node → i@ resp_node_arr_at RedisReply r i node i k → ipackages/redis/src/redis.nu — a pure-NURL Redis client. Speaks RESP2 over a plain libc TCP socket, or over the pure-NURL TLS client (rediss://) with no OpenSSL on the target. The RESP wire format lives in resp.nu; this file owns the connection, the request/reply round-trip and an ergonomic command surface (GET / SET / INCR / lists / hashes / sets / pub-sub …).
( redis_connect host port ) → !RedisConn RedisErr ( redis_connect_tls host port sni verify ) → !RedisConn RedisErr ( redis_auth conn user password ) → !v RedisErr ( redis_command conn args ) → !RedisReply RedisErr (raw) ( redis_get conn key ) / ( redis_set … ) … → typed helpers ( redis_close conn ) → v
Build arbitrary commands with the arg builder: : ( Vec String ) a ( redis_args ) ( redis_arg a SET ) ( redis_arg a key ) ( redis_arg a val ) : !RedisReply RedisErr r ( redis_command c a ) ( redis_args_free a )
: | RedisErr: | RedisErr {
RedisConnFail // could not open the TCP socket
RedisTls // TLS upgrade failed (handshake / cert error)
RedisProtocol // malformed RESP from the server
RedisIo // socket read/write failure or unexpected EOF
RedisServerError // server returned a RESP error — see conn.lasterr
RedisType // reply was not the type the helper expected
}
@ redis_err_name RedisErr e → s: RedisStr: RedisStr {
i found
String val
}
Optional bulk-string reply. found == 0 means the server answered nil (e.g. GET on a missing key); val is owned and empty in that case. A named struct rather than ?String so it round-trips through !T E.
@ redis_str_is_nil RedisStr s → b@ redis_str_val RedisStr s → s@ redis_str_free RedisStr s → v: RedisMessage: RedisMessage {
i kind
String channel
String pattern
String payload
i count
}
A pub/sub frame. kind: 0 message · 1 subscribe · 2 unsubscribe · 3 pmessage · 4 psubscribe · 5 punsubscribe · -1 unrecognised. pattern is set only for the p* variants; count is the live subscription count carried by (un)subscribe confirmations.
@ redis_message_kind RedisMessage m → i@ redis_message_channel RedisMessage m → s@ redis_message_pattern RedisMessage m → s@ redis_message_payload RedisMessage m → s@ redis_message_count RedisMessage m → i@ redis_message_is_payload RedisMessage m → bTrue for an actual published message (message / pmessage), false for a subscribe/unsubscribe control frame.
@ redis_message_free RedisMessage m → v: RedisConn: RedisConn {
i tls // 0 plaintext, 1 TLS
i raw // raw socket fd (plaintext reads / fd the TLS layer took over)
TcpConn tcp // plaintext transport (writes via tcp_write_all)
* TlsConn tc // TLS transport (when tls = 1)
( Vec u ) rxbuf // bytes read but not yet parsed into a reply
String lasterr // last server error text
String host_name
i port_num
i db_index
}
@ redis_command *RedisConn c ( Vec String ) args → !RedisReply RedisErrSend a command (array of bulk strings) and return its reply. A RESP error reply is surfaced as RedisServerError with the text in conn.lasterr. The caller still owns args (free with redis_args_free) and owns the returned reply (free with resp_reply_free).
@ redis_args → ( Vec String )@ redis_arg ( Vec String ) v s a → v@ redis_arg_i ( Vec String ) v i n → v@ redis_args_free ( Vec String ) v → v@ redis_connect s host i port → !*RedisConn RedisErr@ redis_connect_tls s host i port s server_name i verify → !*RedisConn RedisErrverify != 0 → verify-full (chain + hostname); verify == 0 → encrypt only.
@ redis_close *RedisConn c → v@ redis_last_error *RedisConn c → s@ redis_is_tls *RedisConn c → b@ redis_auth *RedisConn c s user s password → !v RedisErrAUTH: one-arg (password only) or two-arg (ACL user + password). Pass an empty user for the legacy single-argument form.
@ redis_select *RedisConn c i db → !v RedisErr@ redis_ping *RedisConn c → !b RedisErr@ redis_echo *RedisConn c s msg → !RedisStr RedisErr@ redis_set *RedisConn c s key s val → !v RedisErr@ redis_get *RedisConn c s key → !RedisStr RedisErr@ redis_del *RedisConn c s key → !i RedisErr@ redis_exists *RedisConn c s key → !b RedisErr@ redis_incr *RedisConn c s key → !i RedisErr@ redis_decr *RedisConn c s key → !i RedisErr@ redis_incrby *RedisConn c s key i n → !i RedisErr@ redis_expire *RedisConn c s key i secs → !b RedisErr@ redis_ttl *RedisConn c s key → !i RedisErr@ redis_keys *RedisConn c s pattern → !( Vec String ) RedisErr@ redis_lpush *RedisConn c s key s val → !i RedisErr@ redis_rpush *RedisConn c s key s val → !i RedisErr@ redis_llen *RedisConn c s key → !i RedisErr@ redis_lrange *RedisConn c s key i start i stop → !( Vec String ) RedisErr@ redis_hset *RedisConn c s key s field s val → !i RedisErr@ redis_hget *RedisConn c s key s field → !RedisStr RedisErr@ redis_hgetall *RedisConn c s key → !( Vec String ) RedisErrFlat [field, value, field, value, …] of the whole hash.
@ redis_sadd *RedisConn c s key s member → !i RedisErr@ redis_smembers *RedisConn c s key → !( Vec String ) RedisErr@ redis_publish *RedisConn c s channel s msg → !i RedisErr@ redis_subscribe *RedisConn c s channel → !RedisMessage RedisErrSubscribe to a channel (or pattern). Returns the subscription confirmation; thereafter call redis_next_message to receive published messages. While subscribed only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING are valid commands.
@ redis_unsubscribe *RedisConn c s channel → !RedisMessage RedisErr@ redis_psubscribe *RedisConn c s pattern → !RedisMessage RedisErr@ redis_punsubscribe *RedisConn c s pattern → !RedisMessage RedisErr@ redis_next_message *RedisConn c → !RedisMessage RedisErrBlock until the next pub/sub frame arrives, then decode it. Returns a message / pmessage for delivered payloads, or a (un)subscribe control frame; RedisIo on a closed connection.
@ redis_flushdb *RedisConn c → !v RedisErrpackages/redis/src/main.nu — the redis command: a small redis-cli-style client built on the pure-NURL Redis client. Connects over RESP2 with an optional pure-NURL TLS upgrade (rediss://, no OpenSSL on the target), authenticates with AUTH, runs one command with -c or starts a REPL.
redis [flags] ["redis://[user:pass@]host:port/db"] -h HOST host (default $REDIS_HOST or 127.0.0.1) -p PORT port (default $REDIS_PORT or 6379) -a PASSWORD AUTH password (default $REDIS_PASSWORD) --user USER ACL username for AUTH (Redis 6+) -n DB SELECT database index (default 0) --tls connect over TLS (verify-full) --insecure with --tls / rediss://, skip certificate verification -c "CMD ARGS" run one command and exit; otherwise start a REPL A rediss:// URL implies --tls.
& c @ isatty i32 fd → i32: ConnInfo: ConnInfo {
String host
i port
String user
String password
i db
i tlsmode // 0 plain, 1 tls insecure, 2 tls verify-full
}
@ main → i