Skip to main content

fast_cache/commands/
pexpire.rs

1//! PEXPIRE command parsing and execution.
2
3use crate::Result;
4use crate::protocol::{FastCommand, Frame};
5#[cfg(feature = "server")]
6use crate::server::commands::{BorrowedCommandContext, DirectCommandContext};
7#[cfg(feature = "server")]
8use crate::server::commands::{RawCommandContext, RawDirectCommand};
9#[cfg(feature = "server")]
10use crate::server::wire::ServerWire;
11use crate::storage::{Command, EngineCommandContext, EngineFrameFuture};
12
13use super::DecodedFastCommand;
14use super::expire::{Expire, relative_expire_at_ms};
15use super::parsing::{CommandArity, TtlMillis};
16
17pub(crate) struct PExpire;
18pub(crate) static COMMAND: PExpire = PExpire;
19
20#[derive(Debug, Clone)]
21pub(crate) struct OwnedPExpire {
22    key: Vec<u8>,
23    ttl_ms: u64,
24}
25
26impl OwnedPExpire {
27    fn new(key: Vec<u8>, ttl_ms: u64) -> Self {
28        Self { key, ttl_ms }
29    }
30}
31
32impl super::OwnedCommandData for OwnedPExpire {
33    type Spec = PExpire;
34
35    fn route_key(&self) -> Option<&[u8]> {
36        Some(&self.key)
37    }
38
39    fn to_borrowed_command(&self) -> super::BorrowedCommandBox<'_> {
40        Box::new(BorrowedPExpire::new(&self.key, self.ttl_ms))
41    }
42}
43
44#[derive(Debug, Clone, Copy)]
45pub(crate) struct BorrowedPExpire<'a> {
46    key: &'a [u8],
47    ttl_ms: u64,
48}
49
50impl<'a> BorrowedPExpire<'a> {
51    fn new(key: &'a [u8], ttl_ms: u64) -> Self {
52        Self { key, ttl_ms }
53    }
54}
55
56impl<'a> super::BorrowedCommandData<'a> for BorrowedPExpire<'a> {
57    type Spec = PExpire;
58
59    fn route_key(&self) -> Option<&'a [u8]> {
60        Some(self.key)
61    }
62
63    fn to_owned_command(&self) -> Command {
64        Command::new(Box::new(OwnedPExpire::new(self.key.to_vec(), self.ttl_ms)))
65    }
66
67    fn execute_engine<'b>(&'b self, ctx: EngineCommandContext<'b>) -> EngineFrameFuture<'b>
68    where
69        'a: 'b,
70    {
71        let key = self.key;
72        let expire_at_ms = relative_expire_at_ms(self.ttl_ms);
73        Box::pin(async move {
74            Expire::execute_engine_integer(ctx, key, expire_at_ms)
75                .await
76                .map(Frame::Integer)
77        })
78    }
79
80    #[cfg(feature = "server")]
81    fn execute_borrowed_frame(&self, store: &crate::storage::EmbeddedStore, _now_ms: u64) -> Frame {
82        Frame::Integer(store.expire(self.key, relative_expire_at_ms(self.ttl_ms)) as i64)
83    }
84
85    #[cfg(feature = "server")]
86    fn execute_borrowed(&self, ctx: BorrowedCommandContext<'_, '_, '_>) {
87        let changed = ctx
88            .store
89            .expire(self.key, relative_expire_at_ms(self.ttl_ms));
90        ServerWire::write_resp_integer(ctx.out, changed as i64);
91    }
92
93    #[cfg(feature = "server")]
94    fn execute_direct_borrowed(&self, ctx: DirectCommandContext) -> Frame {
95        Frame::Integer(ctx.expire_at(self.key, ctx.now_ms.saturating_add(self.ttl_ms)) as i64)
96    }
97}
98
99impl super::CommandSpec for PExpire {
100    const NAME: &'static str = "PEXPIRE";
101    const MUTATES_VALUE: bool = true;
102}
103
104impl super::OwnedCommandParse for PExpire {
105    fn parse_owned(parts: &[Vec<u8>]) -> Result<Command> {
106        CommandArity::<Self>::exact(parts.len(), 3)?;
107        Ok(Command::new(Box::new(OwnedPExpire::new(
108            parts[1].clone(),
109            TtlMillis::<Self>::millis(&parts[2])?,
110        ))))
111    }
112}
113
114impl<'a> super::BorrowedCommandParse<'a> for PExpire {
115    fn parse_borrowed(parts: &[&'a [u8]]) -> Result<super::BorrowedCommandBox<'a>> {
116        CommandArity::<Self>::exact(parts.len(), 3)?;
117        Ok(Box::new(BorrowedPExpire::new(
118            parts[1],
119            TtlMillis::<Self>::millis(parts[2])?,
120        )))
121    }
122}
123
124impl DecodedFastCommand for PExpire {
125    fn matches_decoded_fast(&self, _command: &FastCommand<'_>) -> bool {
126        false
127    }
128}
129
130#[cfg(feature = "server")]
131impl RawDirectCommand for PExpire {
132    fn execute(&self, ctx: RawCommandContext<'_, '_, '_>) {
133        match ctx.args.as_slice() {
134            [key, ttl] => match TtlMillis::<()>::ascii_millis(ttl) {
135                Some(ttl_ms) => {
136                    let changed = ctx.store.expire(key, relative_expire_at_ms(ttl_ms));
137                    ServerWire::write_resp_integer(ctx.out, changed as i64);
138                }
139                None => ServerWire::write_resp_error(ctx.out, "ERR value is not an integer"),
140            },
141            _ => ServerWire::write_resp_error(
142                ctx.out,
143                "ERR wrong number of arguments for 'PEXPIRE' command",
144            ),
145        }
146    }
147}