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}