chunkedge_command/
parsers.rs

1//! A collection of parses for use in command argument nodes.
2pub mod angle;
3pub mod block_pos;
4pub mod bool;
5pub mod color;
6pub mod column_pos;
7pub mod entity_anchor;
8pub mod entity_selector;
9pub mod gamemode;
10pub mod inventory_slot;
11pub mod numbers;
12pub mod rotation;
13pub mod score_holder;
14pub mod strings;
15pub mod swizzle;
16pub mod time;
17pub mod vec2;
18pub mod vec3;
19
20use std::ops::Add;
21
22pub use block_pos::BlockPos;
23pub(crate) use chunkedge_server::protocol::packets::play::commands_s2c::Parser;
24pub use column_pos::ColumnPos;
25pub use entity_anchor::EntityAnchor;
26pub use entity_selector::EntitySelector;
27pub use inventory_slot::InventorySlot;
28pub use rotation::Rotation;
29pub use score_holder::ScoreHolder;
30pub use strings::{GreedyString, QuotableString};
31pub use swizzle::Swizzle;
32use thiserror::Error;
33pub use time::Time;
34pub use vec2::Vec2;
35pub use vec3::Vec3;
36
37pub trait CommandArg: Sized {
38    fn arg_from_str(string: &str) -> Result<Self, CommandArgParseError> {
39        Self::parse_arg(&mut ParseInput::new(string))
40    }
41
42    fn parse_arg<'a>(input: &mut ParseInput<'a>) -> Result<Self, CommandArgParseError>;
43    /// what will the client be sent
44    fn display() -> Parser<'static>;
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ParseInput<'a>(&'a str);
49
50impl<'a> ParseInput<'a> {
51    fn advance(&mut self) {
52        self.advance_n_chars(1);
53    }
54
55    fn advance_n_chars(&mut self, n: usize) {
56        if self.is_done() {
57            return;
58        }
59        match self.0.char_indices().nth(n) {
60            Some((len, _)) => {
61                self.0 = &self.0[len..];
62            }
63            None => {
64                self.0 = &self.0[self.0.len()..];
65            }
66        }
67    }
68
69    fn advance_n_bytes(&mut self, n: usize) {
70        if self.is_done() {
71            return;
72        }
73        self.0 = &self.0[n..];
74    }
75    pub fn new(input: &'a str) -> Self {
76        ParseInput(input)
77    }
78
79    /// Returns the next character without advancing the input
80    pub fn peek(&self) -> Option<char> {
81        self.0.chars().next()
82    }
83
84    /// Returns the next n characters without advancing the input
85    pub fn peek_n(&self, n: usize) -> &'a str {
86        self.0
87            .char_indices()
88            .nth(n)
89            .map_or(self.0, |(idx, _)| &self.0[..idx])
90    }
91
92    /// Returns the next word without advancing the input
93    pub fn peek_word(&self) -> &'a str {
94        self.0
95            .char_indices()
96            .find(|(_, c)| c.is_whitespace())
97            .map_or(self.0, |(idx, _)| &self.0[..idx])
98    }
99
100    /// Checks if the input is empty
101    pub fn is_done(&self) -> bool {
102        self.0.is_empty()
103    }
104
105    /// Returns the next character and advances the input
106    pub fn pop(&mut self) -> Option<char> {
107        let c = self.peek()?;
108        self.advance();
109        Some(c)
110    }
111
112    /// Returns the next n characters and advances the input
113    pub fn pop_n(&mut self, n: usize) -> &str {
114        let s = self.peek_n(n);
115        self.advance_n_bytes(s.len());
116        s
117    }
118
119    /// Returns the next word and advances the input
120    pub fn pop_word(&mut self) -> &str {
121        let s = self.peek_word();
122        self.advance_n_bytes(s.len());
123        s
124    }
125
126    /// Returns the rest of the input and advances the input
127    pub fn pop_all(&mut self) -> Option<&str> {
128        let s = self.0;
129        self.advance_n_bytes(self.0.len());
130        Some(s)
131    }
132
133    /// Returns the next word and advances the input
134    pub fn pop_to_next(&mut self, c: char) -> Option<&str> {
135        let pos = self.0.find(c)?;
136        let s = &self.0[..pos];
137        self.advance_n_bytes(pos);
138        Some(s)
139    }
140
141    /// Matches the case-insensitive string and advances the input if it matches
142    pub fn match_next(&mut self, string: &str) -> bool {
143        if self
144            .0
145            .to_lowercase()
146            .starts_with(string.to_lowercase().as_str())
147        {
148            self.advance_n_bytes(string.len());
149            true
150        } else {
151            false
152        }
153    }
154
155    /// Skip all whitespace at the front of the input
156    pub fn skip_whitespace(&mut self) {
157        while let Some(c) = self.peek() {
158            if c.is_whitespace() {
159                self.advance();
160            } else {
161                break;
162            }
163        }
164    }
165
166    /// Set the inner string
167    pub fn into_inner(self) -> &'a str {
168        self.0
169    }
170
171    #[allow(clippy::len_without_is_empty)]
172    pub fn len(&self) -> usize {
173        self.0.len()
174    }
175}
176
177#[derive(Debug, Error)]
178pub enum CommandArgParseError {
179    // these should be player facing and not disclose internal information
180    #[error("invalid argument, expected {expected} got {got}")] // e.g. "integer" number
181    InvalidArgument { expected: String, got: String },
182    #[error("invalid argument length")]
183    InvalidArgLength,
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub enum AbsoluteOrRelative<T> {
188    Absolute(T),
189    Relative(T), // current value + T
190}
191
192impl<T> AbsoluteOrRelative<T>
193where
194    T: Add<Output = T> + Copy,
195{
196    pub fn get(&self, original: T) -> T {
197        match self {
198            Self::Absolute(num) => *num,
199            Self::Relative(num) => *num + original,
200        }
201    }
202}
203
204impl<T> CommandArg for AbsoluteOrRelative<T>
205where
206    T: CommandArg + Default,
207{
208    fn parse_arg<'a>(input: &mut ParseInput<'a>) -> Result<Self, CommandArgParseError> {
209        input.skip_whitespace();
210        if input.peek() == Some('~') {
211            input.advance();
212            if input.peek() == Some(' ') || input.peek().is_none() {
213                Ok(AbsoluteOrRelative::Relative(T::default()))
214            } else {
215                Ok(AbsoluteOrRelative::Relative(T::parse_arg(input)?))
216            }
217        } else if input.peek() == Some(' ') || input.peek().is_none() {
218            Err(CommandArgParseError::InvalidArgLength)
219        } else {
220            Ok(AbsoluteOrRelative::Absolute(T::parse_arg(input)?))
221        }
222    }
223
224    fn display() -> Parser<'static> {
225        T::display()
226    }
227}
228
229impl<T: Default> Default for AbsoluteOrRelative<T> {
230    fn default() -> Self {
231        AbsoluteOrRelative::Absolute(T::default())
232    }
233}
234
235#[cfg(test)]
236mod test {
237    use super::*;
238    #[test]
239    fn test_parse_input() {
240        let mut input = ParseInput::new("The QuIck brown FOX jumps over the lazy dog");
241        assert_eq!(input.peek(), Some('T'));
242        assert_eq!(input.peek_n(0), "");
243        assert_eq!(input.peek_n(1), "T");
244        assert_eq!(input.peek_n(2), "Th");
245        assert_eq!(input.peek_n(3), "The");
246
247        assert_eq!(input.peek_word(), "The");
248        input.pop_word();
249        input.skip_whitespace();
250        assert_eq!(input.peek_word(), "QuIck");
251
252        assert!(input.match_next("quick"));
253        input.pop();
254        assert_eq!(input.peek_word(), "brown");
255
256        assert!(input.match_next("brown fox"));
257        assert_eq!(input.pop_all(), Some(" jumps over the lazy dog"));
258    }
259    #[test]
260    fn test_absolute_or_relative() {
261        let mut input = ParseInput::new("~");
262        assert_eq!(
263            AbsoluteOrRelative::<i32>::parse_arg(&mut input).unwrap(),
264            AbsoluteOrRelative::Relative(0)
265        );
266        assert!(input.is_done());
267
268        let mut input = ParseInput::new("~1");
269        assert_eq!(
270            AbsoluteOrRelative::<i32>::parse_arg(&mut input).unwrap(),
271            AbsoluteOrRelative::Relative(1)
272        );
273        assert!(input.is_done());
274
275        let mut input = ParseInput::new("~1.5");
276        assert_eq!(
277            AbsoluteOrRelative::<f32>::parse_arg(&mut input).unwrap(),
278            AbsoluteOrRelative::Relative(1.5)
279        );
280        assert!(input.is_done());
281
282        let mut input = ParseInput::new("1");
283        assert_eq!(
284            AbsoluteOrRelative::<i32>::parse_arg(&mut input).unwrap(),
285            AbsoluteOrRelative::Absolute(1)
286        );
287        assert!(input.is_done());
288
289        let mut input = ParseInput::new("1.5 ");
290        assert_eq!(
291            AbsoluteOrRelative::<f32>::parse_arg(&mut input).unwrap(),
292            AbsoluteOrRelative::Absolute(1.5)
293        );
294        assert!(!input.is_done());
295
296        let mut input = ParseInput::new("1.5 2");
297        assert_eq!(
298            AbsoluteOrRelative::<f32>::parse_arg(&mut input).unwrap(),
299            AbsoluteOrRelative::Absolute(1.5)
300        );
301        assert!(!input.is_done());
302        assert_eq!(
303            AbsoluteOrRelative::<f32>::parse_arg(&mut input).unwrap(),
304            AbsoluteOrRelative::Absolute(2.0)
305        );
306        assert!(input.is_done());
307    }
308}