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