Skip to main content

steel_core/command/execution/
biome.rs

1//! Biome registry command arguments.
2
3use steel_registry::{
4    BIOMES_REGISTRY, REGISTRY, RegistryExt as _, TaggedRegistryExt as _, biome::BiomeRef,
5};
6use steel_utils::{Identifier, translations};
7
8use super::argument::{parse_identifier, unknown_resource};
9use super::suggestions::identifier_matches;
10use crate::command::brigadier::{
11    CommandSyntaxError, CommandSyntaxErrorKind, StringReader, SuggestionsBuilder,
12};
13
14/// A registered biome or biome tag retained by a command argument.
15#[derive(Clone, Debug, PartialEq)]
16pub(crate) enum BiomeOrTag {
17    Biome(BiomeRef),
18    Tag(Identifier),
19}
20
21impl BiomeOrTag {
22    pub(crate) fn matches(&self, biome: BiomeRef) -> bool {
23        match self {
24            Self::Biome(expected) => *expected == biome,
25            Self::Tag(tag) => biome.has_tag(tag),
26        }
27    }
28}
29
30pub(super) fn parse_biome_or_tag(
31    reader: &mut StringReader<'_>,
32) -> Result<BiomeOrTag, CommandSyntaxError> {
33    if reader.peek() != Some('#') {
34        let key = parse_identifier(reader)?;
35        return REGISTRY.biomes.by_key(&key).map_or_else(
36            || Err(unknown_resource(reader, &key, &BIOMES_REGISTRY)),
37            |biome| Ok(BiomeOrTag::Biome(biome)),
38        );
39    }
40
41    let start = reader.checkpoint();
42    reader.skip();
43    let key = match parse_identifier(reader) {
44        Ok(key) => key,
45        Err(error) => {
46            reader.restore(start);
47            return Err(error);
48        }
49    };
50    if REGISTRY.biomes.tag_keys().any(|tag| tag == &key) {
51        return Ok(BiomeOrTag::Tag(key));
52    }
53
54    let message = translations::ARGUMENT_RESOURCE_TAG_NOT_FOUND
55        .message([key.to_string(), BIOMES_REGISTRY.to_string()])
56        .component();
57    let error = reader.error(CommandSyntaxErrorKind::Dynamic(Box::new(message)));
58    reader.restore(start);
59    Err(error)
60}
61
62pub(super) fn suggest_biomes(builder: &mut SuggestionsBuilder<'_>) {
63    let remaining = builder.remaining_lowercase();
64    let suggestions = if let Some(tag_prefix) = remaining.strip_prefix('#') {
65        REGISTRY
66            .biomes
67            .tag_keys()
68            .filter(|tag| identifier_matches(tag_prefix, tag))
69            .map(|tag| format!("#{tag}"))
70            .collect::<Vec<_>>()
71    } else {
72        REGISTRY
73            .biomes
74            .iter()
75            .filter(|(_, biome)| identifier_matches(remaining, &biome.key))
76            .map(|(_, biome)| biome.key.to_string())
77            .chain(
78                REGISTRY
79                    .biomes
80                    .tag_keys()
81                    .filter(|tag| identifier_matches(remaining, tag))
82                    .map(|tag| format!("#{tag}")),
83            )
84            .collect()
85    };
86    for suggestion in suggestions {
87        builder.suggest(suggestion);
88    }
89}