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