feox_server/protocol/command/
mod.rs

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