Skip to main content

feox_server/protocol/command/
mod.rs

1use crate::protocol::resp::RespValue;
2use bytes::Bytes;
3
4mod client;
5mod command_table;
6mod executor;
7mod hash;
8mod list;
9mod parser;
10
11#[allow(unused_imports)]
12pub use command_table::{CommandDef, COMMAND_TABLE};
13pub use executor::CommandExecutor;
14
15#[derive(Debug, Clone)]
16pub enum Command {
17    // Basic commands
18    Get(Vec<u8>),
19    Set {
20        key: Vec<u8>,
21        value: Bytes,
22        ex: Option<u64>,
23        px: Option<u64>,
24    },
25    Del(Vec<Vec<u8>>),
26    Exists(Vec<Vec<u8>>),
27
28    // Atomic operations
29    Incr(Vec<u8>),
30    IncrBy {
31        key: Vec<u8>,
32        delta: i64,
33    },
34    Decr(Vec<u8>),
35    DecrBy {
36        key: Vec<u8>,
37        delta: i64,
38    },
39
40    // TTL commands
41    Expire {
42        key: Vec<u8>,
43        seconds: u64,
44    },
45    PExpire {
46        key: Vec<u8>,
47        milliseconds: u64,
48    },
49    Ttl(Vec<u8>),
50    PTtl(Vec<u8>),
51    Persist(Vec<u8>),
52
53    // Bulk operations
54    MGet(Vec<Vec<u8>>),
55    MSet(Vec<(Vec<u8>, Bytes)>),
56
57    // Server commands
58    Ping(Option<Bytes>),
59    Echo(Bytes),
60    Info(Option<String>),
61    Config {
62        action: String,
63        args: Vec<Bytes>,
64    },
65    Command {
66        subcommand: Option<String>,
67        args: Vec<Vec<u8>>,
68    },
69    Quit,
70    FlushDb,
71
72    // Key scanning
73    Keys(String), // Pattern
74    Scan {
75        cursor: Vec<u8>,
76        count: usize,
77        pattern: Option<String>,
78    },
79
80    // FeOx-specific
81    JsonPatch {
82        key: Vec<u8>,
83        patch: Bytes,
84    },
85    Cas {
86        key: Vec<u8>,
87        expected: Bytes,
88        new_value: Bytes,
89    },
90
91    // Authentication
92    Auth(Vec<u8>),
93
94    // List commands
95    LPush {
96        key: Vec<u8>,
97        values: Vec<Bytes>,
98    },
99    RPush {
100        key: Vec<u8>,
101        values: Vec<Bytes>,
102    },
103    LPop {
104        key: Vec<u8>,
105        count: Option<usize>,
106    },
107    RPop {
108        key: Vec<u8>,
109        count: Option<usize>,
110    },
111    LLen(Vec<u8>),
112    LRange {
113        key: Vec<u8>,
114        start: i64,
115        stop: i64,
116    },
117    LIndex {
118        key: Vec<u8>,
119        index: i64,
120    },
121
122    Subscribe(Vec<Vec<u8>>),
123    Unsubscribe(Option<Vec<Vec<u8>>>),
124    PSubscribe(Vec<Vec<u8>>),
125    PUnsubscribe(Option<Vec<Vec<u8>>>),
126    Publish {
127        channel: Vec<u8>,
128        message: Vec<u8>,
129    },
130    PubSub {
131        subcommand: String,
132        args: Vec<Vec<u8>>,
133    },
134
135    // Client management
136    Client {
137        subcommand: String,
138        args: Vec<Vec<u8>>,
139    },
140
141    // Transaction commands
142    Multi,
143    Exec,
144    Discard,
145    Watch(Vec<Vec<u8>>),
146    Unwatch,
147
148    // Hash commands
149    HSet {
150        key: Vec<u8>,
151        fields: Vec<(Vec<u8>, Bytes)>,
152    },
153    HGet {
154        key: Vec<u8>,
155        field: Vec<u8>,
156    },
157    HMGet {
158        key: Vec<u8>,
159        fields: Vec<Vec<u8>>,
160    },
161    HDel {
162        key: Vec<u8>,
163        fields: Vec<Vec<u8>>,
164    },
165    HExists {
166        key: Vec<u8>,
167        field: Vec<u8>,
168    },
169    HGetAll(Vec<u8>),
170    HLen(Vec<u8>),
171    HKeys(Vec<u8>),
172    HVals(Vec<u8>),
173    HIncrBy {
174        key: Vec<u8>,
175        field: Vec<u8>,
176        delta: i64,
177    },
178}
179
180impl Command {
181    /// Parse command from RESP array
182    #[inline(always)]
183    pub fn from_resp(value: RespValue) -> Result<Self, String> {
184        parser::parse_command(value)
185    }
186
187    /// Check if this is a pub/sub command
188    pub fn is_pubsub_command(&self) -> bool {
189        matches!(
190            self,
191            Command::Subscribe(_)
192                | Command::Unsubscribe(_)
193                | Command::PSubscribe(_)
194                | Command::PUnsubscribe(_)
195                | Command::Publish { .. }
196                | Command::PubSub { .. }
197        )
198    }
199
200    /// Check if this command is allowed in pub/sub mode
201    pub fn is_allowed_in_pubsub_mode(&self) -> bool {
202        matches!(
203            self,
204            Command::Subscribe(_)
205                | Command::Unsubscribe(_)
206                | Command::PSubscribe(_)
207                | Command::PUnsubscribe(_)
208                | Command::Ping(_)
209                | Command::Quit
210        )
211    }
212
213    /// Convert to PubSubOp if this is a pub/sub command
214    pub fn to_pubsub_op(self) -> Option<crate::network::PubSubOp> {
215        match self {
216            Command::Subscribe(channels) => Some(crate::network::PubSubOp::Subscribe(channels)),
217            Command::Unsubscribe(channels) => Some(crate::network::PubSubOp::Unsubscribe(channels)),
218            Command::PSubscribe(patterns) => Some(crate::network::PubSubOp::PSubscribe(patterns)),
219            Command::PUnsubscribe(patterns) => {
220                Some(crate::network::PubSubOp::PUnsubscribe(patterns))
221            }
222            Command::Publish { channel, message } => {
223                Some(crate::network::PubSubOp::Publish { channel, message })
224            }
225            Command::PubSub { subcommand, args } => match subcommand.to_uppercase().as_str() {
226                "CHANNELS" => Some(crate::network::PubSubOp::PubSubChannels {
227                    pattern: args.first().cloned(),
228                }),
229                "NUMSUB" => Some(crate::network::PubSubOp::PubSubNumSub { channels: args }),
230                "NUMPAT" => Some(crate::network::PubSubOp::PubSubNumPat),
231                _ => None,
232            },
233            _ => None,
234        }
235    }
236}