Skip to main content

why2_chat/
command.rs

1/*
2This is part of WHY2
3Copyright (C) 2022-2026 Václav Šmejkal
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use std::fmt::
20{
21    Display,
22    Formatter,
23    Result,
24};
25
26use crate::network::MessageCode;
27
28use std::net::TcpStream;
29
30use crate::
31{
32    options,
33    network::{ self, MessagePacket },
34};
35
36//ENUMS
37#[derive(Clone, PartialEq)]
38pub enum Command
39{
40    Exit,           //DISCONNECT FROM SERVER
41    Voice,          //ENABLE VOICE CHAT
42    Mute,           //TOGGLE-MUTE USER/YOURSELF
43    Channel,        //SWITCH CHANNEL
44    Help,           //PRINT COMMANDS
45    Info,           //COMMAND INFO
46    List,           //LIST USERS
47    Files,          //LIST FILES
48    Upload,         //UPLOAD FILE TO SERVER
49    Download,       //DOWNLOAD FILE FROM SERVER
50    PrivateMessage, //ONE TO ONE MESSAGE
51    UsernameColor,  //SET COLOR OF USERNAME
52    MessageColor,   //SET COLOR OF MESSAGE
53    Invalid,        //INVALID COMMAND
54}
55
56//STRUCTS
57pub struct CommandArg //COMMAND PARAMETER
58{
59    pub name: &'static str,
60    pub required: bool,
61}
62
63pub struct CommandInfo //COMMAND INFO
64{
65    pub command: Command,
66    pub triggers: &'static [&'static str],
67    pub shortcut: Option<char>,
68    pub args: &'static [CommandArg],
69    pub description: &'static str,
70}
71
72pub const COMMAND_LIST: &[CommandInfo] =
73&[
74    CommandInfo
75    {
76        command: Command::Help,
77        triggers: &[ "HELP", "H", "COMMANDS", "USAGE", "GUIDE" ],
78        shortcut: Some('h'),
79        args: &[],
80        description: "Prints all available commands",
81    },
82
83    CommandInfo
84    {
85        command: Command::Info,
86        triggers: &[ "INFO", "COMMAND", "MAN" ],
87        shortcut: None,
88        args: &[CommandArg { name: "COMMAND", required: true }],
89        description: "Shows command info",
90    },
91
92    CommandInfo
93    {
94        command: Command::Voice,
95        triggers: &[ "VOICE", "VOIP", "CALL" ],
96        shortcut: None,
97        args: &[],
98        description: "Toggles voice chat",
99    },
100
101    CommandInfo
102    {
103        command: Command::Mute,
104        triggers: &[ "MUTE", "UNMUTE", "SILENCE", "STFU" ],
105        shortcut: Some('s'),
106        args: &[CommandArg { name: "ID", required: false }],
107        description: "Toggle-mutes user/yourself",
108    },
109
110    CommandInfo
111    {
112        command: Command::Channel,
113        triggers: &[ "CHANNEL", "SWITCH", "CHECKOUT", "AREA" ],
114        shortcut: None,
115        args: &[CommandArg { name: "NAME", required: false }],
116        description: "Switches to channel/lobby if NAME is omitted",
117    },
118
119    CommandInfo
120    {
121        command: Command::Upload,
122        triggers: &[ "UPLOAD", "FILEUP", "PUSH", "UP" ],
123        shortcut: None,
124        args: &[CommandArg { name: "PATH", required: true }],
125        description: "Uploads file to server",
126    },
127
128    CommandInfo
129    {
130        command: Command::Download,
131        triggers: &[ "DOWNLOAD", "FILEDOWN", "PULL", "DOWN", "FETCH" ],
132        shortcut: None,
133        args:
134        &[
135            CommandArg { name: "USER ID", required: true },
136            CommandArg { name: "FILE ID", required: true },
137        ],
138        description: "Downloads file from server",
139    },
140
141    CommandInfo
142    {
143        command: Command::List,
144        triggers: &[ "LIST", "USERS", "CLIENTS", "CHANNELS", "IDS", "ID" ],
145        shortcut: Some('l'),
146        args: &[],
147        description: "Shows connected users and their IDs",
148    },
149
150    CommandInfo
151    {
152        command: Command::Files,
153        triggers: &[ "FILES", "LISTFILES", "UPLOADS", "DOWNLOADS", "SHARES" ],
154        shortcut: Some('u'),
155        args: &[],
156        description: "Shows available files and their IDs",
157    },
158
159    CommandInfo
160    {
161        command: Command::PrivateMessage,
162        triggers: &[ "PM", "DM", "MSG", "TELL" ],
163        shortcut: None,
164        args:
165        &[
166            CommandArg { name: "ID", required: true },
167            CommandArg { name: "MESSAGE", required: true },
168        ],
169        description: "Sends private message",
170    },
171
172    CommandInfo
173    {
174        command: Command::UsernameColor,
175        triggers: &[ "UCOLOR", "USERNAME" ],
176        shortcut: None,
177        args: &[CommandArg { name: "COLOR", required: true }],
178        description: "Sets color of username",
179    },
180
181    CommandInfo
182    {
183        command: Command::MessageColor,
184        triggers: &[ "COLOR", "MESSAGE" ],
185        shortcut: None,
186        args: &[CommandArg { name: "COLOR", required: true }],
187        description: "Sets color of message",
188    },
189
190    CommandInfo
191    {
192        command: Command::Exit,
193        triggers: &[ "EXIT", "LEAVE", "QUIT", "DISCONNECT" ],
194        shortcut: Some('c'),
195        args: &[],
196        description: "Disconnects from the server",
197    },
198];
199
200//CONSTS
201pub const COMMAND_PREFIX: &str = "/"; //PREFIX FOR COMMANDS
202
203//IMPLEMENTATIONS
204impl Command
205{
206    //GET CODE MATCHING TO COMMAND
207    pub fn to_code(&self) -> Option<MessageCode>
208    {
209        match self
210        {
211            Command::Exit => Some(MessageCode::Disconnect),
212            Command::Voice => Some(MessageCode::Voice),
213            Command::Channel => Some(MessageCode::Channel),
214            Command::List => Some(MessageCode::List),
215            Command::PrivateMessage => Some(MessageCode::PrivateMessage),
216            Command::Upload => Some(MessageCode::Upload),
217            Command::Download => Some(MessageCode::Download),
218            Command::Files => Some(MessageCode::Files),
219
220            _ => None,
221        }
222    }
223}
224
225impl Display for Command
226{
227    //Command TO STRING
228    fn fmt(&self, f: &mut Formatter<'_>) -> Result
229    {
230        let name = COMMAND_LIST.iter()
231            .find(|info| info.command == *self)
232            .map(|info| info.triggers[0].to_lowercase())
233            .unwrap_or_default(); //HANDLE INVALID
234
235        write!(f, "{}{}", COMMAND_PREFIX, name)
236    }
237}
238
239pub fn get_command(input: &str) -> (Option<Command>, Option<String>) //GET COMMAND + PARAMETERS FROM STRING
240{
241    //input DOESN'T START WITH PREFIX, NO COMMAND
242    if !input.starts_with(COMMAND_PREFIX) { return (None, None); }
243
244    //SPLIT input TO COMMAND AND PARAMETERS
245    let no_prefix = &input[COMMAND_PREFIX.len()..]; //EXTRACT COMMAND WITHOUT PREFIX (IN UPPERCASE)
246    let (command, parameters) = match no_prefix.split_once(' ') //EXTRACT POSSIBLE PARAMETERS
247    {
248        Some((command, parameters)) => (command.to_ascii_uppercase(), Some(parameters.trim().to_string())),
249        None => (no_prefix.to_ascii_uppercase(), None)
250    };
251
252    //SEARCH FOR COMMAND
253    for info in COMMAND_LIST
254    {
255        if info.triggers.contains(&command.as_str())
256        {
257            return (Some(info.command.clone()), parameters);
258        }
259    }
260
261    (Some(Command::Invalid), None)
262}
263
264pub fn send_command_code(stream: &mut TcpStream, command: &Command, parameters: &Option<String>) -> bool //SEND CODE FROM COMMAND IF POSSIBLE
265{
266    //CODE COMMAND
267    if let Some(code) = command.to_code() && command != &Command::Upload
268    {
269        network::send(stream, MessagePacket
270        {
271            text: parameters.clone(),
272            code: Some(code),
273            ..Default::default()
274        }, options::get_keys().as_ref());
275        true
276    } else { false }
277}