implemented command

refactor command to a new crate
This commit is contained in:
2026-02-08 17:34:52 +08:00
parent bfcd414a14
commit 900cd48509
17 changed files with 424 additions and 353 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
/target
**/target
.idea

11
Cargo.lock generated
View File

@@ -374,15 +374,24 @@ dependencies = [
"clap_mangen",
"color-eyre",
"dirs",
"magma-command",
"pest",
"pest_derive",
"semver",
"serde",
"serde_json",
"tokio",
"toml",
]
[[package]]
name = "magma-command"
version = "0.1.0"
dependencies = [
"color-eyre",
"serde",
"serde_json",
]
[[package]]
name = "memchr"
version = "2.7.6"

View File

@@ -3,15 +3,24 @@ name = "magma"
version = "0.1.0"
edition = "2024"
[workspace]
members = [ "magma-command" ]
[dependencies]
clap = { version = "4.5.54", features = ["cargo", "derive"] }
clap_mangen = "0.2.31"
pest = { version = "2.8.5", features = ["pretty-print"] }
pest_derive = { version = "2.8.5", features = ["grammar-extras"] }
color-eyre = "0.6.5"
tokio = { version = "1.49.0", features = ["full"] }
serde = { version = "1.0.228", features = ["derive"] }
toml = "0.9.11"
semver = { version = "1.0.27", features = ["serde"] }
dirs = "6.0.0"
serde_json = "1.0.149"
magma-command = { path = "./magma-command" }
color-eyre.workspace = true
serde.workspace = true
[workspace.dependencies]
color-eyre = "0.6.5"
serde = { version = "1.0.228", features = ["derive"] }

View File

@@ -8141,7 +8141,7 @@
"type": "literal",
"name": "run",
"executable": false,
"redirects": [],
"redirects": [""],
"children": []
},
{

7
magma-command/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "magma-command"
version = "0.1.0"

10
magma-command/Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "magma-command"
version = "0.1.0"
edition = "2024"
[dependencies]
serde_json = "1.0.149"
serde.workspace = true
color-eyre.workspace = true

View File

@@ -0,0 +1,48 @@
#[derive(Debug, Clone)]
pub enum CommandError {
EmptyCommand,
IncompleteCommand {
path: String,
suggestions: Vec<String>,
},
TooManyArguments {
path: String,
},
InvalidArgument {
path: String,
argument: String,
expected: Vec<String>,
},
InvalidRedirect {
from: String,
to: Vec<String>,
},
}
impl std::fmt::Display for CommandError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::EmptyCommand => write!(f, "Empty command"),
Self::IncompleteCommand { path, suggestions } => {
write!(f, "Incomplete command: {}\nExpected one of: {}", path, suggestions.join(", "))
}
Self::TooManyArguments { path } => {
write!(f, "Too many arguments for command: {}", path)
}
Self::InvalidArgument { path, argument, expected } => {
write!(
f,
"Invalid argument '{}' for command: {}\nExpected one of: {}",
argument,
path,
expected.join(", ")
)
},
Self::InvalidRedirect { from, to } => {
write!(f, "Invalid redirect from '{}' to '{}'", from, to.join(", "))
}
}
}
}
impl std::error::Error for CommandError {}

View File

