twilight_command_parser/config.rs
1//! Configuration for a [`Parser`].
2//!
3//! Provided are methods for [adding commands][`add_command`] and
4//! [removing them][`remove_command`], as well as
5//! [adding prefixes][`add_prefix`] and [removing prefixes][`remove_prefix`].
6//! You can also [iterate over commands][`commands`] and [prefixes][`prefixes`].
7//!
8//! [`Parser`]: super::Parser
9//! [`add_command`]: CommandParserConfig::add_command
10//! [`add_prefix`]: CommandParserConfig::add_prefix
11//! [`commands`]: CommandParserConfig::commands
12//! [`prefixes`]: CommandParserConfig::prefixes
13//! [`remove_command`]: CommandParserConfig::remove_command
14//! [`remove_prefix`]: CommandParserConfig::remove_prefix
15
16use super::casing::CaseSensitivity;
17use std::borrow::Cow;
18use std::slice::{Iter, IterMut};
19
20/// Configuration for a [`Parser`].
21///
22/// [`Parser`]: crate::Parser
23#[derive(Clone, Debug, Default)]
24pub struct CommandParserConfig<'a> {
25 pub(crate) commands: Vec<CaseSensitivity>,
26 pub(crate) prefixes: Vec<Cow<'a, str>>,
27}
28
29impl<'a> CommandParserConfig<'a> {
30 /// Creates a fresh default configuration with no commands or prefixes.
31 pub const fn new() -> Self {
32 Self {
33 commands: Vec::new(),
34 prefixes: Vec::new(),
35 }
36 }
37
38 /// Returns an iterator of immutable references to the commands.
39 pub fn commands(&self) -> Commands<'_> {
40 Commands {
41 iter: self.commands.iter(),
42 }
43 }
44
45 /// Returns an iterator of mutable references to the commands.
46 ///
47 /// Use the [`add_command`] and [`remove_command`] methods for an easier way to
48 /// manage commands.
49 ///
50 /// [`add_command`]: Self::add_command
51 /// [`remove_command`]: Self::remove_command
52 pub fn commands_mut(&mut self) -> CommandsMut<'_> {
53 CommandsMut {
54 iter: self.commands.iter_mut(),
55 }
56 }
57
58 /// Returns an iterator of immutable references to the prefixes.
59 ///
60 /// Use the [`add_prefix`] and [`remove_prefix`] methods for an easier way
61 /// to manage prefixes.
62 ///
63 /// [`add_prefix`]: Self::add_prefix
64 /// [`remove_prefix`]: Self::remove_prefix
65 pub fn prefixes(&self) -> Prefixes<'_> {
66 Prefixes {
67 iter: self.prefixes.iter(),
68 }
69 }
70
71 /// Returns an iterator of mutable references to the prefixes.
72 pub fn prefixes_mut(&'a mut self) -> PrefixesMut<'a> {
73 PrefixesMut {
74 iter: self.prefixes.iter_mut(),
75 }
76 }
77
78 /// Add a command to the list of commands.
79 ///
80 /// # Examples
81 ///
82 /// Add a case-sensitive "ping" command:
83 ///
84 /// ```
85 /// use twilight_command_parser::CommandParserConfig;
86 ///
87 /// let mut config = CommandParserConfig::new();
88 /// config.add_command("ping", true);
89 /// assert_eq!(1, config.commands().len());
90 /// ```
91 pub fn add_command(&mut self, name: impl Into<String>, case_sensitive: bool) -> bool {
92 self._add_command(name.into(), case_sensitive)
93 }
94
95 fn _add_command(&mut self, name: String, case_sensitive: bool) -> bool {
96 let command = if case_sensitive {
97 CaseSensitivity::Sensitive(name)
98 } else {
99 CaseSensitivity::Insensitive(name.into())
100 };
101 if self.commands.contains(&command) {
102 false
103 } else {
104 self.commands.push(command);
105 true
106 }
107 }
108
109 /// Removes a command from the list of commands.
110 ///
111 /// Any commands that would match the command provided are removed.
112 ///
113 /// # Examples
114 ///
115 /// ```
116 /// use twilight_command_parser::CommandParserConfig;
117 ///
118 /// let mut config = CommandParserConfig::new();
119 /// config.add_command("ping", true);
120 /// config.add_command("PING", false);
121 /// assert_eq!(2, config.commands().len());
122 ///
123 /// // Now remove it and verify that there are no commands.
124 /// config.remove_command("ping");
125 /// assert_eq!(config.commands().len(), 0);
126 /// ```
127 pub fn remove_command(&mut self, command: impl AsRef<str>) {
128 self.commands.retain(|c| c != command.as_ref());
129 }
130
131 /// Adds a prefix to the list of prefixes.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// use twilight_command_parser::CommandParserConfig;
137 ///
138 /// let mut config = CommandParserConfig::new();
139 /// config.add_prefix("!");
140 /// assert_eq!(1, config.prefixes().len());
141 /// ```
142 pub fn add_prefix(&mut self, prefix: impl Into<Cow<'a, str>>) -> bool {
143 let prefix = prefix.into();
144 if self.prefixes.contains(&prefix) {
145 false
146 } else {
147 self.prefixes.push(prefix);
148 true
149 }
150 }
151
152 /// Removes a prefix from the list of prefixes.
153 ///
154 /// Returns whether a prefix with the name was removed.
155 ///
156 /// # Examples
157 ///
158 /// ```
159 /// use twilight_command_parser::CommandParserConfig;
160 ///
161 /// let mut config = CommandParserConfig::new();
162 /// config.add_prefix("!");
163 /// config.add_prefix("~");
164 /// assert_eq!(2, config.prefixes().len());
165 ///
166 /// // Now remove one and verify that there is only 1 prefix.
167 /// config.remove_prefix("!");
168 /// assert_eq!(1, config.prefixes().len());
169 /// ```
170 pub fn remove_prefix(&mut self, prefix: impl Into<Cow<'a, str>>) -> Option<Cow<'a, str>> {
171 let needle = prefix.into();
172 let pos = self.prefixes.iter().position(|e| *e == needle)?;
173 Some(self.prefixes.remove(pos))
174 }
175}
176
177/// Iterator over the parser configuration's immutably borrowed commands.
178pub struct Commands<'a> {
179 iter: Iter<'a, CaseSensitivity>,
180}
181
182impl<'a> Iterator for Commands<'a> {
183 type Item = (&'a str, bool);
184
185 fn next(&mut self) -> Option<Self::Item> {
186 self.iter
187 .next()
188 .map(|casing| (casing.as_ref(), casing.is_sensitive()))
189 }
190
191 fn size_hint(&self) -> (usize, Option<usize>) {
192 self.iter.size_hint()
193 }
194}
195
196impl<'a> ExactSizeIterator for Commands<'a> {}
197
198/// Iterator over the parser configuration's mutably borrowed commands.
199pub struct CommandsMut<'a> {
200 iter: IterMut<'a, CaseSensitivity>,
201}
202
203impl<'a> Iterator for CommandsMut<'a> {
204 type Item = (&'a mut str, bool);
205
206 fn next(&mut self) -> Option<Self::Item> {
207 let casing = self.iter.next()?;
208 let is_sensitive = casing.is_sensitive();
209
210 Some((casing.as_mut(), is_sensitive))
211 }
212
213 fn size_hint(&self) -> (usize, Option<usize>) {
214 self.iter.size_hint()
215 }
216}
217
218impl<'a> ExactSizeIterator for CommandsMut<'a> {}
219
220/// Iterator over the parser configuration's immutably borrowed prefixes.
221pub struct Prefixes<'a> {
222 iter: Iter<'a, Cow<'a, str>>,
223}
224
225impl<'a> Iterator for Prefixes<'a> {
226 type Item = &'a Cow<'a, str>;
227
228 fn next(&mut self) -> Option<Self::Item> {
229 self.iter.next()
230 }
231
232 fn size_hint(&self) -> (usize, Option<usize>) {
233 self.iter.size_hint()
234 }
235}
236
237impl<'a> ExactSizeIterator for Prefixes<'a> {}
238
239/// Iterator over the parser configuration's mutably borrowed prefixes.
240pub struct PrefixesMut<'a> {
241 iter: IterMut<'a, Cow<'a, str>>,
242}
243
244impl<'a> Iterator for PrefixesMut<'a> {
245 type Item = &'a mut Cow<'a, str>;
246
247 fn next(&mut self) -> Option<Self::Item> {
248 self.iter.next()
249 }
250
251 fn size_hint(&self) -> (usize, Option<usize>) {
252 self.iter.size_hint()
253 }
254}
255
256impl<'a> ExactSizeIterator for PrefixesMut<'a> {}
257
258#[cfg(test)]
259mod tests {
260 use super::{CommandParserConfig, Commands, CommandsMut, Prefixes, PrefixesMut};
261 use static_assertions::assert_impl_all;
262 use std::fmt::Debug;
263
264 assert_impl_all!(CommandParserConfig<'_>: Clone, Debug, Default, Send, Sync);
265 assert_impl_all!(CommandsMut<'_>: ExactSizeIterator, Iterator, Send, Sync);
266 assert_impl_all!(Commands<'_>: ExactSizeIterator, Iterator, Send, Sync);
267 assert_impl_all!(PrefixesMut<'_>: ExactSizeIterator, Iterator, Send, Sync);
268 assert_impl_all!(Prefixes<'_>: ExactSizeIterator, Iterator, Send, Sync);
269
270 #[test]
271 fn test_getters() {
272 let mut config = CommandParserConfig::new();
273 assert!(config.commands().len() == 0);
274 assert!(config.commands_mut().len() == 0);
275 assert!(config.prefixes().len() == 0);
276 assert!(config.prefixes_mut().len() == 0);
277 }
278}