Skip to main content

fast_cache/commands/
psetex.rs

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