@@ -1,5 +1,8 @@
pub mod types;
pub mod error;
use crate::types::mc_command::{CommandNode, CommandTree, ParserType};
use types::{CommandNode, CommandTree, ParserType, StringKind};
use error::CommandError;
pub struct MinecraftCommandValidator {
root: CommandNode,
@@ -65,15 +68,14 @@ impl MinecraftCommandValidator {
tokens: &[String],
node: &CommandNode,
index: usize,
path: Vec<String>,
mut path: Vec<String>,
) -> Result<ValidationResult, CommandError> {
// If we've consumed all tokens
if index >= tokens.len() {
return if node.is_executable() {
Ok(ValidationResult {
valid: true,
path,
suggestions: vec![],
suggestions: self.get_suggestions_from_node(node),
})
} else {
Err(CommandError::IncompleteCommand {
@@ -83,60 +85,80 @@ impl MinecraftCommandValidator {
};
}
let token = &tokens[index];
let current_token = &tokens[index];
let children = node.children();
if children.is_empty() {
if node.is_executable() {
return Err(CommandError::TooManyArguments {
if children.is_empty() && node.redirects().is_empty() {
return if node.is_executable() {
Err(CommandError::TooManyArguments {
path: path.join(" "),
});
})
} else {
return Err(CommandError::IncompleteCommand {
Err(CommandError::IncompleteCommand {
path: path.join(" "),
suggestions: vec![],
});
suggestions: self.get_suggestions_from_node(node),
})
}
}
// Try literal match first
for child in children {
if let CommandNode::Literal { name, .. } = child {
if name == token {
let mut new_path = path.clone();
new_path.push(token.clone());
return self.validate_tokens(tokens, child, index + 1, new_path);
if !node.redirects().is_empty() {
let nodes = self.get_redirects(&node.redirects());
for node in nodes {
if let Ok(result) = self.validate_tokens(tokens, node, index, path.clone()) {
return Ok(result);
}
}
return Err(CommandError::InvalidRedirect {
from: path.join(" "),
to: node.redirects().to_vec(),
})
}
// Try argument parsers
for child in children {
if let CommandNode::Argument { name, parser, .. } = child {
let parser_type = ParserType::from_parser_info(parser);
// Check if this is a greedy parser (includes Message, Component, and greedy strings)
if self.is_greedy_parser(&parser_type) {
// Greedy parser consumes all remaining tokens
let remaining = tokens[index..].join(" ");
if self.validate_argument(&remaining, &parser_type).is_ok() {
let mut new_path = path.clone();
new_path.push(format!("<{}>", name));
// Jump to the end since greedy consumed everything
return self.validate_tokens(tokens, child, tokens.len(), new_path);
}
} else if self.validate_argument(token, &parser_type).is_ok() {
let mut new_path = path.clone();
new_path.push(format!("<{}>", name));
return self.validate_tokens(tokens, child, index + 1, new_path);
}
if let CommandNode::Literal { name, .. } = child && name == current_token {
path.push(current_token.clone());
return self.validate_tokens(tokens, child, index + 1, path);
}
}
self.parse_args(node, tokens, index, path)
}
// No match found
/// an empty string value will redirect to all children
fn get_redirects(&self, name: &[String]) -> Vec<&CommandNode> {
self.root.children().iter()
.filter(
|child|
name.contains(child.name()) || name.contains(&String::from(""))
)
.collect()
}
fn parse_args(
&self,
node: &CommandNode,
tokens: &[String],
index: usize,
mut path: Vec<String>
) -> Result<ValidationResult, CommandError> {
let token = &tokens[index];
for child in node.children() {
let CommandNode::Argument { name, parser, .. } = child else {
continue;
};
let parser_type = ParserType::from_parser_info(parser);
if self.is_greedy_parser(&parser_type)
&& self.validate_argument(&tokens[index..].join(" "), &parser_type).is_ok()
{
path.push(format!("<{}>", name));
return self.validate_tokens(tokens, child, tokens.len(), path);
} else if self.validate_argument(token, &parser_type).is_ok() {
path.push(format!("<{}>", name));
return self.validate_tokens(tokens, child, index + 1, path);
}
}
Err(CommandError::InvalidArgument {
path: path.join(" "),
argument: token.clone(),
argument: token.to_string(),
expected: self.get_suggestions_from_node(node),
})
}
@@ -144,7 +166,7 @@ impl MinecraftCommandValidator {
fn is_greedy_parser(&self, parser_type: &ParserType) -> bool {
matches!(
parser_type,
ParserType::String { kind: crate::types::mc_command::StringKind::GreedyPhrase }
ParserType::String { kind: StringKind::GreedyPhrase }
| ParserType::Message
| ParserType::Component
)
@@ -161,43 +183,35 @@ impl MinecraftCommandValidator {
}
ParserType::Integer { min, max } => {
let num: i32 = value.parse().map_err(|_| "Invalid integer")?;
if let Some(min) = min {
if (num as f64) < *min {
return Err(format!("Value must be >= {}", min));
}
if let Some(min) = min && (num as f64) < *min {
return Err(format!("Value must be >= {}", min));
}
if let Some(max) = max {
if (num as f64) > *max {
return Err(format!("Value must be <= {}", max));
}
if let Some(max) = max && (num as f64) > *max {
return Err(format!("Value must be <= {}", max));
}
Ok(())
}
ParserType::Float { min, max } | ParserType::Double { min, max } => {
let num: f64 = value.parse().map_err(|_| "Invalid number")?;
if let Some(min) = min {
if num < *min {
return Err(format!("Value must be >= {}", min));
}
if let Some(min) = min && num < *min {
return Err(format!("Value must be >= {}", min));
}
if let Some(max) = max {
if num > *max {
return Err(format!("Value must be <= {}", max));
}
if let Some(max) = max && num > *max {
return Err(format!("Value must be <= {}", max));
}
Ok(())
}
ParserType::String { .. } => Ok(()),
ParserType::Entity { .. } => {
if value.starts_with('@') || value.starts_with('"') {
//TODO: have to check amount
if value.starts_with('@') {
Ok(())
} else {
Err("Invalid entity selector".to_string())
}
}
// Message and Component parsers accept any text
ParserType::String { .. } => Ok(()),
ParserType::Message | ParserType::Component => Ok(()),
_ => todo!(),
_ => Ok(()),
}
}
@@ -226,45 +240,3 @@ pub struct ValidationResult {
pub path: Vec<String>,
pub suggestions: Vec<String>,
}
#[derive(Debug, Clone)]
pub enum CommandError {
EmptyCommand,
IncompleteCommand {
path: String,
suggestions: Vec<String>,
},
TooManyArguments {
path: String,
},
InvalidArgument {
path: String,
argument: String,
expected: Vec<String>,
},
}
impl std::fmt::Display for CommandError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::EmptyCommand => write!(f, "Empty command"),
Self::IncompleteCommand { path, suggestions } => {
write!(f, "Incomplete command: {}\nExpected one of: {}", path, suggestions.join(", "))
}
Self::TooManyArguments { path } => {
write!(f, "Too many arguments for command: {}", path)
}
Self::InvalidArgument { path, argument, expected } => {
write!(
f,
"Invalid argument '{}' for command: {}\nExpected one of: {}",
argument,
path,
expected.join(", ")
)
}
}
}
}
impl std::error::Error for CommandError {}

View File

@@ -0,0 +1,75 @@
#[derive(Debug, Clone)]
pub enum ParserType {
Bool,
Double { min: Option<f64>, max: Option<f64> },
Float { min: Option<f64>, max: Option<f64> },
Integer { min: Option<f64>, max: Option<f64> },
Long { min: Option<f64>, max: Option<f64> },
String { kind: StringKind },
Entity { amount: EntityAmount, entity_type: EntityType },
ScoreHolder { amount: ScoreHolderAmount },
GameProfile,
BlockPos,
ColumnPos,
Vec3,
Vec2,
BlockState,
BlockPredicate,
ItemStack,
ItemPredicate,
Color,
Component,
Message,
Nbt,
NbtTag,
NbtPath,
Objective,
ObjectiveCriteria,
Operation,
Particle,
Angle,
Rotation,
ScoreboardSlot,
Swizzle,
Team,
ItemSlot,
ResourceLocation { registry: Option<String> },
Function,
EntityAnchor,
IntRange,
FloatRange,
Dimension,
Gamemode,
Time,
ResourceOrTag { registry: Option<String> },
Resource { registry: Option<String> },
TemplateMirror,
TemplateRotation,
Uuid,
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StringKind {
SingleWord,
QuotablePhrase,
GreedyPhrase,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityAmount {
Single,
Multiple,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityType {
Players,
Entities,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScoreHolderAmount {
Single,
Multiple,
}

View File

@@ -0,0 +1,86 @@
mod enums;
mod parser;
pub use enums::*;
pub use parser::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommandTree {
pub root: CommandNode,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum CommandNode {
Root {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
},
Literal {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
},
Argument {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
parser: ParserInfo,
},
}
impl CommandNode {
pub fn name(&self) -> &String {
match self {
CommandNode::Root { name, .. } => name,
CommandNode::Literal { name, .. } => name,
CommandNode::Argument { name, .. } => name,
}
}
pub fn is_executable(&self) -> bool {
match self {
CommandNode::Root { executable, .. } => *executable,
CommandNode::Literal { executable, .. } => *executable,
CommandNode::Argument { executable, .. } => *executable,
}
}
pub fn children(&self) -> &[CommandNode] {
match self {
CommandNode::Root { children, .. } => children,
CommandNode::Literal { children, .. } => children,
CommandNode::Argument { children, .. } => children,
}
}
pub fn redirects(&self) -> &[String] {
match self {
CommandNode::Root { redirects, .. } => redirects,
CommandNode::Literal { redirects, .. } => redirects,
CommandNode::Argument { redirects, .. } => redirects,
}
}
pub fn parser(&self) -> Option<&ParserInfo> {
match self {
CommandNode::Argument { parser, .. } => Some(parser),
_ => None,
}
}
}

View File

@@ -0,0 +1,67 @@
use serde::{Deserialize, Serialize};
use crate::types::{EntityAmount, EntityType, ParserType, ScoreHolderAmount, StringKind};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ParserInfo {
pub parser: String,
pub modifier: Option<serde_json::Value>,
}
impl ParserType {
pub fn from_parser_info(info: &ParserInfo) -> Self {
match info.parser.as_str() {
"brigadier:bool" => ParserType::Bool,
"brigadier:double" => ParserType::Double { min: None, max: None },
"brigadier:float" => ParserType::Float { min: None, max: None },
"brigadier:integer" => ParserType::Integer { min: None, max: None },
"brigadier:long" => ParserType::Long { min: None, max: None },
"brigadier:string" => ParserType::String { kind: StringKind::SingleWord },
"minecraft:entity" => ParserType::Entity {
amount: EntityAmount::Multiple,
entity_type: EntityType::Entities,
},
"minecraft:score_holder" => ParserType::ScoreHolder {
amount: ScoreHolderAmount::Multiple,
},
"minecraft:game_profile" => ParserType::GameProfile,
"minecraft:block_pos" => ParserType::BlockPos,
"minecraft:column_pos" => ParserType::ColumnPos,
"minecraft:vec3" => ParserType::Vec3,
"minecraft:vec2" => ParserType::Vec2,
"minecraft:block_state" => ParserType::BlockState,
"minecraft:block_predicate" => ParserType::BlockPredicate,
"minecraft:item_stack" => ParserType::ItemStack,
"minecraft:item_predicate" => ParserType::ItemPredicate,
"minecraft:color" => ParserType::Color,
"minecraft:component" => ParserType::Component,
"minecraft:message" => ParserType::Message,
"minecraft:nbt_compound_tag" => ParserType::Nbt,
"minecraft:nbt_tag" => ParserType::NbtTag,
"minecraft:nbt_path" => ParserType::NbtPath,
"minecraft:objective" => ParserType::Objective,
"minecraft:objective_criteria" => ParserType::ObjectiveCriteria,
"minecraft:operation" => ParserType::Operation,
"minecraft:particle" => ParserType::Particle,
"minecraft:angle" => ParserType::Angle,
"minecraft:rotation" => ParserType::Rotation,
"minecraft:scoreboard_slot" => ParserType::ScoreboardSlot,
"minecraft:swizzle" => ParserType::Swizzle,
"minecraft:team" => ParserType::Team,
"minecraft:item_slot" => ParserType::ItemSlot,
"minecraft:resource_location" => ParserType::ResourceLocation { registry: None },
"minecraft:function" => ParserType::Function,
"minecraft:entity_anchor" => ParserType::EntityAnchor,
"minecraft:int_range" => ParserType::IntRange,
"minecraft:float_range" => ParserType::FloatRange,
"minecraft:dimension" => ParserType::Dimension,
"minecraft:gamemode" => ParserType::Gamemode,
"minecraft:time" => ParserType::Time,
"minecraft:resource_or_tag" => ParserType::ResourceOrTag { registry: None },
"minecraft:resource" => ParserType::Resource { registry: None },
"minecraft:template_mirror" => ParserType::TemplateMirror,
"minecraft:template_rotation" => ParserType::TemplateRotation,
"minecraft:uuid" => ParserType::Uuid,
_ => ParserType::Unknown(info.parser.clone()),
}
}
}

View File

@@ -1,7 +1,7 @@
use std::path::PathBuf;
use pest::iterators::{Pair, Pairs};
use pest::Parser;
use crate::helpers::MinecraftCommandValidator;
use magma_command::MinecraftCommandValidator;
use crate::parser::{MagmaParser, Rule};
use crate::types::{MagmaProjectConfig, McFunctionFile};
@@ -36,15 +36,17 @@ impl MagmaCompiler {
Rule::functionExpression => {
let mut pairs = pair.into_inner();
let identifier = pairs.next().unwrap();
let args = if let Some(rule) = pairs.peek()
&& rule.as_rule() == Rule::functionArgs
{
Some(pairs.next().unwrap())
} else { None };
{ pairs.next() }
else { None };
let statements = if let Some(rule) = pairs.peek()
&& rule.as_rule() == Rule::block
{ unbox_rule(pairs.next().unwrap()) }
{ expand_next_rule(pairs) }
else { None };
self.parse_function(None, identifier, args, statements)?;
}
_ => {}
@@ -64,25 +66,33 @@ impl MagmaCompiler {
return Ok(file);
};
for statement in statements {
let Some(statement) = self.parse_statement(statement)? else {
continue;
};
file.add_line(statement);
let statements = self.parse_statement(statement)?;
file.add_lines(statements);
}
println!("{}", file.lines().join("\n"));
println!();
Ok(file)
}
fn parse_statement(&self, statement: Pair<Rule>) -> color_eyre::Result<Option<String>> {
fn parse_statement(&self, statement: Pair<Rule>) -> color_eyre::Result<Vec<String>> {
let statement = statement.into_inner().next().unwrap();
let mut lines = Vec::new();
match statement.as_rule() {
Rule::commandLine => {
let command = statement.into_inner().next().unwrap();
Ok(Some(self.parse_command(command)?))
lines.push(self.parse_command(command)?);
},
Rule::commandBlock => {
let commands = statement.into_inner();
for command in commands {
lines.push(self.parse_command(command)?);
}
}
_ => {
Ok(None)
println!("{:?}", statement);
}
}
Ok(lines)
}
fn parse_command(&self, command: Pair<Rule>) -> color_eyre::Result<String> {
@@ -93,7 +103,7 @@ impl MagmaCompiler {
let string = unbox_string(primitive);
primitives.push(string);
}
_ => primitives.push(primitive.as_str().to_string())
_ => { primitives.push(primitive.as_str().to_string())}
}
}
let unboxed_command = primitives.join(" ");
@@ -102,9 +112,8 @@ impl MagmaCompiler {
}
}
fn unbox_rule(rule: Pair<Rule>) -> Option<Pairs<Rule>> {
let mut block = rule.into_inner();
if let Some(inner) = block.next() {
fn expand_next_rule(mut rule: Pairs<Rule>) -> Option<Pairs<Rule>> {
if let Some(inner) = rule.next() {
Some(inner.into_inner())
} else { None }
}

View File

@@ -47,7 +47,7 @@ commandLine = { "command" ~ command }
commandBlock = { "command" ~ "{" ~ command* ~ "}" }
command = { commandName ~ mcArg+ ~ ";" }
commandName = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
commandName = @{ ASCII_ALPHA+ }
mcArg = _{ nbtBlock | string | mcPrimitive }
nbtBlock = { "{" ~ (nbtBlock | string | !("}" | "{") ~ ANY)* ~ "}" }
mcPrimitive = @{ (!(";" | "{" | "}" | "\"" | WHITESPACE) ~ ANY)+ }

View File

@@ -1,6 +1,3 @@
mod command_validator;
pub use command_validator::*;
use std::path::PathBuf;
pub const FILE_EXTENSION: &str = "mg";

View File

@@ -1,221 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommandTree {
pub root: CommandNode,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum CommandNode {
Root {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
},
Literal {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
},
Argument {
name: String,
#[serde(default)]
executable: bool,
#[serde(default)]
redirects: Vec<String>,
#[serde(default)]
children: Vec<CommandNode>,
parser: ParserInfo,
},
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ParserInfo {
pub parser: String,
pub modifier: Option<serde_json::Value>,
}
impl CommandNode {
pub fn name(&self) -> &str {
match self {
CommandNode::Root { name, .. } => name,
CommandNode::Literal { name, .. } => name,
CommandNode::Argument { name, .. } => name,
}
}
pub fn is_executable(&self) -> bool {
match self {
CommandNode::Root { executable, .. } => *executable,
CommandNode::Literal { executable, .. } => *executable,
CommandNode::Argument { executable, .. } => *executable,
}
}
pub fn children(&self) -> &[CommandNode] {
match self {
CommandNode::Root { children, .. } => children,
CommandNode::Literal { children, .. } => children,
CommandNode::Argument { children, .. } => children,
}
}
pub fn redirects(&self) -> &[String] {
match self {
CommandNode::Root { redirects, .. } => redirects,
CommandNode::Literal { redirects, .. } => redirects,
CommandNode::Argument { redirects, .. } => redirects,
}
}
pub fn parser(&self) -> Option<&ParserInfo> {
match self {
CommandNode::Argument { parser, .. } => Some(parser),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub enum ParserType {
Bool,
Double { min: Option<f64>, max: Option<f64> },
Float { min: Option<f64>, max: Option<f64> },
Integer { min: Option<f64>, max: Option<f64> },
Long { min: Option<f64>, max: Option<f64> },
String { kind: StringKind },
Entity { amount: EntityAmount, entity_type: EntityType },
ScoreHolder { amount: ScoreHolderAmount },
GameProfile,
BlockPos,
ColumnPos,
Vec3,
Vec2,
BlockState,
BlockPredicate,
ItemStack,
ItemPredicate,
Color,
Component,
Message,
Nbt,
NbtTag,
NbtPath,
Objective,
ObjectiveCriteria,
Operation,
Particle,
Angle,
Rotation,
ScoreboardSlot,
Swizzle,
Team,
ItemSlot,
ResourceLocation { registry: Option<String> },
Function,
EntityAnchor,
IntRange,
FloatRange,
Dimension,
Gamemode,
Time,
ResourceOrTag { registry: Option<String> },
Resource { registry: Option<String> },
TemplateMirror,
TemplateRotation,
Uuid,
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StringKind {
SingleWord,
QuotablePhrase,
GreedyPhrase,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityAmount {
Single,
Multiple,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityType {
Players,
Entities,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScoreHolderAmount {
Single,
Multiple,
}
impl ParserType {
pub fn from_parser_info(info: &ParserInfo) -> Self {
match info.parser.as_str() {
"brigadier:bool" => ParserType::Bool,
"brigadier:double" => ParserType::Double { min: None, max: None },
"brigadier:float" => ParserType::Float { min: None, max: None },
"brigadier:integer" => ParserType::Integer { min: None, max: None },
"brigadier:long" => ParserType::Long { min: None, max: None },
"brigadier:string" => ParserType::String { kind: StringKind::SingleWord },
"minecraft:entity" => ParserType::Entity {
amount: EntityAmount::Multiple,
entity_type: EntityType::Entities,
},
"minecraft:score_holder" => ParserType::ScoreHolder {
amount: ScoreHolderAmount::Multiple,
},
"minecraft:game_profile" => ParserType::GameProfile,
"minecraft:block_pos" => ParserType::BlockPos,
"minecraft:column_pos" => ParserType::ColumnPos,
"minecraft:vec3" => ParserType::Vec3,
"minecraft:vec2" => ParserType::Vec2,
"minecraft:block_state" => ParserType::BlockState,
"minecraft:block_predicate" => ParserType::BlockPredicate,
"minecraft:item_stack" => ParserType::ItemStack,
"minecraft:item_predicate" => ParserType::ItemPredicate,
"minecraft:color" => ParserType::Color,
"minecraft:component" => ParserType::Component,
"minecraft:message" => ParserType::Message,
"minecraft:nbt_compound_tag" => ParserType::Nbt,
"minecraft:nbt_tag" => ParserType::NbtTag,
"minecraft:nbt_path" => ParserType::NbtPath,
"minecraft:objective" => ParserType::Objective,
"minecraft:objective_criteria" => ParserType::ObjectiveCriteria,
"minecraft:operation" => ParserType::Operation,
"minecraft:particle" => ParserType::Particle,
"minecraft:angle" => ParserType::Angle,
"minecraft:rotation" => ParserType::Rotation,
"minecraft:scoreboard_slot" => ParserType::ScoreboardSlot,
"minecraft:swizzle" => ParserType::Swizzle,
"minecraft:team" => ParserType::Team,
"minecraft:item_slot" => ParserType::ItemSlot,
"minecraft:resource_location" => ParserType::ResourceLocation { registry: None },
"minecraft:function" => ParserType::Function,
"minecraft:entity_anchor" => ParserType::EntityAnchor,
"minecraft:int_range" => ParserType::IntRange,
"minecraft:float_range" => ParserType::FloatRange,
"minecraft:dimension" => ParserType::Dimension,
"minecraft:gamemode" => ParserType::Gamemode,
"minecraft:time" => ParserType::Time,
"minecraft:resource_or_tag" => ParserType::ResourceOrTag { registry: None },
"minecraft:resource" => ParserType::Resource { registry: None },
"minecraft:template_mirror" => ParserType::TemplateMirror,
"minecraft:template_rotation" => ParserType::TemplateRotation,
"minecraft:uuid" => ParserType::Uuid,
_ => ParserType::Unknown(info.parser.clone()),
}
}
}

View File

@@ -18,6 +18,10 @@ impl McFunctionFile {
self.content.push(line);
}
pub fn add_lines(&mut self, lines: Vec<String>) {
self.content.extend(lines);
}
pub fn lines(&self) -> &Vec<String> {
&self.content
}

View File

@@ -1,6 +1,5 @@
mod magma_project_config;
mod mcfunction_file;
pub(crate) mod mc_command;
pub use magma_project_config::*;
pub(crate) use mcfunction_file::*;