Skip to main content

fast_cache/commands/
del.rs

1//! DEL command parsing and execution.
2//!
3//! DEL-specific behavior starts here. Transport-specific implementations live
4//! in `del/*.rs` submodules.
5
6mod engine;
7#[cfg(feature = "server")]
8mod fcnp;
9#[cfg(feature = "server")]
10mod server;
11
12use smallvec::SmallVec;
13
14use crate::Result;
15use crate::protocol::{FastCommand, Frame};
16#[cfg(feature = "server")]
17use crate::server::commands::{BorrowedCommandContext, DirectCommandContext};
18use crate::storage::{Command, EngineCommandContext, EngineFrameFuture};
19
20use super::DecodedFastCommand;
21use super::parsing::CommandArity;
22
23type BorrowedKeys<'a> = SmallVec<[&'a [u8]; 8]>;
24
25/// Command spec registered in the command catalogs.
26pub(crate) struct Del;
27pub(crate) static COMMAND: Del = Del;
28
29/// Owned DEL payload used when a RESP frame has already been copied out.
30#[derive(Debug, Clone)]
31pub(crate) struct OwnedDel {
32    keys: Vec<Vec<u8>>,
33}
34
35impl OwnedDel {
36    fn new(keys: Vec<Vec<u8>>) -> Self {
37        Self { keys }
38    }
39}
40
41impl super::OwnedCommandData for OwnedDel {
42    type Spec = Del;
43
44    fn route_key(&self) -> Option<&[u8]> {
45        self.keys.first().map(Vec::as_slice)
46    }
47
48    fn to_borrowed_command(&self) -> super::BorrowedCommandBox<'_> {
49        Box::new(BorrowedDel::from_owned(&self.keys))
50    }
51}
52
53/// Borrowed DEL payload used on zero-copy request paths.
54#[derive(Debug, Clone)]
55pub(crate) struct BorrowedDel<'a> {
56    keys: BorrowedKeys<'a>,
57}
58
59impl<'a> BorrowedDel<'a> {
60    fn new(keys: impl IntoIterator<Item = &'a [u8]>) -> Self {
61        Self {
62            keys: keys.into_iter().collect(),
63        }
64    }
65
66    fn from_owned(keys: &'a [Vec<u8>]) -> Self {
67        Self::new(keys.iter().map(Vec::as_slice))
68    }
69}
70
71impl<'a> super::BorrowedCommandData<'a> for BorrowedDel<'a> {
72    type Spec = Del;
73
74    fn route_key(&self) -> Option<&'a [u8]> {
75        self.keys.first().copied()
76    }
77
78    fn to_owned_command(&self) -> Command {
79        Command::new(Box::new(OwnedDel::new(
80            self.keys.iter().map(|key| key.to_vec()).collect(),
81        )))
82    }
83
84    fn execute_engine<'b>(&'b self, ctx: EngineCommandContext<'b>) -> EngineFrameFuture<'b>
85    where
86        'a: 'b,
87    {
88        let keys = self.keys.clone();
89        Box::pin(async move { Del::execute_engine_frame(ctx, keys.as_slice()).await })
90    }
91
92    #[cfg(feature = "server")]
93    fn execute_borrowed_frame(&self, store: &crate::storage::EmbeddedStore, _now_ms: u64) -> Frame {
94        Frame::Integer(Del::delete_embedded_keys(store, self.keys.as_slice()))
95    }
96
97    #[cfg(feature = "server")]
98    fn execute_borrowed(&self, ctx: BorrowedCommandContext<'_, '_, '_>) {
99        Del::execute_borrowed(ctx, self.keys.as_slice());
100    }
101
102    #[cfg(feature = "server")]
103    fn execute_direct_borrowed(&self, ctx: DirectCommandContext) -> Frame {
104        Frame::Integer(self.keys.iter().filter(|key| ctx.delete(key)).count() as i64)
105    }
106}
107
108impl super::CommandSpec for Del {
109    const NAME: &'static str = "DEL";
110    const MUTATES_VALUE: bool = true;
111}
112
113impl super::OwnedCommandParse for Del {
114    fn parse_owned(parts: &[Vec<u8>]) -> Result<Command> {
115        CommandArity::<Self>::at_least(parts.len(), 2, "key")?;
116        Ok(Command::new(Box::new(OwnedDel::new(parts[1..].to_vec()))))
117    }
118}
119
120impl<'a> super::BorrowedCommandParse<'a> for Del {
121    fn parse_borrowed(parts: &[&'a [u8]]) -> Result<super::BorrowedCommandBox<'a>> {
122        CommandArity::<Self>::at_least(parts.len(), 2, "key")?;
123        Ok(Box::new(BorrowedDel::new(parts[1..].iter().copied())))
124    }
125}
126
127impl DecodedFastCommand for Del {
128    fn matches_decoded_fast(&self, command: &FastCommand<'_>) -> bool {
129        matches!(command, FastCommand::Delete { .. })
130    }
131}