mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
chore(command): add argument type helpers (#1972)
* added helpers for all currently existing argument types * implemented helper for `/setidletimeout` * fixed formatting * changed `/kill` to use helper
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType};
|
||||
use crate::command::argument_types::coordinates::Coordinates;
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::string_reader::StringReader;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
pub const INCOMPLETE_ERROR_TYPE: CommandErrorType<0> =
|
||||
CommandErrorType::new(translation::ARGUMENT_POS3D_INCOMPLETE);
|
||||
@@ -52,6 +54,24 @@ impl ArgumentType for Vec3ArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Vec3ArgumentType {
|
||||
/// Returns a [`CommandContext`]'s parsed three-dimensional vector as a set of [`Coordinates`].
|
||||
pub fn get_coordinates(
|
||||
context: &CommandContext,
|
||||
name: &str,
|
||||
) -> Result<Coordinates, CommandSyntaxError> {
|
||||
Ok(*context.get_argument(name)?)
|
||||
}
|
||||
|
||||
/// Returns a [`CommandContext`]'s parsed three-dimensional vector and resolves it to a [`Vector3`].
|
||||
pub fn get_vector3(
|
||||
context: &CommandContext,
|
||||
name: &str,
|
||||
) -> Result<Vector3<f64>, CommandSyntaxError> {
|
||||
Ok(Self::get_coordinates(context, name)?.resolve(context.source.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::command::argument_types::argument_type::ArgumentType;
|
||||
|
||||
@@ -24,6 +24,8 @@ impl ArgumentType for BoolArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(BoolArgumentType, bool);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::command::{
|
||||
|
||||
@@ -43,6 +43,8 @@ impl ArgumentType for DoubleArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(DoubleArgumentType, f64);
|
||||
|
||||
impl DoubleArgumentType {
|
||||
/// Constructs a new [`DoubleArgumentType`] with no minimum or maximum bounds.
|
||||
#[must_use]
|
||||
|
||||
@@ -43,6 +43,8 @@ impl ArgumentType for FloatArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(FloatArgumentType, f32);
|
||||
|
||||
impl FloatArgumentType {
|
||||
/// Constructs a new [`FloatArgumentType`] with no minimum or maximum bounds.
|
||||
#[must_use]
|
||||
|
||||
@@ -43,6 +43,8 @@ impl ArgumentType for IntegerArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(IntegerArgumentType, i32);
|
||||
|
||||
impl IntegerArgumentType {
|
||||
/// Constructs a new [`IntegerArgumentType`] with no minimum or maximum bounds.
|
||||
#[must_use]
|
||||
|
||||
@@ -43,6 +43,8 @@ impl ArgumentType for LongArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(LongArgumentType, i64);
|
||||
|
||||
impl LongArgumentType {
|
||||
/// Constructs a new [`LongArgumentType`] with no minimum or maximum bounds.
|
||||
#[must_use]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use pumpkin_protocol::java::client::play::StringProtoArgBehavior;
|
||||
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::{
|
||||
argument_types::argument_type::{ArgumentType, JavaClientArgumentType},
|
||||
errors::command_syntax_error::CommandSyntaxError,
|
||||
@@ -49,6 +50,13 @@ impl ArgumentType for StringArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl StringArgumentType {
|
||||
/// Returns a [`CommandContext`]'s parsed `String` argument as a string slice.
|
||||
pub fn get<'a>(context: &'a CommandContext, name: &str) -> Result<&'a str, CommandSyntaxError> {
|
||||
Ok(context.get_argument::<String>(name)?.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::command::{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType};
|
||||
use crate::command::argument_types::entity_selector::EntitySelector;
|
||||
use crate::command::argument_types::entity_selector::parser::EntitySelectorParser;
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::string_reader::StringReader;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use pumpkin_data::translation;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A [`CommandErrorType`] to tell that no entities could be found.
|
||||
pub const NO_ENTITIES_ERROR_TYPE: CommandErrorType<0> =
|
||||
@@ -101,4 +105,74 @@ impl EntityArgumentType {
|
||||
Ok(selector)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to get a single entity from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_entity(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Arc<dyn EntityBase>, CommandSyntaxError> {
|
||||
context
|
||||
.get_argument::<EntitySelector>(name)?
|
||||
.find_single_entity(context.source.as_ref())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Tries to get at least 1 entity from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_entities(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Vec<Arc<dyn EntityBase>>, CommandSyntaxError> {
|
||||
let entities = Self::get_optional_entities(context, name).await?;
|
||||
if entities.is_empty() {
|
||||
Err(NO_ENTITIES_ERROR_TYPE.create_without_context())
|
||||
} else {
|
||||
Ok(entities)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to get any number of entities from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_optional_entities(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Vec<Arc<dyn EntityBase>>, CommandSyntaxError> {
|
||||
context
|
||||
.get_argument::<EntitySelector>(name)?
|
||||
.find_entities(context.source.as_ref())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Tries to get a single player from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_player(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Arc<Player>, CommandSyntaxError> {
|
||||
context
|
||||
.get_argument::<EntitySelector>(name)?
|
||||
.find_single_player(context.source.as_ref())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Tries to get at least 1 player from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_players(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Vec<Arc<Player>>, CommandSyntaxError> {
|
||||
let players = Self::get_optional_players(context, name).await?;
|
||||
if players.is_empty() {
|
||||
Err(NO_PLAYERS_ERROR_TYPE.create_without_context())
|
||||
} else {
|
||||
Ok(players)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to get any number of players from a parsed argument of the provided [`CommandContext`].
|
||||
pub async fn get_optional_players(
|
||||
context: &CommandContext<'_>,
|
||||
name: &str,
|
||||
) -> Result<Vec<Arc<Player>>, CommandSyntaxError> {
|
||||
context
|
||||
.get_argument::<EntitySelector>(name)?
|
||||
.find_players(context.source.as_ref())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ mod option;
|
||||
pub mod parser;
|
||||
|
||||
use crate::command::argument_types::entity;
|
||||
use crate::command::argument_types::entity::{
|
||||
ENTITY_SELECTOR_PERMISSION, NO_ENTITIES_ERROR_TYPE, NO_PLAYERS_ERROR_TYPE,
|
||||
};
|
||||
use crate::command::argument_types::entity::ENTITY_SELECTOR_PERMISSION;
|
||||
use crate::command::argument_types::entity_selector::parser::SELECTORS_NOT_ALLOWED_ERROR_TYPE;
|
||||
use crate::command::context::command_source::CommandSource;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
@@ -84,7 +82,7 @@ impl EntitySelector {
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Arc<dyn EntityBase>, CommandSyntaxError> {
|
||||
let list = self.find_optional_entities(source).await?;
|
||||
let list = self.find_entities(source).await?;
|
||||
match list.as_slice() {
|
||||
[] => Err(entity::NO_ENTITIES_ERROR_TYPE.create_without_context()),
|
||||
[entity] => Ok(entity.clone()),
|
||||
@@ -92,28 +90,14 @@ impl EntitySelector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find any entities represented by this selector.
|
||||
/// If none are found, an error is returned.
|
||||
pub async fn find_entities(
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Vec<Arc<dyn EntityBase>>, CommandSyntaxError> {
|
||||
let entities = self.find_optional_entities(source).await?;
|
||||
if entities.is_empty() {
|
||||
Err(NO_ENTITIES_ERROR_TYPE.create_without_context())
|
||||
} else {
|
||||
Ok(entities)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find any entities represented by this selector. If none are found, an empty `Vec` will still be returned.
|
||||
pub async fn find_optional_entities(
|
||||
pub async fn find_entities(
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Vec<Arc<dyn EntityBase>>, CommandSyntaxError> {
|
||||
self.check_permissions(source).await?;
|
||||
if !self.includes_entities {
|
||||
self.find_optional_players(source)
|
||||
self.find_players(source)
|
||||
.await
|
||||
.map(|v| v.into_iter().map(|p| p as Arc<dyn EntityBase>).collect())
|
||||
} else if let Some(name) = self.player_name.as_ref() {
|
||||
@@ -176,7 +160,7 @@ impl EntitySelector {
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Arc<Player>, CommandSyntaxError> {
|
||||
let list = self.find_optional_players(source).await?;
|
||||
let list = self.find_players(source).await?;
|
||||
if list.len() == 1 {
|
||||
Ok(list.first().unwrap().clone())
|
||||
} else {
|
||||
@@ -185,23 +169,9 @@ impl EntitySelector {
|
||||
}
|
||||
|
||||
/// Tries to find any players represented by this selector.
|
||||
/// If none are found, an error is returned.
|
||||
pub async fn find_players(
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Vec<Arc<Player>>, CommandSyntaxError> {
|
||||
let players = self.find_optional_players(source).await?;
|
||||
if players.is_empty() {
|
||||
Err(NO_PLAYERS_ERROR_TYPE.create_without_context())
|
||||
} else {
|
||||
Ok(players)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find any players represented by this selector. If none are found, an empty `Vec` will still be returned.
|
||||
pub async fn find_optional_players(
|
||||
&self,
|
||||
source: &CommandSource,
|
||||
) -> Result<Vec<Arc<Player>>, CommandSyntaxError> {
|
||||
self.check_permissions(source).await?;
|
||||
if let Some(name) = self.player_name.as_ref() {
|
||||
|
||||
@@ -51,6 +51,18 @@ macro_rules! assert_parse_err_reset {
|
||||
};
|
||||
}
|
||||
|
||||
/// Macro to implement a single `get()` function for an argument type whose `Item` is `Copy`.
|
||||
macro_rules! impl_copy_get {
|
||||
($ty:ty, $item:ty) => {
|
||||
impl $ty {
|
||||
#[doc = concat!("Returns a [`CommandContext`]'s parsed `", stringify!($item), "` argument.")]
|
||||
pub fn get(context: &$crate::command::context::command_context::CommandContext, name: &str) -> Result<$item, CommandSyntaxError> {
|
||||
Ok(*context.get_argument(name)?)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const EMPTY_BOUNDS_ERROR_TYPE: CommandErrorType<0> =
|
||||
CommandErrorType::new(translation::ARGUMENT_RANGE_EMPTY);
|
||||
const SWAPPED_BOUNDS_ERROR_TYPE: CommandErrorType<0> =
|
||||
|
||||
@@ -32,6 +32,8 @@ impl ArgumentType for IntRangeArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(IntRangeArgumentType, IntBounds);
|
||||
|
||||
/// Parses an inclusive range of `f64`s that can be represented in the following ways:
|
||||
/// - `value`: Only includes the number `value`.
|
||||
/// - `min..`: All numbers above or equal to `min`.
|
||||
@@ -55,6 +57,8 @@ impl ArgumentType for FloatRangeArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl_copy_get!(FloatRangeArgumentType, DoubleBounds);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pumpkin_util::math::bounds::{DoubleBounds, IntBounds};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType};
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::string_reader::StringReader;
|
||||
@@ -71,6 +72,14 @@ impl ArgumentType for TimeArgumentType {
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeArgumentType {
|
||||
/// Returns a [`CommandContext`]'s parsed time argument in the form
|
||||
/// of its duration, in ticks.
|
||||
pub fn get(context: &CommandContext, name: &str) -> Result<i32, CommandSyntaxError> {
|
||||
Ok(*context.get_argument(name)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::command::{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::command::argument_builder::{ArgumentBuilder, argument, command};
|
||||
use crate::command::argument_types::entity::EntityArgumentType;
|
||||
use crate::command::argument_types::entity_selector::EntitySelector;
|
||||
use crate::command::context::command_context::CommandContext;
|
||||
use crate::command::node::dispatcher::CommandDispatcher;
|
||||
use crate::command::node::{CommandExecutor, CommandExecutorResult};
|
||||
@@ -20,8 +19,7 @@ struct TargetsExecutor;
|
||||
impl CommandExecutor for TargetsExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let selector: &EntitySelector = context.get_argument(ARG_TARGETS)?;
|
||||
let targets = selector.find_entities(&context.source).await?;
|
||||
let targets = EntityArgumentType::get_entities(context, ARG_TARGETS).await?;
|
||||
|
||||
let target_count = targets.len();
|
||||
for target in &targets {
|
||||
|
||||
@@ -23,7 +23,7 @@ struct SetIdleTimeoutExecutor;
|
||||
impl CommandExecutor for SetIdleTimeoutExecutor {
|
||||
fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> {
|
||||
Box::pin(async move {
|
||||
let minutes: i32 = *context.get_argument(ARG_MINUTES)?;
|
||||
let minutes: i32 = IntegerArgumentType::get(context, ARG_MINUTES)?;
|
||||
|
||||
context
|
||||
.server()
|
||||
|
||||
Reference in New Issue
Block a user