Skip to main content

steel_core/command/execution/
structure.rs

1//! Structure registry key command arguments.
2
3use steel_registry::{REGISTRY, RegistryExt as _, TaggedRegistryExt as _, structure::StructureRef};
4use steel_utils::Identifier;
5
6use super::argument::parse_identifier;
7use super::suggestions::identifier_matches;
8use crate::command::brigadier::{CommandSyntaxError, StringReader, SuggestionsBuilder};
9
10/// A structure resource key or tag key retained until command execution.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub(crate) enum StructureOrTagKey {
13    Structure(Identifier),
14    Tag(Identifier),
15}
16
17impl StructureOrTagKey {
18    pub(crate) fn resolve(&self) -> Option<Vec<StructureRef>> {
19        match self {
20            Self::Structure(key) => REGISTRY
21                .structures
22                .by_key(key)
23                .map(|structure| vec![structure]),
24            Self::Tag(key) => REGISTRY.structures.get_tag(key),
25        }
26    }
27
28    pub(crate) fn as_printable(&self) -> String {
29        match self {
30            Self::Structure(key) => key.to_string(),
31            Self::Tag(key) => format!("#{key}"),
32        }
33    }
34
35    pub(crate) fn found_name(&self, found_structure: &Identifier) -> String {
36        match self {
37            Self::Structure(key) => key.to_string(),
38            Self::Tag(key) => format!("#{key} ({found_structure})"),
39        }
40    }
41}
42
43pub(super) fn parse_structure_or_tag_key(
44    reader: &mut StringReader<'_>,
45) -> Result<StructureOrTagKey, CommandSyntaxError> {
46    if reader.peek() != Some('#') {
47        return parse_identifier(reader).map(StructureOrTagKey::Structure);
48    }
49
50    let start = reader.checkpoint();
51    reader.skip();
52    match parse_identifier(reader) {
53        Ok(key) => Ok(StructureOrTagKey::Tag(key)),
54        Err(error) => {
55            reader.restore(start);
56            Err(error)
57        }
58    }
59}
60
61pub(super) fn suggest_structures(builder: &mut SuggestionsBuilder<'_>) {
62    let remaining = builder.remaining_lowercase();
63    let suggestions = if let Some(tag_prefix) = remaining.strip_prefix('#') {
64        REGISTRY
65            .structures
66            .tag_keys()
67            .filter(|tag| identifier_matches(tag_prefix, tag))
68            .map(|tag| format!("#{tag}"))
69            .collect::<Vec<_>>()
70    } else {
71        REGISTRY
72            .structures
73            .iter()
74            .filter(|(_, structure)| identifier_matches(remaining, &structure.key))
75            .map(|(_, structure)| structure.key.to_string())
76            .chain(
77                REGISTRY
78                    .structures
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}