1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! The RESP2 and RESP3 codec.
//!
//! Requests come in as ranges into the connection's own read buffer, and
//! replies go out as the bytes that go on the socket. Nothing in between is
//! materialised, because everything in between is what the lineage's profiles
//! kept finding at the top.
//!
//! # Reading
//!
//! [`Argv`] decodes commands. It is per connection, it remembers where it got
//! to when a command arrives in pieces, and after the first few commands it
//! stops allocating. Multibulk and inline requests both land in the same place,
//! so the command layer never learns which one a client used.
//!
//! ```
//! use yo_resp::{Argv, Limits, Step};
//!
//! let buf = b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n";
//! let mut argv = Argv::new();
//! match argv.decode(buf, &Limits::default())? {
//! Step::Command { consumed } => {
//! assert_eq!(consumed, buf.len());
//! assert_eq!(argv.arg(buf, 0), Some(&b"SET"[..]));
//! assert_eq!(argv.arg(buf, 2), Some(&b"v"[..]));
//! }
//! Step::Incomplete => unreachable!("the whole command is here"),
//! }
//! # Ok::<(), yo_resp::ProtocolError>(())
//! ```
//!
//! # Writing
//!
//! [`Out`] is the reply buffer, and it knows which protocol the connection is
//! speaking. A command writes the richer form once and the RESP2 downgrade
//! happens here rather than in the command:
//!
//! ```
//! use yo_resp::{Out, Proto};
//!
//! fn hgetall(out: &mut Out) {
//! out.map(1);
//! out.bulk(b"field");
//! out.bulk(b"value");
//! }
//!
//! let mut two = Out::new(Proto::Resp2);
//! hgetall(&mut two);
//! assert_eq!(two.as_slice(), b"*2\r\n$5\r\nfield\r\n$5\r\nvalue\r\n");
//!
//! let mut three = Out::new(Proto::Resp3);
//! hgetall(&mut three);
//! assert_eq!(three.as_slice(), b"%1\r\n$5\r\nfield\r\n$5\r\nvalue\r\n");
//! ```
//!
//! # Reading replies
//!
//! [`frame`] decodes a reply into a borrowed [`Frame`]. The server has no use
//! for it. The replication client, the differential harness and this crate's
//! own round trip tests do.
//!
//! # Running a command
//!
//! [`dispatch`] is the layer above both halves. It looks a command name up,
//! checks its arity, and calls the same `yo-kv` method the embedded API calls,
//! which is the placement rule Y23 is about: one implementation of `INCR`, two
//! ways to reach it.
//!
//! ```
//! use yo_resp::{Argv, Limits, Out, Proto};
//! use yo_resp::dispatch::{Args, Server, Session, execute};
//!
//! let mut server = Server::new();
//! let mut session = Session::new(1);
//! let mut out = Out::new(Proto::Resp2);
//! let wire = b"*1\r\n$4\r\nPING\r\n";
//! let mut argv = Argv::new();
//! argv.decode(wire, &Limits::default())?;
//! execute(&mut server, &mut session, Args::new(&argv, wire), &mut out);
//! assert_eq!(out.as_slice(), b"+PONG\r\n");
//! # Ok::<(), yo_resp::ProtocolError>(())
//! ```
//!
//! # Driving it from the loop
//!
//! [`engine`] is the piece between the two: connections, read buffers, framing
//! and one write per connection per batch, put on `yo_reactor::Engine` so the
//! loop can run commands without knowing what a command is. It is where a
//! server becomes possible, and it works over anything that implements
//! [`engine::Sink`], which is a socket in production and a `Vec` in a test.
//!
//! ```
//! use yo_reactor::Reactor;
//! use yo_resp::engine::{Recorder, Wire, pump};
//!
//! let mut r = Reactor::inline(Wire::new(Recorder::new()));
//! let conn = r.engine_mut().accept();
//!
//! r.engine_mut().feed(conn, b"*1\r\n$4\r\nPING\r\n");
//! pump(&mut r, &mut Vec::new());
//! assert_eq!(r.engine().sink().sent(conn), b"+PONG\r\n");
//! ```
//!
//! # What is not here
//!
//! Sockets. This crate turns bytes into arguments, runs them, turns values into
//! bytes, and says which connection they belong to. Reading and writing the
//! bytes themselves is the ring's job, and `04` section 7 owns the ring.
pub use ;
pub use ProtocolError;
pub use Frame;
pub use ;
pub use Out;
pub use ;
/// Redis's own integer and double text, shared with the string type.
///
/// This module lives in `yo-common` because the codec is not the only thing
/// that needs it: whether a string is stored int encoded is decided by the same
/// `string2ll` rules that decide whether a bulk length parses. Re-exported here
/// so that `yo_resp::num` keeps meaning what it meant.
pub use num;