1pub mod del;
2pub mod exists;
3pub mod expire;
4pub mod get;
5pub mod getex;
6pub(crate) mod parsing;
7pub mod persist;
8pub mod pexpire;
9pub mod psetex;
10pub mod pttl;
11pub mod set;
12pub mod setex;
13pub mod ttl;
14
15use bytes::Bytes as SharedBytes;
16
17use crate::protocol::{CommandSpanFrame, FastCommand, FastRequest, FastResponse, Frame};
18use crate::storage::{
19 EngineCommandContext, EngineFastFuture, EngineFrameFuture, EngineRespSpanFuture,
20};
21use crate::{FastCacheError, Result};
22
23pub(crate) trait CommandSpec {
24 const NAME: &'static str;
25 const MUTATES_VALUE: bool;
26
27 #[inline(always)]
28 fn matches(name: &[u8]) -> bool {
29 name.eq_ignore_ascii_case(Self::NAME.as_bytes())
30 }
31}
32
33pub(crate) trait OwnedCommandParse: CommandSpec {
34 fn parse_owned(parts: &[Vec<u8>]) -> Result<crate::storage::Command>;
35}
36
37pub(crate) type OwnedCommandBox = Box<dyn OwnedCommandObject>;
38
39pub(crate) trait OwnedCommandData: std::fmt::Debug + Send + Sync {
45 type Spec: CommandSpec;
46
47 fn route_key(&self) -> Option<&[u8]>;
48 fn to_borrowed_command(&self) -> BorrowedCommandBox<'_>;
49}
50
51pub(crate) trait OwnedCommandObject: std::fmt::Debug + Send + Sync {
53 fn name(&self) -> &'static str;
54 fn mutates_value(&self) -> bool;
55 fn route_key(&self) -> Option<&[u8]>;
56 fn to_borrowed_command(&self) -> BorrowedCommandBox<'_>;
57}
58
59impl<T> OwnedCommandObject for T
60where
61 T: OwnedCommandData,
62{
63 fn name(&self) -> &'static str {
64 <T::Spec as CommandSpec>::NAME
65 }
66
67 fn mutates_value(&self) -> bool {
68 <T::Spec as CommandSpec>::MUTATES_VALUE
69 }
70
71 fn route_key(&self) -> Option<&[u8]> {
72 <T as OwnedCommandData>::route_key(self)
73 }
74
75 fn to_borrowed_command(&self) -> BorrowedCommandBox<'_> {
76 <T as OwnedCommandData>::to_borrowed_command(self)
77 }
78}
79
80pub(crate) type BorrowedCommandBox<'a> = Box<dyn BorrowedCommandObject<'a> + 'a>;
81
82pub(crate) trait BorrowedCommandData<'a>: std::fmt::Debug + Send + Sync {
87 type Spec: CommandSpec;
88
89 fn route_key(&self) -> Option<&'a [u8]>;
90 fn supports_spanned_resp(&self) -> bool {
91 false
92 }
93 fn to_owned_command(&self) -> crate::storage::Command;
94 fn execute_engine<'b>(&'b self, ctx: EngineCommandContext<'b>) -> EngineFrameFuture<'b>
95 where
96 'a: 'b;
97
98 #[cfg(feature = "server")]
99 fn execute_borrowed_frame(&self, store: &crate::storage::EmbeddedStore, now_ms: u64) -> Frame;
100
101 #[cfg(feature = "server")]
102 fn execute_borrowed(&self, ctx: crate::server::commands::BorrowedCommandContext<'_, '_, '_>);
103
104 #[cfg(feature = "server")]
105 fn execute_direct_borrowed(&self, ctx: crate::server::commands::DirectCommandContext) -> Frame;
106}
107
108pub(crate) trait BorrowedCommandObject<'a>: std::fmt::Debug + Send + Sync {
110 fn name(&self) -> &'static str;
111 fn mutates_value(&self) -> bool;
112 fn route_key(&self) -> Option<&'a [u8]>;
113 fn supports_spanned_resp(&self) -> bool;
114 fn to_owned_command(&self) -> crate::storage::Command;
115 fn execute_engine<'b>(&'b self, ctx: EngineCommandContext<'b>) -> EngineFrameFuture<'b>
116 where
117 'a: 'b;
118
119 #[cfg(feature = "server")]
120 fn execute_borrowed_frame(&self, store: &crate::storage::EmbeddedStore, now_ms: u64) -> Frame;
121
122 #[cfg(feature = "server")]
123 fn execute_borrowed(&self, ctx: crate::server::commands::BorrowedCommandContext<'_, '_, '_>);
124
125 #[cfg(feature = "server")]
126 fn execute_direct_borrowed(&self, ctx: crate::server::commands::DirectCommandContext) -> Frame;
127}
128
129impl<'a, T> BorrowedCommandObject<'a> for T
130where
131 T: BorrowedCommandData<'a>,
132{
133 fn name(&self) -> &'static str {
134 <T::Spec as CommandSpec>::NAME
135 }
136
137 fn mutates_value(&self) -> bool {
138 <T::Spec as CommandSpec>::MUTATES_VALUE
139 }
140
141 fn route_key(&self) -> Option<&'a [u8]> {
142 <T as BorrowedCommandData<'a>>::route_key(self)
143 }
144
145 fn supports_spanned_resp(&self) -> bool {
146 <T as BorrowedCommandData<'a>>::supports_spanned_resp(self)
147 }
148
149 fn to_owned_command(&self) -> crate::storage::Command {
150 <T as BorrowedCommandData<'a>>::to_owned_command(self)
151 }
152
153 fn execute_engine<'b>(&'b self, ctx: EngineCommandContext<'b>) -> EngineFrameFuture<'b>
154 where
155 'a: 'b,
156 {
157 <T as BorrowedCommandData<'a>>::execute_engine(self, ctx)
158 }
159
160 #[cfg(feature = "server")]
161 fn execute_borrowed_frame(&self, store: &crate::storage::EmbeddedStore, now_ms: u64) -> Frame {
162 <T as BorrowedCommandData<'a>>::execute_borrowed_frame(self, store, now_ms)
163 }
164
165 #[cfg(feature = "server")]
166 fn execute_borrowed(&self, ctx: crate::server::commands::BorrowedCommandContext<'_, '_, '_>) {
167 <T as BorrowedCommandData<'a>>::execute_borrowed(self, ctx);
168 }
169
170 #[cfg(feature = "server")]
171 fn execute_direct_borrowed(&self, ctx: crate::server::commands::DirectCommandContext) -> Frame {
172 <T as BorrowedCommandData<'a>>::execute_direct_borrowed(self, ctx)
173 }
174}
175
176pub(crate) trait BorrowedCommandParse<'a>: CommandSpec {
177 fn parse_borrowed(parts: &[&'a [u8]]) -> Result<BorrowedCommandBox<'a>>;
178}
179
180pub(crate) trait CommandMetadata: Sync {
182 fn mutates_value(&self) -> bool;
183 fn matches(&self, name: &[u8]) -> bool;
184}
185
186impl<T> CommandMetadata for T
187where
188 T: CommandSpec + Sync,
189{
190 fn mutates_value(&self) -> bool {
191 T::MUTATES_VALUE
192 }
193
194 #[inline(always)]
195 fn matches(&self, name: &[u8]) -> bool {
196 <T as CommandSpec>::matches(name)
197 }
198}
199
200pub(crate) trait CommandDefinition: CommandMetadata {
202 fn parse_owned(&self, parts: &[Vec<u8>]) -> Result<crate::storage::Command>;
203 fn parse_borrowed<'a>(&self, parts: &[&'a [u8]]) -> Result<BorrowedCommandBox<'a>>;
204}
205
206impl<T> CommandDefinition for T
207where
208 T: CommandSpec + OwnedCommandParse + Sync,
209 for<'a> T: BorrowedCommandParse<'a>,
210{
211 fn parse_owned(&self, parts: &[Vec<u8>]) -> Result<crate::storage::Command> {
212 <T as OwnedCommandParse>::parse_owned(parts)
213 }
214
215 fn parse_borrowed<'a>(&self, parts: &[&'a [u8]]) -> Result<BorrowedCommandBox<'a>> {
216 <T as BorrowedCommandParse<'a>>::parse_borrowed(parts)
217 }
218}
219
220pub(crate) trait DecodedFastCommand: CommandMetadata {
221 fn matches_decoded_fast(&self, command: &FastCommand<'_>) -> bool;
222}
223
224pub(crate) trait EngineCommandDispatch: DecodedFastCommand {
225 fn execute_engine_fast<'a>(
226 &'static self,
227 ctx: EngineCommandContext<'a>,
228 request: FastRequest<'a>,
229 ) -> EngineFastFuture<'a>;
230}
231
232pub(crate) trait EngineRespSpanCommandDispatch: CommandMetadata {
233 fn execute_engine_resp_spanned<'a>(
234 &'static self,
235 ctx: EngineCommandContext<'a>,
236 frame: CommandSpanFrame,
237 owner: SharedBytes,
238 out: &'a mut Vec<u8>,
239 ) -> EngineRespSpanFuture<'a>;
240}
241
242pub(crate) static CATALOG: &[&dyn CommandDefinition] = &[
243 &get::COMMAND,
244 &set::COMMAND,
245 &del::COMMAND,
246 &exists::COMMAND,
247 &ttl::COMMAND,
248 &pttl::COMMAND,
249 &expire::COMMAND,
250 &pexpire::COMMAND,
251 &persist::COMMAND,
252 &getex::COMMAND,
253 &setex::COMMAND,
254 &psetex::COMMAND,
255];
256
257pub(crate) struct CommandCatalog;
258
259impl CommandCatalog {
260 pub(crate) fn find(name: &[u8]) -> Option<&'static dyn CommandDefinition> {
261 CATALOG
262 .iter()
263 .copied()
264 .find(|command| command.matches(name))
265 }
266
267 pub(crate) fn parse_owned(parts: &[Vec<u8>]) -> Result<crate::storage::Command> {
268 let command = Self::find_required(parts.first().map(Vec::as_slice))?;
269 command.parse_owned(parts)
270 }
271
272 pub(crate) fn parse_borrowed<'a>(parts: &[&'a [u8]]) -> Result<BorrowedCommandBox<'a>> {
273 let command = Self::find_required(parts.first().copied())?;
274 command.parse_borrowed(parts)
275 }
276
277 fn find_required(name: Option<&[u8]>) -> Result<&'static dyn CommandDefinition> {
278 match name {
279 Some(name) => Self::find(name).ok_or_else(|| {
280 FastCacheError::Command(format!(
281 "unsupported command: {}",
282 String::from_utf8_lossy(name)
283 ))
284 }),
285 None => Err(FastCacheError::Command("empty command".into())),
286 }
287 }
288}
289
290pub(crate) struct EngineCommandCatalog;
291
292impl EngineCommandCatalog {
293 fn find_fast(command: &FastCommand<'_>) -> Option<&'static dyn EngineCommandDispatch> {
294 [
295 &get::COMMAND as &dyn EngineCommandDispatch,
296 &set::COMMAND,
297 &del::COMMAND,
298 &exists::COMMAND,
299 &ttl::COMMAND,
300 &expire::COMMAND,
301 &getex::COMMAND,
302 &setex::COMMAND,
303 ]
304 .into_iter()
305 .find(|candidate| candidate.matches_decoded_fast(command))
306 }
307
308 fn find_resp_span(name: &[u8]) -> Option<&'static dyn EngineRespSpanCommandDispatch> {
309 [&set::COMMAND as &dyn EngineRespSpanCommandDispatch]
310 .into_iter()
311 .find(|candidate| candidate.matches(name))
312 }
313
314 pub(crate) async fn execute_fast<'a>(
315 ctx: EngineCommandContext<'a>,
316 request: FastRequest<'a>,
317 ) -> Option<Result<FastResponse>> {
318 let handler = Self::find_fast(&request.command)?;
319 Some(handler.execute_engine_fast(ctx, request).await)
320 }
321
322 pub(crate) async fn execute_resp_spanned<'a>(
323 ctx: EngineCommandContext<'a>,
324 frame: CommandSpanFrame,
325 owner: SharedBytes,
326 out: &'a mut Vec<u8>,
327 ) -> Option<Result<()>> {
328 let name = &owner[frame.parts.first()?.clone()];
329 let handler = Self::find_resp_span(name)?;
330 Some(
331 handler
332 .execute_engine_resp_spanned(ctx, frame, owner, out)
333 .await,
334 )
335 }
336}