Compare commits
	
		
			No commits in common. "588bc3f46434b9a06deb2030f4fe0c1829a98a59" and "897e85c2d73576c8fadb442df3b91446dea12a20" have entirely different histories.
		
	
	
		
			588bc3f464
			...
			897e85c2d7
		
	
		|  | @ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| ## [Unreleased] | ||||
| 
 | ||||
| ### Added | ||||
| - support for commands using macros | ||||
| 
 | ||||
| ### Changed | ||||
| - use "return" command for conditionals instead of data storage when using supported pack format | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ use std::{ | |||
| use crate::{ | ||||
|     prelude::Command, | ||||
|     util::{ | ||||
|         compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, | ||||
|         compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||
|         MacroString, | ||||
|     }, | ||||
| }; | ||||
|  | @ -16,25 +16,22 @@ use super::Execute; | |||
| 
 | ||||
| /// Compile an if condition command.
 | ||||
| /// The first tuple element is a boolean indicating if the prefix should be used for that command.
 | ||||
| #[expect(clippy::too_many_arguments)] | ||||
| #[tracing::instrument(skip_all)] | ||||
| pub fn compile_if_cond( | ||||
|     cond: &Condition, | ||||
|     then: &Execute, | ||||
|     el: Option<&Execute>, | ||||
|     prefix: &str, | ||||
|     prefix_contains_macros: bool, | ||||
|     options: &CompileOptions, | ||||
|     global_state: &MutCompilerState, | ||||
|     function_state: &FunctionCompilerState, | ||||
| ) -> Vec<CompiledCommand> { | ||||
| ) -> Vec<(bool, String)> { | ||||
|     if options.pack_format < 20 { | ||||
|         compile_pre_20_format( | ||||
|             cond, | ||||
|             then, | ||||
|             el, | ||||
|             prefix, | ||||
|             prefix_contains_macros, | ||||
|             options, | ||||
|             global_state, | ||||
|             function_state, | ||||
|  | @ -45,7 +42,6 @@ pub fn compile_if_cond( | |||
|             then, | ||||
|             el, | ||||
|             prefix, | ||||
|             prefix_contains_macros, | ||||
|             options, | ||||
|             global_state, | ||||
|             function_state, | ||||
|  | @ -53,18 +49,16 @@ pub fn compile_if_cond( | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[expect(clippy::too_many_lines, clippy::too_many_arguments)] | ||||
| #[allow(clippy::too_many_lines)] | ||||
| fn compile_pre_20_format( | ||||
|     cond: &Condition, | ||||
|     then: &Execute, | ||||
|     el: Option<&Execute>, | ||||
|     prefix: &str, | ||||
|     prefix_contains_macros: bool, | ||||
|     options: &CompileOptions, | ||||
|     global_state: &MutCompilerState, | ||||
|     function_state: &FunctionCompilerState, | ||||
| ) -> Vec<CompiledCommand> { | ||||
|     let contains_macro = prefix_contains_macros || cond.contains_macro(); | ||||
| ) -> Vec<(bool, String)> { | ||||
|     let then_count = then.get_count(options); | ||||
| 
 | ||||
|     let str_cond = cond.clone().compile(options, global_state, function_state); | ||||
|  | @ -99,21 +93,14 @@ fn compile_pre_20_format( | |||
|             .iter() | ||||
|             .map(|s| { | ||||
|                 if allows_prefix { | ||||
|                     s.clone().apply_prefix("run ") | ||||
|                     (true, "run ".to_string() + s) | ||||
|                 } else { | ||||
|                     s.clone() | ||||
|                     (false, s.clone()) | ||||
|                 } | ||||
|             }) | ||||
|             .collect() | ||||
|     } else { | ||||
|         then.compile_internal( | ||||
|             String::new(), | ||||
|             false, | ||||
|             contains_macro, | ||||
|             options, | ||||
|             global_state, | ||||
|             function_state, | ||||
|         ) | ||||
|         then.compile_internal(String::new(), false, options, global_state, function_state) | ||||
|     }; | ||||
|     // if the conditions have multiple parts joined by a disjunction, commands need to be grouped
 | ||||
|     let each_or_cmd = (str_cond.len() > 1).then(|| { | ||||
|  | @ -125,10 +112,10 @@ fn compile_pre_20_format( | |||
|             format!("data modify storage shulkerbox:cond {success_uid} set value true"), | ||||
|             combine_conditions_commands( | ||||
|                 str_cond.clone(), | ||||
|                 &[CompiledCommand::new(format!( | ||||
|                     "run data modify storage shulkerbox:cond {success_uid} set value true" | ||||
|                 )) | ||||
|                 .or_contains_macros(contains_macro)], | ||||
|                 &[( | ||||
|                     true, | ||||
|                     format!("run data modify storage shulkerbox:cond {success_uid} set value true"), | ||||
|                 )], | ||||
|             ), | ||||
|         ) | ||||
|     }); | ||||
|  | @ -161,7 +148,6 @@ fn compile_pre_20_format( | |||
|             let el = el.compile_internal( | ||||
|                 String::new(), | ||||
|                 else_cond.len() > 1, | ||||
|                 contains_macro, | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|  | @ -176,10 +162,10 @@ fn compile_pre_20_format( | |||
|             tracing::error!("No success_uid found for each_or_cmd, using default"); | ||||
|             "if_success" | ||||
|         }); | ||||
|         Some( | ||||
|             CompiledCommand::new(format!("data remove storage shulkerbox:cond {success_uid}")) | ||||
|                 .with_forbid_prefix(true), | ||||
|         ) | ||||
|         Some(( | ||||
|             false, | ||||
|             format!("data remove storage shulkerbox:cond {success_uid}"), | ||||
|         )) | ||||
|     } else { | ||||
|         None | ||||
|     }; | ||||
|  | @ -192,22 +178,26 @@ fn compile_pre_20_format( | |||
|         .chain(then_commands) | ||||
|         .chain(el_commands) | ||||
|         .chain(reset_success_storage) | ||||
|         .map(|cmd| cmd.apply_prefix(prefix)) | ||||
|         .map(|(use_prefix, cmd)| { | ||||
|             let cmd = if use_prefix { | ||||
|                 prefix.to_string() + &cmd | ||||
|             } else { | ||||
|                 cmd | ||||
|             }; | ||||
|             (use_prefix, cmd) | ||||
|         }) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| #[expect(clippy::too_many_arguments)] | ||||
| fn compile_since_20_format( | ||||
|     cond: &Condition, | ||||
|     then: &Execute, | ||||
|     el: Option<&Execute>, | ||||
|     prefix: &str, | ||||
|     prefix_contains_macros: bool, | ||||
|     options: &CompileOptions, | ||||
|     global_state: &MutCompilerState, | ||||
|     function_state: &FunctionCompilerState, | ||||
| ) -> Vec<CompiledCommand> { | ||||
|     let contains_macros = prefix_contains_macros || cond.contains_macro(); | ||||
| ) -> Vec<(bool, String)> { | ||||
|     let then_count = then.get_count(options); | ||||
| 
 | ||||
|     let str_cond = cond | ||||
|  | @ -226,14 +216,12 @@ fn compile_since_20_format( | |||
|             function_state, | ||||
|         ); | ||||
|         let group = Command::Group(group_cmds); | ||||
|         let cmds = group.compile(options, global_state, function_state); | ||||
|         if contains_macros { | ||||
|             cmds.into_iter() | ||||
|                 .map(|cmd| cmd.or_contains_macros(true)) | ||||
|         let allows_prefix = !group.forbid_prefix(); | ||||
|         group | ||||
|             .compile(options, global_state, function_state) | ||||
|             .into_iter() | ||||
|             .map(|s| (allows_prefix, s)) | ||||
|             .collect() | ||||
|         } else { | ||||
|             cmds | ||||
|         } | ||||
|     } else if then_count > 1 { | ||||
|         let then_cmd = match then.clone() { | ||||
|             Execute::Run(cmd) => vec![*cmd], | ||||
|  | @ -251,30 +239,35 @@ fn compile_since_20_format( | |||
|         }; | ||||
|         combine_conditions_commands_concat(str_cond, &then_cmd) | ||||
|             .into_iter() | ||||
|             .flat_map(|cmd| { | ||||
|                 cmd.compile(options, global_state, function_state) | ||||
|                     .into_iter() | ||||
|                     .map(move |compiled_cmd| { | ||||
|                         compiled_cmd | ||||
|                             .apply_prefix(prefix) | ||||
|                             .or_contains_macros(contains_macros) | ||||
|             .map(|cmd| { | ||||
|                 ( | ||||
|                     cmd.forbid_prefix(), | ||||
|                     cmd.compile(options, global_state, function_state), | ||||
|                 ) | ||||
|             }) | ||||
|             .flat_map(|(forbid_prefix, cmds)| { | ||||
|                 cmds.into_iter() | ||||
|                     .map(move |cmd| (!forbid_prefix, prefix.to_string() + &cmd)) | ||||
|             }) | ||||
|             .collect() | ||||
|     } else { | ||||
|         str_cond | ||||
|             .into_iter() | ||||
|             .flat_map(|cond| { | ||||
|                 then.compile_internal( | ||||
|                     String::new(), | ||||
|                     false, | ||||
|                     contains_macros, | ||||
|                     options, | ||||
|                     global_state, | ||||
|                     function_state, | ||||
|         combine_conditions_commands_concat( | ||||
|             str_cond, | ||||
|             &Command::Concat( | ||||
|                 Box::new(Command::Raw("run ".to_string())), | ||||
|                 Box::new(Command::Execute(then.clone())), | ||||
|             ), | ||||
|         ) | ||||
|         .into_iter() | ||||
|                 .map(move |cmd| cmd.apply_prefix(prefix.to_string() + &cond.compile() + " ")) | ||||
|         .map(|cmd| { | ||||
|             ( | ||||
|                 cmd.forbid_prefix(), | ||||
|                 cmd.compile(options, global_state, function_state), | ||||
|             ) | ||||
|         }) | ||||
|         .flat_map(|(forbid_prefix, cmds)| { | ||||
|             cmds.into_iter() | ||||
|                 .map(move |cmd| (!forbid_prefix, prefix.to_string() + &cmd)) | ||||
|         }) | ||||
|         .collect() | ||||
|     } | ||||
|  | @ -282,15 +275,19 @@ fn compile_since_20_format( | |||
| 
 | ||||
| fn combine_conditions_commands( | ||||
|     conditions: Vec<String>, | ||||
|     commands: &[CompiledCommand], | ||||
| ) -> Vec<CompiledCommand> { | ||||
|     commands: &[(bool, String)], | ||||
| ) -> Vec<(bool, String)> { | ||||
|     conditions | ||||
|         .into_iter() | ||||
|         .flat_map(|cond| { | ||||
|             let prefix = cond + " "; | ||||
|             commands.iter().map(move |cmd| { | ||||
|             commands.iter().map(move |(use_prefix, cmd)| { | ||||
|                 // combine the condition with the command if it uses a prefix
 | ||||
|                 cmd.clone().apply_prefix(&prefix) | ||||
|                 let cmd = if *use_prefix { | ||||
|                     cond.clone() + " " + cmd | ||||
|                 } else { | ||||
|                     cmd.clone() | ||||
|                 }; | ||||
|                 (*use_prefix, cmd) | ||||
|             }) | ||||
|         }) | ||||
|         .collect() | ||||
|  | @ -660,21 +657,18 @@ mod tests { | |||
|             .into_iter() | ||||
|             .map(str::to_string) | ||||
|             .collect(); | ||||
|         let commands = &[ | ||||
|             CompiledCommand::new("1".to_string()), | ||||
|             CompiledCommand::new("2".to_string()).with_forbid_prefix(true), | ||||
|         ]; | ||||
|         let commands = &[(true, "1".to_string()), (false, "2".to_string())]; | ||||
| 
 | ||||
|         let combined = combine_conditions_commands(conditions, commands); | ||||
|         assert_eq!( | ||||
|             combined, | ||||
|             vec![ | ||||
|                 CompiledCommand::new("a 1".to_string()), | ||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true), | ||||
|                 CompiledCommand::new("b 1".to_string()), | ||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true), | ||||
|                 CompiledCommand::new("c 1".to_string()), | ||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true) | ||||
|                 (true, "a 1".to_string()), | ||||
|                 (false, "2".to_string()), | ||||
|                 (true, "b 1".to_string()), | ||||
|                 (false, "2".to_string()), | ||||
|                 (true, "c 1".to_string()), | ||||
|                 (false, "2".to_string()) | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| use std::{collections::HashSet, ops::RangeInclusive, string::ToString}; | ||||
| use std::{collections::HashSet, ops::RangeInclusive}; | ||||
| 
 | ||||
| use super::Command; | ||||
| use crate::util::{ | ||||
|     compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, | ||||
|     compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||
|     ExtendableQueue, MacroString, | ||||
| }; | ||||
| 
 | ||||
|  | @ -39,38 +39,35 @@ impl Execute { | |||
|         options: &CompileOptions, | ||||
|         global_state: &MutCompilerState, | ||||
|         function_state: &FunctionCompilerState, | ||||
|     ) -> Vec<CompiledCommand> { | ||||
|     ) -> Vec<String> { | ||||
|         // Directly compile the command if it is a run command, skipping the execute part
 | ||||
|         // Otherwise, compile the execute command using internal function
 | ||||
|         match self { | ||||
|             Self::Run(cmd) => cmd.compile(options, global_state, function_state), | ||||
|             Self::Runs(cmds) => cmds | ||||
|                 .iter() | ||||
|                 .flat_map(|c| c.compile(options, global_state, function_state)) | ||||
|                 .collect(), | ||||
|             _ => self.compile_internal( | ||||
|         if let Self::Run(cmd) = self { | ||||
|             cmd.compile(options, global_state, function_state) | ||||
|         } else { | ||||
|             self.compile_internal( | ||||
|                 String::from("execute "), | ||||
|                 false, | ||||
|                 false, | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|             ), | ||||
|             ) | ||||
|             .into_iter() | ||||
|             .map(|(_, cmd)| cmd) | ||||
|             .collect() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Compile the execute command into strings with the given prefix.
 | ||||
|     /// Each first tuple element is a boolean indicating if the prefix should be used for that command.
 | ||||
|     #[expect(clippy::too_many_lines)] | ||||
|     fn compile_internal( | ||||
|         &self, | ||||
|         prefix: String, | ||||
|         require_grouping: bool, | ||||
|         prefix_contains_macros: bool, | ||||
|         options: &CompileOptions, | ||||
|         global_state: &MutCompilerState, | ||||
|         function_state: &FunctionCompilerState, | ||||
|     ) -> Vec<CompiledCommand> { | ||||
|     ) -> Vec<(bool, String)> { | ||||
|         match self { | ||||
|             Self::Align(arg, next) | ||||
|             | Self::Anchored(arg, next) | ||||
|  | @ -81,14 +78,14 @@ impl Execute { | |||
|             | Self::On(arg, next) | ||||
|             | Self::Positioned(arg, next) | ||||
|             | Self::Rotated(arg, next) | ||||
|             | Self::Store(arg, next) => next.compile_internal( | ||||
|             | Self::Store(arg, next) | ||||
|             | Self::Summon(arg, next) => next.compile_internal( | ||||
|                 format!( | ||||
|                     "{prefix}{op} {arg} ", | ||||
|                     op = self.variant_name(), | ||||
|                     arg = arg.compile() | ||||
|                 ), | ||||
|                 require_grouping, | ||||
|                 prefix_contains_macros || arg.contains_macro(), | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|  | @ -99,7 +96,6 @@ impl Execute { | |||
|                     selector = selector.compile() | ||||
|                 ), | ||||
|                 require_grouping, | ||||
|                 prefix_contains_macros || selector.contains_macro(), | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|  | @ -109,28 +105,14 @@ impl Execute { | |||
|                 then.as_ref(), | ||||
|                 el.as_deref(), | ||||
|                 &prefix, | ||||
|                 prefix_contains_macros, | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|             ), | ||||
|             Self::Summon(arg, next) => next.compile_internal( | ||||
|                 format!( | ||||
|                     "{prefix}{op} {arg} ", | ||||
|                     op = self.variant_name(), | ||||
|                     arg = arg.compile() | ||||
|                 ), | ||||
|                 true, | ||||
|                 prefix_contains_macros || arg.contains_macro(), | ||||
|                 options, | ||||
|                 global_state, | ||||
|                 function_state, | ||||
|             ), | ||||
|             Self::Run(command) => match command.as_ref() { | ||||
|             Self::Run(command) => match &**command { | ||||
|                 Command::Execute(ex) => ex.compile_internal( | ||||
|                     prefix, | ||||
|                     require_grouping, | ||||
|                     prefix_contains_macros, | ||||
|                     options, | ||||
|                     global_state, | ||||
|                     function_state, | ||||
|  | @ -138,38 +120,25 @@ impl Execute { | |||
|                 command => command | ||||
|                     .compile(options, global_state, function_state) | ||||
|                     .into_iter() | ||||
|                     .map(|c| { | ||||
|                         map_run_cmd(command.forbid_prefix(), c, &prefix) | ||||
|                             .or_contains_macros(prefix_contains_macros) | ||||
|                     }) | ||||
|                     .map(|c| map_run_cmd(command.forbid_prefix(), c, &prefix)) | ||||
|                     .collect(), | ||||
|             }, | ||||
|             Self::Runs(commands) if !require_grouping => commands | ||||
|                 .iter() | ||||
|                 .flat_map(|c| match c { | ||||
|                     Command::Execute(ex) => ex.compile_internal( | ||||
|                         prefix.clone(), | ||||
|                         require_grouping, | ||||
|                         prefix_contains_macros, | ||||
|                         options, | ||||
|                         global_state, | ||||
|                         function_state, | ||||
|                     ), | ||||
|                     command => command.compile(options, global_state, function_state), | ||||
|                 }) | ||||
|                 .map(|cmd| { | ||||
|                     map_run_cmd(false, cmd, &prefix).or_contains_macros(prefix_contains_macros) | ||||
|                 .flat_map(|c| { | ||||
|                     let forbid_prefix = c.forbid_prefix(); | ||||
|                     c.compile(options, global_state, function_state) | ||||
|                         .into_iter() | ||||
|                         .map(move |c| (forbid_prefix, c)) | ||||
|                 }) | ||||
|                 .map(|(forbid_prefix, c)| map_run_cmd(forbid_prefix, c, &prefix)) | ||||
|                 .collect(), | ||||
|             Self::Runs(commands) => { | ||||
|                 let group = Command::Group(commands.clone()); | ||||
|                 group | ||||
|                     .compile(options, global_state, function_state) | ||||
|                     .into_iter() | ||||
|                     .map(|c| { | ||||
|                         map_run_cmd(group.forbid_prefix(), c, &prefix) | ||||
|                             .or_contains_macros(prefix_contains_macros) | ||||
|                     }) | ||||
|                     .map(|c| map_run_cmd(group.forbid_prefix(), c, &prefix)) | ||||
|                     .collect() | ||||
|             } | ||||
|         } | ||||
|  | @ -185,7 +154,6 @@ impl Execute { | |||
|         self.compile_internal( | ||||
|             String::new(), | ||||
|             false, | ||||
|             false, | ||||
|             options, | ||||
|             &global_state, | ||||
|             &function_state, | ||||
|  | @ -320,11 +288,12 @@ where | |||
| 
 | ||||
| /// Combine command parts, respecting if the second part is a comment
 | ||||
| /// The first tuple element is a boolean indicating if the prefix should be used
 | ||||
| fn map_run_cmd(forbid_prefix: bool, cmd: CompiledCommand, prefix: &str) -> CompiledCommand { | ||||
|     let forbid_prefix = | ||||
|         forbid_prefix || cmd.as_str().is_empty() || cmd.as_str().chars().all(char::is_whitespace); | ||||
|     cmd.or_forbid_prefix(forbid_prefix) | ||||
|         .apply_prefix(prefix.to_string() + "run ") | ||||
| fn map_run_cmd(forbid_prefix: bool, cmd: String, prefix: &str) -> (bool, String) { | ||||
|     if forbid_prefix || cmd.is_empty() || cmd.chars().all(char::is_whitespace) { | ||||
|         (false, cmd) | ||||
|     } else { | ||||
|         (true, prefix.to_string() + "run " + &cmd) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
|  | @ -349,9 +318,7 @@ mod tests { | |||
| 
 | ||||
|         assert_eq!( | ||||
|             compiled, | ||||
|             vec![CompiledCommand::new( | ||||
|                 "execute as @a if block ~ ~-1 ~ minecraft:stone run say hi" | ||||
|             )] | ||||
|             vec!["execute as @a if block ~ ~-1 ~ minecraft:stone run say hi".to_string()] | ||||
|         ); | ||||
| 
 | ||||
|         let direct = Execute::Run(Box::new("say direct".into())).compile( | ||||
|  | @ -360,6 +327,6 @@ mod tests { | |||
|             &FunctionCompilerState::default(), | ||||
|         ); | ||||
| 
 | ||||
|         assert_eq!(direct, vec![CompiledCommand::new("say direct")]); | ||||
|         assert_eq!(direct, vec!["say direct".to_string()]); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ use super::Function; | |||
| use crate::{ | ||||
|     prelude::Datapack, | ||||
|     util::{ | ||||
|         compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, | ||||
|         compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||
|         MacroString, | ||||
|     }, | ||||
| }; | ||||
|  | @ -53,18 +53,14 @@ impl Command { | |||
|         options: &CompileOptions, | ||||
|         global_state: &MutCompilerState, | ||||
|         function_state: &FunctionCompilerState, | ||||
|     ) -> Vec<CompiledCommand> { | ||||
|     ) -> Vec<String> { | ||||
|         match self { | ||||
|             Self::Raw(command) => vec![CompiledCommand::new(command.clone())], | ||||
|             Self::UsesMacro(command) => { | ||||
|                 vec![CompiledCommand::new(command.compile()).with_contains_macros(true)] | ||||
|             } | ||||
|             Self::Raw(command) => vec![command.clone()], | ||||
|             Self::UsesMacro(command) => vec![command.compile()], | ||||
|             Self::Debug(message) => compile_debug(message, options), | ||||
|             Self::Execute(ex) => ex.compile(options, global_state, function_state), | ||||
|             Self::Group(commands) => compile_group(commands, options, global_state, function_state), | ||||
|             Self::Comment(comment) => { | ||||
|                 vec![CompiledCommand::new("#".to_string() + comment).with_forbid_prefix(true)] | ||||
|             } | ||||
|             Self::Comment(comment) => vec!["#".to_string() + comment], | ||||
|             Self::Concat(a, b) => { | ||||
|                 let a = a.compile(options, global_state, function_state); | ||||
|                 let b = b.compile(options, global_state, function_state); | ||||
|  | @ -76,9 +72,7 @@ impl Command { | |||
|                             } else if b.is_empty() { | ||||
|                                 a.clone() | ||||
|                             } else { | ||||
|                                 b.clone() | ||||
|                                     .apply_prefix(a.as_str()) | ||||
|                                     .or_forbid_prefix(a.forbids_prefix()) | ||||
|                                 a.clone() + b | ||||
|                             } | ||||
|                         }) | ||||
|                     }) | ||||
|  | @ -175,12 +169,12 @@ impl From<&mut Function> for Command { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn compile_debug(message: &MacroString, option: &CompileOptions) -> Vec<CompiledCommand> { | ||||
| fn compile_debug(message: &MacroString, option: &CompileOptions) -> Vec<String> { | ||||
|     if option.debug { | ||||
|         vec![CompiledCommand::new(format!( | ||||
|         vec![format!( | ||||
|             r#"tellraw @a [{{"text":"[","color":"dark_blue"}},{{"text":"DEBUG","color":"dark_green","hoverEvent":{{"action":"show_text","value":[{{"text":"Debug message generated by Shulkerbox"}},{{"text":"\nSet debug to 'false' to disable"}}]}}}},{{"text":"] ","color":"dark_blue"}},{{"text":"{}","color":"black"}}]"#, | ||||
|             message.compile() | ||||
|         ))] | ||||
|         )] | ||||
|     } else { | ||||
|         Vec::new() | ||||
|     } | ||||
|  | @ -192,16 +186,13 @@ fn compile_group( | |||
|     options: &CompileOptions, | ||||
|     global_state: &MutCompilerState, | ||||
|     function_state: &FunctionCompilerState, | ||||
| ) -> Vec<CompiledCommand> { | ||||
| ) -> Vec<String> { | ||||
|     let command_count = commands | ||||
|         .iter() | ||||
|         .map(|cmd| cmd.get_count(options)) | ||||
|         .sum::<usize>(); | ||||
|     // only create a function if there are more than one command
 | ||||
|     match command_count { | ||||
|         0 => Vec::new(), | ||||
|         1 => commands[0].compile(options, global_state, function_state), | ||||
|         _ => { | ||||
|     if command_count > 1 { | ||||
|         let uid = function_state.request_uid(); | ||||
|         let pass_macros = group_contains_macro(commands); | ||||
| 
 | ||||
|  | @ -226,18 +217,20 @@ fn compile_group( | |||
|         let mut function_invocation = format!("function {namespace}:{function_path}"); | ||||
| 
 | ||||
|         if pass_macros { | ||||
|                 // WARNING: this seems to be the only way to pass macros to the function called.
 | ||||
|                 // Because everything is passed as a string, it looses one "level" of escaping per pass.
 | ||||
|             let macros_block = group_get_macros(commands) | ||||
|                 .into_iter() | ||||
|                     .map(|m| format!(r#"{m}:"$({m})""#)) | ||||
|                 .map(|m| format!("{m}:$({m})")) | ||||
|                 .collect::<Vec<_>>() | ||||
|                 .join(","); | ||||
|             function_invocation.push_str(&format!(" {{{macros_block}}}")); | ||||
|         } | ||||
| 
 | ||||
|             vec![CompiledCommand::new(function_invocation).with_contains_macros(pass_macros)] | ||||
|         } | ||||
|         vec![function_invocation] | ||||
|     } else { | ||||
|         commands | ||||
|             .iter() | ||||
|             .flat_map(|cmd| cmd.compile(options, global_state, function_state)) | ||||
|             .collect::<Vec<_>>() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -395,12 +388,12 @@ mod tests { | |||
| 
 | ||||
|         assert_eq!( | ||||
|             command_a.compile(options, global_state, function_state), | ||||
|             vec![CompiledCommand::new("say Hello, world!")] | ||||
|             vec!["say Hello, world!".to_string()] | ||||
|         ); | ||||
|         assert_eq!(command_a.get_count(options), 1); | ||||
|         assert_eq!( | ||||
|             command_b.compile(options, global_state, function_state), | ||||
|             vec![CompiledCommand::new("say foo bar")] | ||||
|             vec!["say foo bar".to_string()] | ||||
|         ); | ||||
|         assert_eq!(command_b.get_count(options), 1); | ||||
|     } | ||||
|  | @ -415,7 +408,7 @@ mod tests { | |||
| 
 | ||||
|         assert_eq!( | ||||
|             comment.compile(options, global_state, function_state), | ||||
|             vec![CompiledCommand::new("#this is a comment").with_forbid_prefix(true)] | ||||
|             vec!["#this is a comment".to_string()] | ||||
|         ); | ||||
|         assert_eq!(comment.get_count(options), 0); | ||||
|     } | ||||
|  |  | |||
|  | @ -68,20 +68,18 @@ impl Function { | |||
|                 if c.contains_macro() { | ||||
|                     cmds.into_iter() | ||||
|                         .map(|c| { | ||||
|                             if c.contains_macros() { | ||||
|                                 let content = format!("${c}"); | ||||
|                                 c.with_command(content) | ||||
|                             } else { | ||||
|                             if c.starts_with('#') { | ||||
|                                 c | ||||
|                             } | ||||
|                             .to_string() | ||||
|                         }) | ||||
|                         .collect::<Vec<_>>() | ||||
|                             } else { | ||||
|                     cmds.into_iter().map(|c| c.to_string()).collect::<Vec<_>>() | ||||
|                                 format!("${c}") | ||||
|                             } | ||||
|                         }) | ||||
|             .collect::<Vec<_>>() | ||||
|                         .collect() | ||||
|                 } else { | ||||
|                     cmds | ||||
|                 } | ||||
|             }) | ||||
|             .collect::<Vec<String>>() | ||||
|             .join("\n"); | ||||
|         VFile::Text(content) | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| //! Compile options for the compiler.
 | ||||
| 
 | ||||
| use std::{fmt::Display, ops::Deref, sync::Mutex}; | ||||
| use std::sync::Mutex; | ||||
| 
 | ||||
| use getset::Getters; | ||||
| 
 | ||||
|  | @ -86,127 +86,3 @@ impl FunctionCompilerState { | |||
|         uid | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Compiled command, ready to be written to a function.
 | ||||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||||
| #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] | ||||
| pub struct CompiledCommand { | ||||
|     /// The command string.
 | ||||
|     command: String, | ||||
|     /// Whether the command is not allowed to be prefixed.
 | ||||
|     forbid_prefix: bool, | ||||
|     /// Whether the command contains a macro.
 | ||||
|     contains_macros: bool, | ||||
| } | ||||
| 
 | ||||
| impl CompiledCommand { | ||||
|     /// Create a new compiled command.
 | ||||
|     #[must_use] | ||||
|     pub fn new<S>(command: S) -> Self | ||||
|     where | ||||
|         S: Into<String>, | ||||
|     { | ||||
|         Self { | ||||
|             command: command.into(), | ||||
|             forbid_prefix: false, | ||||
|             contains_macros: false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Get the command string.
 | ||||
|     #[must_use] | ||||
|     pub fn as_str(&self) -> &str { | ||||
|         &self.command | ||||
|     } | ||||
| 
 | ||||
|     /// Set the command string.
 | ||||
|     #[must_use] | ||||
|     pub fn with_command(mut self, command: String) -> Self { | ||||
|         self.command = command; | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Set whether the command is forbidden to be prefixed.
 | ||||
|     #[must_use] | ||||
|     pub fn with_forbid_prefix(mut self, forbid_prefix: bool) -> Self { | ||||
|         self.forbid_prefix = forbid_prefix; | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Set whether the command contains a macro.
 | ||||
|     #[must_use] | ||||
|     pub fn with_contains_macros(mut self, contains_macros: bool) -> Self { | ||||
|         self.contains_macros = contains_macros; | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Get whether the command is forbidden to be prefixed.
 | ||||
|     #[must_use] | ||||
|     pub fn forbids_prefix(&self) -> bool { | ||||
|         self.forbid_prefix | ||||
|     } | ||||
| 
 | ||||
|     /// Get whether the command contains a macro.
 | ||||
|     #[must_use] | ||||
|     pub fn contains_macros(&self) -> bool { | ||||
|         self.contains_macros | ||||
|     } | ||||
| 
 | ||||
|     /// Apply a prefix to the command (if allowed).
 | ||||
|     #[must_use] | ||||
|     pub fn apply_prefix<S>(mut self, prefix: S) -> Self | ||||
|     where | ||||
|         S: Into<String>, | ||||
|     { | ||||
|         if !self.forbid_prefix { | ||||
|             self.command = prefix.into() + &self.command; | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Combine current forbid prefix status with the input.
 | ||||
|     #[must_use] | ||||
|     pub fn or_forbid_prefix(mut self, forbid_prefix: bool) -> Self { | ||||
|         self.forbid_prefix = self.forbid_prefix || forbid_prefix; | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Combine current contains macro status with the input.
 | ||||
|     #[must_use] | ||||
|     pub fn or_contains_macros(mut self, contains_macros: bool) -> Self { | ||||
|         self.contains_macros = self.contains_macros || contains_macros; | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Deref for CompiledCommand { | ||||
|     type Target = String; | ||||
| 
 | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         &self.command | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Display for CompiledCommand { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "{}", self.command) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<CompiledCommand> for String { | ||||
|     fn from(compiled_command: CompiledCommand) -> Self { | ||||
|         compiled_command.command | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<String> for CompiledCommand { | ||||
|     fn from(value: String) -> Self { | ||||
|         Self::new(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<&str> for CompiledCommand { | ||||
|     fn from(value: &str) -> Self { | ||||
|         Self::new(value.to_string()) | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue