Skip to main content

steel_core/command/execution/selector/
parser.rs

1use super::*;
2
3pub(crate) fn parse_entity_selector<S>(
4    reader: &mut StringReader<'_>,
5    source: &S,
6    single: bool,
7    players_only: bool,
8) -> Result<EntitySelector, CommandSyntaxError>
9where
10    S: CommandArgumentSource + ?Sized,
11{
12    let start = reader.checkpoint();
13    let raw = read_selector_argument(reader)?;
14    let allow_selectors = allow_selectors(source);
15    let allow_advanced = allow_advanced_selectors(source);
16    let selector = parse_selector_plan_with_permissions(&raw, allow_selectors, allow_advanced)
17        .map_err(|error| selector_syntax_error(reader, start, &raw, error))?;
18    selector
19        .validate_for_argument(single, players_only)
20        .map_err(|error| selector_syntax_error(reader, start, &raw, error))?;
21    Ok(selector)
22}
23
24pub(crate) fn parse_entity_selector_text(raw: &str) -> Result<EntitySelector, CommandSyntaxError> {
25    let mut command_reader = StringReader::new(raw);
26    if command_reader.peek() != Some('@') {
27        let name = command_reader.read_string()?;
28        if command_reader.can_read() {
29            return Err(
30                command_reader.error(CommandSyntaxErrorKind::Dynamic(Box::new(
31                    TextComponent::plain("unexpected trailing selector data"),
32                ))),
33            );
34        }
35        return parse_name_or_uuid_value(name).map_err(|error| {
36            let mut error_reader = StringReader::new(raw);
37            let start = error_reader.checkpoint();
38            selector_syntax_error(&mut error_reader, start, raw, error)
39        });
40    }
41
42    let mut reader = StringReader::new(raw);
43    let start = reader.checkpoint();
44    parse_selector_plan_with_permissions(raw, true, true)
45        .map_err(|error| selector_syntax_error(&mut reader, start, raw, error))
46}
47
48fn selector_syntax_error(
49    reader: &mut StringReader<'_>,
50    start: ReaderCursor,
51    raw: &str,
52    error: SelectorParseError,
53) -> CommandSyntaxError {
54    reader.restore(start);
55    let end = error.cursor.min(raw.len());
56    if raw.is_char_boundary(end) {
57        for _ in raw[..end].chars() {
58            reader.skip();
59        }
60    }
61    reader.error(CommandSyntaxErrorKind::Dynamic(Box::new(error.message())))
62}
63
64pub(super) fn allow_selectors<S>(source: &S) -> bool
65where
66    S: CommandArgumentSource + ?Sized,
67{
68    source.allows_entity_selectors()
69}
70
71pub(super) fn allow_advanced_selectors<S>(source: &S) -> bool
72where
73    S: CommandArgumentSource + ?Sized,
74{
75    source.allows_advanced_entity_selectors()
76}
77
78pub(super) fn read_selector_argument(
79    reader: &mut StringReader<'_>,
80) -> Result<String, CommandSyntaxError> {
81    if reader.peek() != Some('@') {
82        return reader.read_string();
83    }
84
85    let mut value = String::new();
86    let mut option_depth = 0usize;
87    let mut quote = None;
88    let mut escaped = false;
89    while let Some(ch) = reader.peek() {
90        if option_depth == 0 && !value.is_empty() && java::is_whitespace(ch) {
91            break;
92        }
93        value.push(ch);
94        reader.skip();
95        if escaped {
96            escaped = false;
97            continue;
98        }
99        if quote.is_some() {
100            if ch == '\\' {
101                escaped = true;
102            } else if quote == Some(ch) {
103                quote = None;
104            }
105            continue;
106        }
107        match ch {
108            '"' | '\'' => quote = Some(ch),
109            '[' => option_depth += 1,
110            ']' if option_depth > 0 => {
111                option_depth -= 1;
112                if option_depth == 0 {
113                    break;
114                }
115            }
116            _ => {}
117        }
118    }
119    if value.is_empty() {
120        return Err(reader.error(CommandSyntaxErrorKind::Dynamic(Box::new(
121            TextComponent::from(&translations::ARGUMENT_ENTITY_INVALID),
122        ))));
123    }
124    Ok(value)
125}
126
127fn is_valid_selector_name(name: &str) -> bool {
128    !name.is_empty() && name.encode_utf16().count() <= 16
129}
130
131#[cfg(test)]
132pub(super) fn parse_selector_plan(
133    raw: &str,
134    allow_selectors: bool,
135) -> Result<EntitySelector, SelectorParseError> {
136    parse_selector_plan_with_permissions(raw, allow_selectors, allow_selectors)
137}
138
139pub(super) fn parse_selector_plan_with_permissions(
140    raw: &str,
141    allow_selectors: bool,
142    allow_advanced_selectors: bool,
143) -> Result<EntitySelector, SelectorParseError> {
144    let mut reader = SelectorReader::new(raw);
145    if reader.peek() == Some('@') {
146        if !allow_selectors {
147            return Err(SelectorParseError::not_allowed(reader.cursor()));
148        }
149        reader.read();
150        parse_selector_type(&mut reader, allow_advanced_selectors)
151    } else {
152        parse_name_or_uuid(&mut reader)
153    }
154}
155
156fn parse_name_or_uuid(
157    reader: &mut SelectorReader<'_>,
158) -> Result<EntitySelector, SelectorParseError> {
159    let name = reader.read_remaining();
160    parse_name_or_uuid_value(name)
161}
162
163fn parse_name_or_uuid_value(name: String) -> Result<EntitySelector, SelectorParseError> {
164    if let Ok(uuid) = Uuid::parse_str(&name) {
165        return Ok(EntitySelector::new(
166            SelectorKind::EntityUuid(uuid),
167            1,
168            true,
169            false,
170            SelectorOrder::Arbitrary,
171        ));
172    }
173    if !is_valid_selector_name(&name) {
174        return Err(SelectorParseError::invalid_at(
175            "invalid player name or UUID",
176            0,
177        ));
178    }
179    Ok(EntitySelector::new(
180        SelectorKind::PlayerName(name),
181        1,
182        false,
183        false,
184        SelectorOrder::Arbitrary,
185    ))
186}
187
188fn parse_selector_type(
189    reader: &mut SelectorReader<'_>,
190    allow_advanced_selectors: bool,
191) -> Result<EntitySelector, SelectorParseError> {
192    let selector_start = reader.cursor();
193    let Some(selector_type) = reader.read() else {
194        return Err(SelectorParseError::invalid_at(
195            "missing selector type",
196            selector_start,
197        ));
198    };
199
200    let selector_type = match selector_type {
201        'a' => SelectorType::AllPlayers,
202        'e' => SelectorType::AllEntities,
203        'n' => SelectorType::NearestEntity,
204        'p' => SelectorType::NearestPlayer,
205        'r' => SelectorType::RandomPlayer,
206        's' => SelectorType::SelfEntity,
207        other => {
208            return Err(SelectorParseError::invalid_at(
209                format!("unknown selector type '@{other}'"),
210                selector_start,
211            ));
212        }
213    };
214    let mut selector = EntitySelector::for_selector_type(selector_type);
215
216    if reader.peek() == Some('[') {
217        reader.read();
218        parse_options(reader, &mut selector, allow_advanced_selectors)?;
219    }
220    if reader.can_read() {
221        return Err(SelectorParseError::invalid_at(
222            "unexpected trailing selector data",
223            reader.cursor(),
224        ));
225    }
226    Ok(selector)
227}
228
229fn parse_options(
230    reader: &mut SelectorReader<'_>,
231    selector: &mut EntitySelector,
232    allow_advanced_selectors: bool,
233) -> Result<(), SelectorParseError> {
234    let mut state = SelectorOptionState::default();
235    reader.skip_whitespace();
236    while reader.peek().is_some_and(|ch| ch != ']') {
237        if !allow_advanced_selectors {
238            return Err(SelectorParseError::advanced_not_allowed(reader.cursor()));
239        }
240        reader.skip_whitespace();
241        let key_cursor = reader.cursor();
242        let key = reader.read_key()?;
243        selector.uses_advanced_options = true;
244        reader.skip_whitespace();
245        reader.expect('=')?;
246        reader.skip_whitespace();
247        parse_option(reader, selector, &mut state, &key, key_cursor)?;
248        reader.skip_whitespace();
249        match reader.peek() {
250            Some(',') => {
251                reader.read();
252                reader.skip_whitespace();
253            }
254            Some(']') => break,
255            Some(_) => {
256                return Err(SelectorParseError::invalid_at(
257                    "expected ',' or ']' after selector option",
258                    reader.cursor(),
259                ));
260            }
261            None => {
262                return Err(SelectorParseError::invalid_at(
263                    "expected ']' to end selector options",
264                    reader.cursor(),
265                ));
266            }
267        }
268    }
269    reader.expect(']')?;
270    Ok(())
271}
272
273fn parse_option(
274    reader: &mut SelectorReader<'_>,
275    selector: &mut EntitySelector,
276    state: &mut SelectorOptionState,
277    key: &str,
278    key_cursor: usize,
279) -> Result<(), SelectorParseError> {
280    match key {
281        "name" => parse_name_option(reader, selector, state),
282        "distance" => parse_distance_option(reader, selector, state, key_cursor),
283        "level" => parse_level_option(reader, selector, state, key_cursor),
284        "x" => {
285            ensure_set_once(&mut state.x, "x", key_cursor)?;
286            selector.world_limited = true;
287            selector.position.x = Some(reader.read_f64()?);
288            Ok(())
289        }
290        "y" => {
291            ensure_set_once(&mut state.y, "y", key_cursor)?;
292            selector.world_limited = true;
293            selector.position.y = Some(reader.read_f64()?);
294            Ok(())
295        }
296        "z" => {
297            ensure_set_once(&mut state.z, "z", key_cursor)?;
298            selector.world_limited = true;
299            selector.position.z = Some(reader.read_f64()?);
300            Ok(())
301        }
302        "dx" => {
303            ensure_set_once(&mut state.dx, "dx", key_cursor)?;
304            selector.world_limited = true;
305            selector.delta.x = Some(reader.read_f64()?);
306            Ok(())
307        }
308        "dy" => {
309            ensure_set_once(&mut state.dy, "dy", key_cursor)?;
310            selector.world_limited = true;
311            selector.delta.y = Some(reader.read_f64()?);
312            Ok(())
313        }
314        "dz" => {
315            ensure_set_once(&mut state.dz, "dz", key_cursor)?;
316            selector.world_limited = true;
317            selector.delta.z = Some(reader.read_f64()?);
318            Ok(())
319        }
320        "x_rotation" => {
321            ensure_set_once(&mut state.x_rotation, "x_rotation", key_cursor)?;
322            let value_cursor = reader.cursor();
323            selector.x_rotation =
324                Some(parse_float_range(&reader.read_range_value(), value_cursor)?);
325            Ok(())
326        }
327        "y_rotation" => {
328            ensure_set_once(&mut state.y_rotation, "y_rotation", key_cursor)?;
329            let value_cursor = reader.cursor();
330            selector.y_rotation =
331                Some(parse_float_range(&reader.read_range_value(), value_cursor)?);
332            Ok(())
333        }
334        "limit" => parse_limit_option(reader, selector, state, key_cursor),
335        "sort" => parse_sort_option(reader, selector, state, key_cursor),
336        "gamemode" => parse_gamemode_option(reader, selector, state),
337        "type" => parse_type_option(reader, selector, state),
338        "tag" => {
339            parse_tag_option(reader, selector);
340            Ok(())
341        }
342        "team" => parse_team_option(reader, selector, state),
343        "nbt" => parse_nbt_option(reader, selector),
344        "scores" => parse_scores_option(reader, selector, state, key_cursor),
345        "predicate" => Err(SelectorParseError::unsupported(
346            "predicate needs a reloadable or plugin predicate registry",
347            key_cursor,
348        )),
349        "advancements" => Err(SelectorParseError::unsupported(
350            "advancements needs player advancement foundation",
351            key_cursor,
352        )),
353        _ => Err(SelectorParseError::invalid_at(
354            format!("unknown selector option '{key}'"),
355            key_cursor,
356        )),
357    }
358}
359
360fn parse_name_option(
361    reader: &mut SelectorReader<'_>,
362    selector: &mut EntitySelector,
363    state: &mut SelectorOptionState,
364) -> Result<(), SelectorParseError> {
365    let value_cursor = reader.cursor();
366    let inverted = reader.read_inversion();
367    state
368        .name
369        .parse_element(inverted, "name")
370        .map_err(|error| {
371            SelectorParseError::invalid_at(
372                match error.kind {
373                    SelectorParseErrorKind::Invalid(message) => *message,
374                    SelectorParseErrorKind::NotAllowed
375                    | SelectorParseErrorKind::AdvancedNotAllowed
376                    | SelectorParseErrorKind::Unsupported(_) => {
377                        TextComponent::from("invalid name option")
378                    }
379                },
380                value_cursor,
381            )
382        })?;
383    let value = reader.read_string()?;
384    selector
385        .filters
386        .push(SelectorFilter::Name { value, inverted });
387    Ok(())
388}
389
390fn parse_scores_option(
391    reader: &mut SelectorReader<'_>,
392    selector: &mut EntitySelector,
393    state: &mut SelectorOptionState,
394    key_cursor: usize,
395) -> Result<(), SelectorParseError> {
396    ensure_set_once(&mut state.scores, "scores", key_cursor)?;
397    let scores = reader.read_scores()?;
398    if !scores.is_empty() {
399        selector.filters.push(SelectorFilter::Scores(scores));
400    }
401    Ok(())
402}
403
404fn parse_distance_option(
405    reader: &mut SelectorReader<'_>,
406    selector: &mut EntitySelector,
407    state: &mut SelectorOptionState,
408    key_cursor: usize,
409) -> Result<(), SelectorParseError> {
410    ensure_set_once(&mut state.distance, "distance", key_cursor)?;
411    let value_cursor = reader.cursor();
412    let range = parse_double_range(&reader.read_range_value(), value_cursor)?;
413    if range.min.is_some_and(|value| value < 0.0) || range.max.is_some_and(|value| value < 0.0) {
414        return Err(SelectorParseError::invalid_at(
415            "distance cannot be negative",
416            key_cursor,
417        ));
418    }
419    selector.distance = Some(range);
420    selector.world_limited = true;
421    Ok(())
422}
423
424fn parse_level_option(
425    reader: &mut SelectorReader<'_>,
426    selector: &mut EntitySelector,
427    state: &mut SelectorOptionState,
428    key_cursor: usize,
429) -> Result<(), SelectorParseError> {
430    ensure_set_once(&mut state.level, "level", key_cursor)?;
431    let value_cursor = reader.cursor();
432    let range = parse_int_range(&reader.read_range_value(), value_cursor)?;
433    if range.min.is_some_and(|value| value < 0) || range.max.is_some_and(|value| value < 0) {
434        return Err(SelectorParseError::invalid_at(
435            "level cannot be negative",
436            key_cursor,
437        ));
438    }
439    selector.level = Some(range);
440    selector.includes_entities = false;
441    Ok(())
442}
443
444fn parse_limit_option(
445    reader: &mut SelectorReader<'_>,
446    selector: &mut EntitySelector,
447    state: &mut SelectorOptionState,
448    key_cursor: usize,
449) -> Result<(), SelectorParseError> {
450    if selector.current_entity {
451        return Err(SelectorParseError::invalid_at(
452            "limit cannot be used with @s",
453            key_cursor,
454        ));
455    }
456    ensure_set_once(&mut state.limit, "limit", key_cursor)?;
457    let value = reader.read_i32()?;
458    if value < 1 {
459        return Err(SelectorParseError::invalid_at(
460            "limit must be at least 1",
461            key_cursor,
462        ));
463    }
464    selector.max_results = value as usize;
465    Ok(())
466}
467
468fn parse_sort_option(
469    reader: &mut SelectorReader<'_>,
470    selector: &mut EntitySelector,
471    state: &mut SelectorOptionState,
472    key_cursor: usize,
473) -> Result<(), SelectorParseError> {
474    if selector.current_entity {
475        return Err(SelectorParseError::invalid_at(
476            "sort cannot be used with @s",
477            key_cursor,
478        ));
479    }
480    ensure_set_once(&mut state.sort, "sort", key_cursor)?;
481    let value = reader.read_required_unquoted_string()?;
482    selector.order = match value.as_str() {
483        SORT_NEAREST => SelectorOrder::Nearest,
484        SORT_FURTHEST => SelectorOrder::Furthest,
485        SORT_RANDOM => SelectorOrder::Random,
486        SORT_ARBITRARY => SelectorOrder::Arbitrary,
487        _ => {
488            return Err(SelectorParseError::invalid_at(
489                format!("unknown sort '{value}'"),
490                key_cursor,
491            ));
492        }
493    };
494    Ok(())
495}
496
497fn parse_gamemode_option(
498    reader: &mut SelectorReader<'_>,
499    selector: &mut EntitySelector,
500    state: &mut SelectorOptionState,
501) -> Result<(), SelectorParseError> {
502    let value_cursor = reader.cursor();
503    let inverted = reader.read_inversion();
504    state.gamemode.parse_element(inverted, "gamemode")?;
505    let value = reader.read_required_unquoted_string()?;
506    let Some(game_mode) = parse_game_mode(&value) else {
507        return Err(SelectorParseError::invalid_at(
508            format!("invalid game mode '{value}'"),
509            value_cursor,
510        ));
511    };
512    selector.includes_entities = false;
513    selector.filters.push(SelectorFilter::GameMode {
514        value: game_mode,
515        inverted,
516    });
517    Ok(())
518}
519
520fn parse_type_option(
521    reader: &mut SelectorReader<'_>,
522    selector: &mut EntitySelector,
523    state: &mut SelectorOptionState,
524) -> Result<(), SelectorParseError> {
525    let value_cursor = reader.cursor();
526    let inverted = reader.read_inversion();
527    if reader.peek() == Some('#') {
528        reader.read();
529        reader.skip_whitespace();
530        let value = read_identifier(reader, value_cursor)?;
531        state.entity_type.parse_tag(&value, "type")?;
532        selector
533            .filters
534            .push(SelectorFilter::EntityTypeTag { value, inverted });
535        return Ok(());
536    }
537
538    state.entity_type.parse_element(inverted, "type")?;
539    let key = read_identifier(reader, value_cursor)?;
540    let Some(entity_type) = REGISTRY.entity_types.by_key(&key) else {
541        return Err(SelectorParseError::invalid_at(
542            format!("invalid entity type '{key}'"),
543            value_cursor,
544        ));
545    };
546    if entity_type == &vanilla_entities::PLAYER && !inverted {
547        selector.includes_entities = false;
548    }
549    selector.filters.push(SelectorFilter::EntityType {
550        value: entity_type,
551        inverted,
552    });
553    Ok(())
554}
555
556fn parse_tag_option(reader: &mut SelectorReader<'_>, selector: &mut EntitySelector) {
557    let inverted = reader.read_inversion();
558    let value = reader.read_unquoted_string();
559    selector
560        .filters
561        .push(SelectorFilter::Tag { value, inverted });
562}
563
564fn parse_team_option(
565    reader: &mut SelectorReader<'_>,
566    selector: &mut EntitySelector,
567    state: &mut SelectorOptionState,
568) -> Result<(), SelectorParseError> {
569    let inverted = reader.read_inversion();
570    state.team.parse_element(inverted, "team")?;
571    let value = reader.read_unquoted_string();
572    selector
573        .filters
574        .push(SelectorFilter::Team { value, inverted });
575    Ok(())
576}
577
578fn parse_nbt_option(
579    reader: &mut SelectorReader<'_>,
580    selector: &mut EntitySelector,
581) -> Result<(), SelectorParseError> {
582    let inverted = reader.read_inversion();
583    let value = reader.read_nbt()?;
584    selector
585        .filters
586        .push(SelectorFilter::Nbt { value, inverted });
587    Ok(())
588}
589
590fn read_identifier(
591    reader: &mut SelectorReader<'_>,
592    value_cursor: usize,
593) -> Result<Identifier, SelectorParseError> {
594    let value = reader.read_identifier_string();
595    parse_resource_identifier_value(&value).ok_or_else(|| {
596        SelectorParseError::invalid_at(format!("invalid identifier '{value}'"), value_cursor)
597    })
598}
599
600pub(super) fn parse_resource_identifier_value(value: &str) -> Option<Identifier> {
601    let (namespace, path) = value.split_once(':').map_or(
602        (Identifier::VANILLA_NAMESPACE, value),
603        |(namespace, path)| {
604            if namespace.is_empty() {
605                (Identifier::VANILLA_NAMESPACE, path)
606            } else {
607                (namespace, path)
608            }
609        },
610    );
611    Identifier::validate(namespace, path)
612        .then(|| Identifier::new(namespace.to_owned(), path.to_owned()))
613}
614
615fn ensure_set_once(seen: &mut bool, option: &str, cursor: usize) -> Result<(), SelectorParseError> {
616    if *seen {
617        return Err(SelectorParseError::invalid_at(
618            format!("option '{option}' cannot be repeated"),
619            cursor,
620        ));
621    }
622    *seen = true;
623    Ok(())
624}
625
626fn parse_game_mode(value: &str) -> Option<GameType> {
627    match value {
628        "survival" => Some(GameType::Survival),
629        "creative" => Some(GameType::Creative),
630        "adventure" => Some(GameType::Adventure),
631        "spectator" => Some(GameType::Spectator),
632        _ => None,
633    }
634}
635
636fn parse_double_range(raw: &str, cursor: usize) -> Result<DoubleRange, SelectorParseError> {
637    let (min, max) = parse_range(raw, cursor, str::parse::<f64>)?;
638    if let (Some(min), Some(max)) = (min, max)
639        && min > max
640    {
641        return Err(SelectorParseError::invalid_at(
642            "range minimum exceeds maximum",
643            cursor,
644        ));
645    }
646    Ok(DoubleRange { min, max })
647}
648
649fn parse_float_range(raw: &str, cursor: usize) -> Result<FloatRange, SelectorParseError> {
650    let (min, max) = parse_range(raw, cursor, str::parse::<f32>)?;
651    Ok(FloatRange { min, max })
652}
653
654fn parse_int_range(raw: &str, cursor: usize) -> Result<IntRange, SelectorParseError> {
655    let (min, max) = parse_range(raw, cursor, str::parse::<i32>)?;
656    if let (Some(min), Some(max)) = (min, max)
657        && min > max
658    {
659        return Err(SelectorParseError::invalid_at(
660            "range minimum exceeds maximum",
661            cursor,
662        ));
663    }
664    Ok(IntRange { min, max })
665}
666
667fn parse_range<T: Copy, E>(
668    raw: &str,
669    cursor: usize,
670    parse: impl Fn(&str) -> Result<T, E>,
671) -> Result<(Option<T>, Option<T>), SelectorParseError> {
672    if raw.is_empty() {
673        return Err(SelectorParseError::invalid_at(
674            "missing range value",
675            cursor,
676        ));
677    }
678    let Some((left, right)) = raw.split_once("..") else {
679        let value = parse(raw)
680            .map_err(|_| SelectorParseError::invalid_at("invalid range value", cursor))?;
681        return Ok((Some(value), Some(value)));
682    };
683    if left.is_empty() && right.is_empty() {
684        return Err(SelectorParseError::invalid_at("empty range", cursor));
685    }
686    let right_cursor = cursor + left.len() + "..".len();
687    let min = if left.is_empty() {
688        None
689    } else {
690        Some(
691            parse(left)
692                .map_err(|_| SelectorParseError::invalid_at("invalid range minimum", cursor))?,
693        )
694    };
695    let max =
696        if right.is_empty() {
697            None
698        } else {
699            Some(parse(right).map_err(|_| {
700                SelectorParseError::invalid_at("invalid range maximum", right_cursor)
701            })?)
702        };
703    Ok((min, max))
704}
705
706pub(super) fn wrap_degrees(value: f32) -> f32 {
707    let mut value = value % 360.0;
708    if value >= 180.0 {
709        value -= 360.0;
710    }
711    if value < -180.0 {
712        value += 360.0;
713    }
714    value
715}
716
717#[derive(Clone)]
718struct SelectorReader<'a> {
719    input: &'a str,
720    cursor: usize,
721}
722
723impl<'a> SelectorReader<'a> {
724    const fn new(input: &'a str) -> Self {
725        Self { input, cursor: 0 }
726    }
727
728    const fn cursor(&self) -> usize {
729        self.cursor
730    }
731
732    const fn can_read(&self) -> bool {
733        self.cursor < self.input.len()
734    }
735
736    fn remaining(&self) -> &'a str {
737        &self.input[self.cursor..]
738    }
739
740    fn peek(&self) -> Option<char> {
741        self.remaining().chars().next()
742    }
743
744    fn read(&mut self) -> Option<char> {
745        let ch = self.peek()?;
746        self.cursor += ch.len_utf8();
747        Some(ch)
748    }
749
750    fn skip_whitespace(&mut self) {
751        while self.peek().is_some_and(java::is_whitespace) {
752            self.read();
753        }
754    }
755
756    fn expect(&mut self, expected: char) -> Result<(), SelectorParseError> {
757        if self.peek() == Some(expected) {
758            self.read();
759            Ok(())
760        } else {
761            Err(SelectorParseError::invalid_at(
762                format!("expected '{expected}'"),
763                self.cursor,
764            ))
765        }
766    }
767
768    fn read_remaining(&mut self) -> String {
769        let value = self.remaining().to_owned();
770        self.cursor = self.input.len();
771        value
772    }
773
774    fn read_key(&mut self) -> Result<String, SelectorParseError> {
775        let start = self.cursor;
776        if self.peek().is_some_and(is_quoted_string_start) {
777            return self.read_quoted_string();
778        }
779
780        let key = self.read_unquoted_string();
781        if key.is_empty() {
782            return Err(SelectorParseError::invalid_at(
783                "expected selector option name",
784                start,
785            ));
786        }
787        Ok(key)
788    }
789
790    fn read_scores(&mut self) -> Result<Vec<(String, IntRange)>, SelectorParseError> {
791        self.expect('{')?;
792        let mut scores = Vec::new();
793        self.skip_whitespace();
794        while self.peek().is_some_and(|ch| ch != '}') {
795            self.skip_whitespace();
796            let name = self.read_unquoted_string();
797            self.skip_whitespace();
798            self.expect('=')?;
799            self.skip_whitespace();
800            let range_cursor = self.cursor;
801            let range = parse_int_range(&self.read_range_value(), range_cursor)?;
802            upsert_score_filter(&mut scores, name, range);
803            self.skip_whitespace();
804            match self.peek() {
805                Some(',') => {
806                    self.read();
807                    self.skip_whitespace();
808                }
809                Some('}') | None => {}
810                Some(_) => {
811                    return Err(SelectorParseError::invalid_at(
812                        "expected ',' or '}' after score range",
813                        self.cursor,
814                    ));
815                }
816            }
817        }
818        self.expect('}')?;
819        Ok(scores)
820    }
821
822    fn read_unquoted_string(&mut self) -> String {
823        let start = self.cursor;
824        while self.peek().is_some_and(is_brigadier_unquoted_char) {
825            self.read();
826        }
827        self.input[start..self.cursor].to_owned()
828    }
829
830    fn read_required_unquoted_string(&mut self) -> Result<String, SelectorParseError> {
831        let start = self.cursor;
832        let value = self.read_unquoted_string();
833        if value.is_empty() {
834            return Err(SelectorParseError::invalid_at(
835                "expected selector option value",
836                start,
837            ));
838        }
839        Ok(value)
840    }
841
842    fn read_identifier_string(&mut self) -> String {
843        let start = self.cursor;
844        while self.peek().is_some_and(is_identifier_char) {
845            self.read();
846        }
847        self.input[start..self.cursor].to_owned()
848    }
849
850    fn read_number_string(&mut self) -> String {
851        self.skip_whitespace();
852        let start = self.cursor;
853        while self.peek().is_some_and(is_number_char) {
854            self.read();
855        }
856        self.input[start..self.cursor].to_owned()
857    }
858
859    fn read_range_value(&mut self) -> String {
860        self.skip_whitespace();
861        let start = self.cursor;
862        self.read_range_number();
863        if self.peek() == Some('.') && self.peek_next() == Some('.') {
864            self.read();
865            self.read();
866            self.read_range_number();
867        }
868        self.input[start..self.cursor].to_owned()
869    }
870
871    fn read_range_number(&mut self) {
872        while self
873            .peek()
874            .is_some_and(|ch| is_range_number_char(ch, self.peek_next()))
875        {
876            self.read();
877        }
878    }
879
880    fn peek_next(&self) -> Option<char> {
881        let mut chars = self.remaining().chars();
882        chars.next()?;
883        chars.next()
884    }
885
886    fn read_nbt(&mut self) -> Result<NbtCompound, SelectorParseError> {
887        let nbt_cursor = self.cursor;
888        let (nbt, consumed) =
889            parse_snbt_compound_argument(&self.input[self.cursor..]).map_err(|error| {
890                SelectorParseError::invalid_at(error.component(), nbt_cursor + error.cursor())
891            })?;
892        self.cursor += consumed;
893        Ok(nbt)
894    }
895
896    fn read_inversion(&mut self) -> bool {
897        self.skip_whitespace();
898        if self.peek() == Some('!') {
899            self.read();
900            self.skip_whitespace();
901            true
902        } else {
903            false
904        }
905    }
906
907    fn read_i32(&mut self) -> Result<i32, SelectorParseError> {
908        let cursor = self.cursor;
909        let value = self.read_number_string();
910        value.parse().map_err(|_| {
911            SelectorParseError::invalid_at(format!("invalid integer '{value}'"), cursor)
912        })
913    }
914
915    fn read_f64(&mut self) -> Result<f64, SelectorParseError> {
916        let cursor = self.cursor;
917        let value = self.read_number_string();
918        value.parse().map_err(|_| {
919            SelectorParseError::invalid_at(format!("invalid double '{value}'"), cursor)
920        })
921    }
922
923    fn read_string(&mut self) -> Result<String, SelectorParseError> {
924        self.skip_whitespace();
925        match self.peek() {
926            Some(ch) if is_quoted_string_start(ch) => self.read_quoted_string(),
927            _ => Ok(self.read_unquoted_string()),
928        }
929    }
930
931    fn read_quoted_string(&mut self) -> Result<String, SelectorParseError> {
932        let start = self.cursor;
933        let Some(quote) = self.read() else {
934            return Err(SelectorParseError::invalid_at(
935                "expected quoted string",
936                start,
937            ));
938        };
939        let mut value = String::new();
940        while let Some(ch) = self.read() {
941            match ch {
942                ch if ch == quote => return Ok(value),
943                '\\' => {
944                    let Some(escaped) = self.read() else {
945                        return Err(SelectorParseError::invalid_at("unclosed quote", start));
946                    };
947                    if escaped != quote && escaped != '\\' {
948                        return Err(SelectorParseError::invalid_at(
949                            format!("invalid escape '{escaped}'"),
950                            self.cursor,
951                        ));
952                    }
953                    value.push(escaped);
954                }
955                _ => value.push(ch),
956            }
957        }
958        Err(SelectorParseError::invalid_at("unclosed quote", start))
959    }
960}
961
962fn upsert_score_filter(scores: &mut Vec<(String, IntRange)>, name: String, range: IntRange) {
963    if let Some((_, existing)) = scores
964        .iter_mut()
965        .find(|(existing_name, _)| existing_name == &name)
966    {
967        *existing = range;
968        return;
969    }
970    scores.push((name, range));
971}
972
973const fn is_brigadier_unquoted_char(ch: char) -> bool {
974    ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '.' | '+')
975}
976
977const fn is_identifier_char(ch: char) -> bool {
978    ch.is_ascii_digit() || matches!(ch, 'a'..='z' | '_' | ':' | '/' | '.' | '-')
979}
980
981const fn is_number_char(ch: char) -> bool {
982    ch.is_ascii_digit() || matches!(ch, '.' | '-')
983}
984
985fn is_range_number_char(ch: char, next: Option<char>) -> bool {
986    ch.is_ascii_digit() || ch == '-' || (ch == '.' && next != Some('.'))
987}
988
989const fn is_quoted_string_start(ch: char) -> bool {
990    matches!(ch, '"' | '\'')
991}