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] | ## [Unreleased] | ||||||
| 
 | 
 | ||||||
| ### Added | ### Added | ||||||
| - support for commands using macros |  | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| - use "return" command for conditionals instead of data storage when using supported pack format | - use "return" command for conditionals instead of data storage when using supported pack format | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ use std::{ | ||||||
| use crate::{ | use crate::{ | ||||||
|     prelude::Command, |     prelude::Command, | ||||||
|     util::{ |     util::{ | ||||||
|         compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, |         compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||||
|         MacroString, |         MacroString, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
|  | @ -16,25 +16,22 @@ use super::Execute; | ||||||
| 
 | 
 | ||||||
| /// Compile an if condition command.
 | /// Compile an if condition command.
 | ||||||
| /// The first tuple element is a boolean indicating if the prefix should be used for that 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)] | #[tracing::instrument(skip_all)] | ||||||
| pub fn compile_if_cond( | pub fn compile_if_cond( | ||||||
|     cond: &Condition, |     cond: &Condition, | ||||||
|     then: &Execute, |     then: &Execute, | ||||||
|     el: Option<&Execute>, |     el: Option<&Execute>, | ||||||
|     prefix: &str, |     prefix: &str, | ||||||
|     prefix_contains_macros: bool, |  | ||||||
|     options: &CompileOptions, |     options: &CompileOptions, | ||||||
|     global_state: &MutCompilerState, |     global_state: &MutCompilerState, | ||||||
|     function_state: &FunctionCompilerState, |     function_state: &FunctionCompilerState, | ||||||
| ) -> Vec<CompiledCommand> { | ) -> Vec<(bool, String)> { | ||||||
|     if options.pack_format < 20 { |     if options.pack_format < 20 { | ||||||
|         compile_pre_20_format( |         compile_pre_20_format( | ||||||
|             cond, |             cond, | ||||||
|             then, |             then, | ||||||
|             el, |             el, | ||||||
|             prefix, |             prefix, | ||||||
|             prefix_contains_macros, |  | ||||||
|             options, |             options, | ||||||
|             global_state, |             global_state, | ||||||
|             function_state, |             function_state, | ||||||
|  | @ -45,7 +42,6 @@ pub fn compile_if_cond( | ||||||
|             then, |             then, | ||||||
|             el, |             el, | ||||||
|             prefix, |             prefix, | ||||||
|             prefix_contains_macros, |  | ||||||
|             options, |             options, | ||||||
|             global_state, |             global_state, | ||||||
|             function_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( | fn compile_pre_20_format( | ||||||
|     cond: &Condition, |     cond: &Condition, | ||||||
|     then: &Execute, |     then: &Execute, | ||||||
|     el: Option<&Execute>, |     el: Option<&Execute>, | ||||||
|     prefix: &str, |     prefix: &str, | ||||||
|     prefix_contains_macros: bool, |  | ||||||
|     options: &CompileOptions, |     options: &CompileOptions, | ||||||
|     global_state: &MutCompilerState, |     global_state: &MutCompilerState, | ||||||
|     function_state: &FunctionCompilerState, |     function_state: &FunctionCompilerState, | ||||||
| ) -> Vec<CompiledCommand> { | ) -> Vec<(bool, String)> { | ||||||
|     let contains_macro = prefix_contains_macros || cond.contains_macro(); |  | ||||||
|     let then_count = then.get_count(options); |     let then_count = then.get_count(options); | ||||||
| 
 | 
 | ||||||
|     let str_cond = cond.clone().compile(options, global_state, function_state); |     let str_cond = cond.clone().compile(options, global_state, function_state); | ||||||
|  | @ -99,21 +93,14 @@ fn compile_pre_20_format( | ||||||
|             .iter() |             .iter() | ||||||
|             .map(|s| { |             .map(|s| { | ||||||
|                 if allows_prefix { |                 if allows_prefix { | ||||||
|                     s.clone().apply_prefix("run ") |                     (true, "run ".to_string() + s) | ||||||
|                 } else { |                 } else { | ||||||
|                     s.clone() |                     (false, s.clone()) | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|             .collect() |             .collect() | ||||||
|     } else { |     } else { | ||||||
|         then.compile_internal( |         then.compile_internal(String::new(), false, options, global_state, function_state) | ||||||
|             String::new(), |  | ||||||
|             false, |  | ||||||
|             contains_macro, |  | ||||||
|             options, |  | ||||||
|             global_state, |  | ||||||
|             function_state, |  | ||||||
|         ) |  | ||||||
|     }; |     }; | ||||||
|     // if the conditions have multiple parts joined by a disjunction, commands need to be grouped
 |     // if the conditions have multiple parts joined by a disjunction, commands need to be grouped
 | ||||||
|     let each_or_cmd = (str_cond.len() > 1).then(|| { |     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"), |             format!("data modify storage shulkerbox:cond {success_uid} set value true"), | ||||||
|             combine_conditions_commands( |             combine_conditions_commands( | ||||||
|                 str_cond.clone(), |                 str_cond.clone(), | ||||||
|                 &[CompiledCommand::new(format!( |                 &[( | ||||||
|                     "run data modify storage shulkerbox:cond {success_uid} set value true" |                     true, | ||||||
|                 )) |                     format!("run data modify storage shulkerbox:cond {success_uid} set value true"), | ||||||
|                 .or_contains_macros(contains_macro)], |                 )], | ||||||
|             ), |             ), | ||||||
|         ) |         ) | ||||||
|     }); |     }); | ||||||
|  | @ -161,7 +148,6 @@ fn compile_pre_20_format( | ||||||
|             let el = el.compile_internal( |             let el = el.compile_internal( | ||||||
|                 String::new(), |                 String::new(), | ||||||
|                 else_cond.len() > 1, |                 else_cond.len() > 1, | ||||||
|                 contains_macro, |  | ||||||
|                 options, |                 options, | ||||||
|                 global_state, |                 global_state, | ||||||
|                 function_state, |                 function_state, | ||||||
|  | @ -176,10 +162,10 @@ fn compile_pre_20_format( | ||||||
|             tracing::error!("No success_uid found for each_or_cmd, using default"); |             tracing::error!("No success_uid found for each_or_cmd, using default"); | ||||||
|             "if_success" |             "if_success" | ||||||
|         }); |         }); | ||||||
|         Some( |         Some(( | ||||||
|             CompiledCommand::new(format!("data remove storage shulkerbox:cond {success_uid}")) |             false, | ||||||
|                 .with_forbid_prefix(true), |             format!("data remove storage shulkerbox:cond {success_uid}"), | ||||||
|         ) |         )) | ||||||
|     } else { |     } else { | ||||||
|         None |         None | ||||||
|     }; |     }; | ||||||
|  | @ -192,22 +178,26 @@ fn compile_pre_20_format( | ||||||
|         .chain(then_commands) |         .chain(then_commands) | ||||||
|         .chain(el_commands) |         .chain(el_commands) | ||||||
|         .chain(reset_success_storage) |         .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() |         .collect() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[expect(clippy::too_many_arguments)] |  | ||||||
| fn compile_since_20_format( | fn compile_since_20_format( | ||||||
|     cond: &Condition, |     cond: &Condition, | ||||||
|     then: &Execute, |     then: &Execute, | ||||||
|     el: Option<&Execute>, |     el: Option<&Execute>, | ||||||
|     prefix: &str, |     prefix: &str, | ||||||
|     prefix_contains_macros: bool, |  | ||||||
|     options: &CompileOptions, |     options: &CompileOptions, | ||||||
|     global_state: &MutCompilerState, |     global_state: &MutCompilerState, | ||||||
|     function_state: &FunctionCompilerState, |     function_state: &FunctionCompilerState, | ||||||
| ) -> Vec<CompiledCommand> { | ) -> Vec<(bool, String)> { | ||||||
|     let contains_macros = prefix_contains_macros || cond.contains_macro(); |  | ||||||
|     let then_count = then.get_count(options); |     let then_count = then.get_count(options); | ||||||
| 
 | 
 | ||||||
|     let str_cond = cond |     let str_cond = cond | ||||||
|  | @ -226,14 +216,12 @@ fn compile_since_20_format( | ||||||
|             function_state, |             function_state, | ||||||
|         ); |         ); | ||||||
|         let group = Command::Group(group_cmds); |         let group = Command::Group(group_cmds); | ||||||
|         let cmds = group.compile(options, global_state, function_state); |         let allows_prefix = !group.forbid_prefix(); | ||||||
|         if contains_macros { |         group | ||||||
|             cmds.into_iter() |             .compile(options, global_state, function_state) | ||||||
|                 .map(|cmd| cmd.or_contains_macros(true)) |             .into_iter() | ||||||
|  |             .map(|s| (allows_prefix, s)) | ||||||
|             .collect() |             .collect() | ||||||
|         } else { |  | ||||||
|             cmds |  | ||||||
|         } |  | ||||||
|     } else if then_count > 1 { |     } else if then_count > 1 { | ||||||
|         let then_cmd = match then.clone() { |         let then_cmd = match then.clone() { | ||||||
|             Execute::Run(cmd) => vec![*cmd], |             Execute::Run(cmd) => vec![*cmd], | ||||||
|  | @ -251,30 +239,35 @@ fn compile_since_20_format( | ||||||
|         }; |         }; | ||||||
|         combine_conditions_commands_concat(str_cond, &then_cmd) |         combine_conditions_commands_concat(str_cond, &then_cmd) | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .flat_map(|cmd| { |             .map(|cmd| { | ||||||
|                 cmd.compile(options, global_state, function_state) |                 ( | ||||||
|                     .into_iter() |                     cmd.forbid_prefix(), | ||||||
|                     .map(move |compiled_cmd| { |                     cmd.compile(options, global_state, function_state), | ||||||
|                         compiled_cmd |                 ) | ||||||
|                             .apply_prefix(prefix) |  | ||||||
|                             .or_contains_macros(contains_macros) |  | ||||||
|             }) |             }) | ||||||
|  |             .flat_map(|(forbid_prefix, cmds)| { | ||||||
|  |                 cmds.into_iter() | ||||||
|  |                     .map(move |cmd| (!forbid_prefix, prefix.to_string() + &cmd)) | ||||||
|             }) |             }) | ||||||
|             .collect() |             .collect() | ||||||
|     } else { |     } else { | ||||||
|         str_cond |         combine_conditions_commands_concat( | ||||||
|             .into_iter() |             str_cond, | ||||||
|             .flat_map(|cond| { |             &Command::Concat( | ||||||
|                 then.compile_internal( |                 Box::new(Command::Raw("run ".to_string())), | ||||||
|                     String::new(), |                 Box::new(Command::Execute(then.clone())), | ||||||
|                     false, |             ), | ||||||
|                     contains_macros, |  | ||||||
|                     options, |  | ||||||
|                     global_state, |  | ||||||
|                     function_state, |  | ||||||
|         ) |         ) | ||||||
|         .into_iter() |         .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() |         .collect() | ||||||
|     } |     } | ||||||
|  | @ -282,15 +275,19 @@ fn compile_since_20_format( | ||||||
| 
 | 
 | ||||||
| fn combine_conditions_commands( | fn combine_conditions_commands( | ||||||
|     conditions: Vec<String>, |     conditions: Vec<String>, | ||||||
|     commands: &[CompiledCommand], |     commands: &[(bool, String)], | ||||||
| ) -> Vec<CompiledCommand> { | ) -> Vec<(bool, String)> { | ||||||
|     conditions |     conditions | ||||||
|         .into_iter() |         .into_iter() | ||||||
|         .flat_map(|cond| { |         .flat_map(|cond| { | ||||||
|             let prefix = cond + " "; |             commands.iter().map(move |(use_prefix, cmd)| { | ||||||
|             commands.iter().map(move |cmd| { |  | ||||||
|                 // combine the condition with the command if it uses a prefix
 |                 // 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() |         .collect() | ||||||
|  | @ -660,21 +657,18 @@ mod tests { | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .map(str::to_string) |             .map(str::to_string) | ||||||
|             .collect(); |             .collect(); | ||||||
|         let commands = &[ |         let commands = &[(true, "1".to_string()), (false, "2".to_string())]; | ||||||
|             CompiledCommand::new("1".to_string()), |  | ||||||
|             CompiledCommand::new("2".to_string()).with_forbid_prefix(true), |  | ||||||
|         ]; |  | ||||||
| 
 | 
 | ||||||
|         let combined = combine_conditions_commands(conditions, commands); |         let combined = combine_conditions_commands(conditions, commands); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             combined, |             combined, | ||||||
|             vec![ |             vec![ | ||||||
|                 CompiledCommand::new("a 1".to_string()), |                 (true, "a 1".to_string()), | ||||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true), |                 (false, "2".to_string()), | ||||||
|                 CompiledCommand::new("b 1".to_string()), |                 (true, "b 1".to_string()), | ||||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true), |                 (false, "2".to_string()), | ||||||
|                 CompiledCommand::new("c 1".to_string()), |                 (true, "c 1".to_string()), | ||||||
|                 CompiledCommand::new("2".to_string()).with_forbid_prefix(true) |                 (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 super::Command; | ||||||
| use crate::util::{ | use crate::util::{ | ||||||
|     compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, |     compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||||
|     ExtendableQueue, MacroString, |     ExtendableQueue, MacroString, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -39,38 +39,35 @@ impl Execute { | ||||||
|         options: &CompileOptions, |         options: &CompileOptions, | ||||||
|         global_state: &MutCompilerState, |         global_state: &MutCompilerState, | ||||||
|         function_state: &FunctionCompilerState, |         function_state: &FunctionCompilerState, | ||||||
|     ) -> Vec<CompiledCommand> { |     ) -> Vec<String> { | ||||||
|         // Directly compile the command if it is a run command, skipping the execute part
 |         // Directly compile the command if it is a run command, skipping the execute part
 | ||||||
|         // Otherwise, compile the execute command using internal function
 |         // Otherwise, compile the execute command using internal function
 | ||||||
|         match self { |         if let Self::Run(cmd) = self { | ||||||
|             Self::Run(cmd) => cmd.compile(options, global_state, function_state), |             cmd.compile(options, global_state, function_state) | ||||||
|             Self::Runs(cmds) => cmds |         } else { | ||||||
|                 .iter() |             self.compile_internal( | ||||||
|                 .flat_map(|c| c.compile(options, global_state, function_state)) |  | ||||||
|                 .collect(), |  | ||||||
|             _ => self.compile_internal( |  | ||||||
|                 String::from("execute "), |                 String::from("execute "), | ||||||
|                 false, |                 false, | ||||||
|                 false, |  | ||||||
|                 options, |                 options, | ||||||
|                 global_state, |                 global_state, | ||||||
|                 function_state, |                 function_state, | ||||||
|             ), |             ) | ||||||
|  |             .into_iter() | ||||||
|  |             .map(|(_, cmd)| cmd) | ||||||
|  |             .collect() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Compile the execute command into strings with the given prefix.
 |     /// 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.
 |     /// 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( |     fn compile_internal( | ||||||
|         &self, |         &self, | ||||||
|         prefix: String, |         prefix: String, | ||||||
|         require_grouping: bool, |         require_grouping: bool, | ||||||
|         prefix_contains_macros: bool, |  | ||||||
|         options: &CompileOptions, |         options: &CompileOptions, | ||||||
|         global_state: &MutCompilerState, |         global_state: &MutCompilerState, | ||||||
|         function_state: &FunctionCompilerState, |         function_state: &FunctionCompilerState, | ||||||
|     ) -> Vec<CompiledCommand> { |     ) -> Vec<(bool, String)> { | ||||||
|         match self { |         match self { | ||||||
|             Self::Align(arg, next) |             Self::Align(arg, next) | ||||||
|             | Self::Anchored(arg, next) |             | Self::Anchored(arg, next) | ||||||
|  | @ -81,14 +78,14 @@ impl Execute { | ||||||
|             | Self::On(arg, next) |             | Self::On(arg, next) | ||||||
|             | Self::Positioned(arg, next) |             | Self::Positioned(arg, next) | ||||||
|             | Self::Rotated(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!( |                 format!( | ||||||
|                     "{prefix}{op} {arg} ", |                     "{prefix}{op} {arg} ", | ||||||
|                     op = self.variant_name(), |                     op = self.variant_name(), | ||||||
|                     arg = arg.compile() |                     arg = arg.compile() | ||||||
|                 ), |                 ), | ||||||
|                 require_grouping, |                 require_grouping, | ||||||
|                 prefix_contains_macros || arg.contains_macro(), |  | ||||||
|                 options, |                 options, | ||||||
|                 global_state, |                 global_state, | ||||||
|                 function_state, |                 function_state, | ||||||
|  | @ -99,7 +96,6 @@ impl Execute { | ||||||
|                     selector = selector.compile() |                     selector = selector.compile() | ||||||
|                 ), |                 ), | ||||||
|                 require_grouping, |                 require_grouping, | ||||||
|                 prefix_contains_macros || selector.contains_macro(), |  | ||||||
|                 options, |                 options, | ||||||
|                 global_state, |                 global_state, | ||||||
|                 function_state, |                 function_state, | ||||||
|  | @ -109,28 +105,14 @@ impl Execute { | ||||||
|                 then.as_ref(), |                 then.as_ref(), | ||||||
|                 el.as_deref(), |                 el.as_deref(), | ||||||
|                 &prefix, |                 &prefix, | ||||||
|                 prefix_contains_macros, |  | ||||||
|                 options, |                 options, | ||||||
|                 global_state, |                 global_state, | ||||||
|                 function_state, |                 function_state, | ||||||
|             ), |             ), | ||||||
|             Self::Summon(arg, next) => next.compile_internal( |             Self::Run(command) => match &**command { | ||||||
|                 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() { |  | ||||||
|                 Command::Execute(ex) => ex.compile_internal( |                 Command::Execute(ex) => ex.compile_internal( | ||||||
|                     prefix, |                     prefix, | ||||||
|                     require_grouping, |                     require_grouping, | ||||||
|                     prefix_contains_macros, |  | ||||||
|                     options, |                     options, | ||||||
|                     global_state, |                     global_state, | ||||||
|                     function_state, |                     function_state, | ||||||
|  | @ -138,38 +120,25 @@ impl Execute { | ||||||
|                 command => command |                 command => command | ||||||
|                     .compile(options, global_state, function_state) |                     .compile(options, global_state, function_state) | ||||||
|                     .into_iter() |                     .into_iter() | ||||||
|                     .map(|c| { |                     .map(|c| map_run_cmd(command.forbid_prefix(), c, &prefix)) | ||||||
|                         map_run_cmd(command.forbid_prefix(), c, &prefix) |  | ||||||
|                             .or_contains_macros(prefix_contains_macros) |  | ||||||
|                     }) |  | ||||||
|                     .collect(), |                     .collect(), | ||||||
|             }, |             }, | ||||||
|             Self::Runs(commands) if !require_grouping => commands |             Self::Runs(commands) if !require_grouping => commands | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .flat_map(|c| match c { |                 .flat_map(|c| { | ||||||
|                     Command::Execute(ex) => ex.compile_internal( |                     let forbid_prefix = c.forbid_prefix(); | ||||||
|                         prefix.clone(), |                     c.compile(options, global_state, function_state) | ||||||
|                         require_grouping, |                         .into_iter() | ||||||
|                         prefix_contains_macros, |                         .map(move |c| (forbid_prefix, c)) | ||||||
|                         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) |  | ||||||
|                 }) |                 }) | ||||||
|  |                 .map(|(forbid_prefix, c)| map_run_cmd(forbid_prefix, c, &prefix)) | ||||||
|                 .collect(), |                 .collect(), | ||||||
|             Self::Runs(commands) => { |             Self::Runs(commands) => { | ||||||
|                 let group = Command::Group(commands.clone()); |                 let group = Command::Group(commands.clone()); | ||||||
|                 group |                 group | ||||||
|                     .compile(options, global_state, function_state) |                     .compile(options, global_state, function_state) | ||||||
|                     .into_iter() |                     .into_iter() | ||||||
|                     .map(|c| { |                     .map(|c| map_run_cmd(group.forbid_prefix(), c, &prefix)) | ||||||
|                         map_run_cmd(group.forbid_prefix(), c, &prefix) |  | ||||||
|                             .or_contains_macros(prefix_contains_macros) |  | ||||||
|                     }) |  | ||||||
|                     .collect() |                     .collect() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -185,7 +154,6 @@ impl Execute { | ||||||
|         self.compile_internal( |         self.compile_internal( | ||||||
|             String::new(), |             String::new(), | ||||||
|             false, |             false, | ||||||
|             false, |  | ||||||
|             options, |             options, | ||||||
|             &global_state, |             &global_state, | ||||||
|             &function_state, |             &function_state, | ||||||
|  | @ -320,11 +288,12 @@ where | ||||||
| 
 | 
 | ||||||
| /// Combine command parts, respecting if the second part is a comment
 | /// 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
 | /// 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 { | fn map_run_cmd(forbid_prefix: bool, cmd: String, prefix: &str) -> (bool, String) { | ||||||
|     let forbid_prefix = |     if forbid_prefix || cmd.is_empty() || cmd.chars().all(char::is_whitespace) { | ||||||
|         forbid_prefix || cmd.as_str().is_empty() || cmd.as_str().chars().all(char::is_whitespace); |         (false, cmd) | ||||||
|     cmd.or_forbid_prefix(forbid_prefix) |     } else { | ||||||
|         .apply_prefix(prefix.to_string() + "run ") |         (true, prefix.to_string() + "run " + &cmd) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | @ -349,9 +318,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             compiled, |             compiled, | ||||||
|             vec![CompiledCommand::new( |             vec!["execute as @a if block ~ ~-1 ~ minecraft:stone run say hi".to_string()] | ||||||
|                 "execute as @a if block ~ ~-1 ~ minecraft:stone run say hi" |  | ||||||
|             )] |  | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let direct = Execute::Run(Box::new("say direct".into())).compile( |         let direct = Execute::Run(Box::new("say direct".into())).compile( | ||||||
|  | @ -360,6 +327,6 @@ mod tests { | ||||||
|             &FunctionCompilerState::default(), |             &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::{ | use crate::{ | ||||||
|     prelude::Datapack, |     prelude::Datapack, | ||||||
|     util::{ |     util::{ | ||||||
|         compile::{CompileOptions, CompiledCommand, FunctionCompilerState, MutCompilerState}, |         compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, | ||||||
|         MacroString, |         MacroString, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
|  | @ -53,18 +53,14 @@ impl Command { | ||||||
|         options: &CompileOptions, |         options: &CompileOptions, | ||||||
|         global_state: &MutCompilerState, |         global_state: &MutCompilerState, | ||||||
|         function_state: &FunctionCompilerState, |         function_state: &FunctionCompilerState, | ||||||
|     ) -> Vec<CompiledCommand> { |     ) -> Vec<String> { | ||||||
|         match self { |         match self { | ||||||
|             Self::Raw(command) => vec![CompiledCommand::new(command.clone())], |             Self::Raw(command) => vec![command.clone()], | ||||||
|             Self::UsesMacro(command) => { |             Self::UsesMacro(command) => vec![command.compile()], | ||||||
|                 vec![CompiledCommand::new(command.compile()).with_contains_macros(true)] |  | ||||||
|             } |  | ||||||
|             Self::Debug(message) => compile_debug(message, options), |             Self::Debug(message) => compile_debug(message, options), | ||||||
|             Self::Execute(ex) => ex.compile(options, global_state, function_state), |             Self::Execute(ex) => ex.compile(options, global_state, function_state), | ||||||
|             Self::Group(commands) => compile_group(commands, options, global_state, function_state), |             Self::Group(commands) => compile_group(commands, options, global_state, function_state), | ||||||
|             Self::Comment(comment) => { |             Self::Comment(comment) => vec!["#".to_string() + comment], | ||||||
|                 vec![CompiledCommand::new("#".to_string() + comment).with_forbid_prefix(true)] |  | ||||||
|             } |  | ||||||
|             Self::Concat(a, b) => { |             Self::Concat(a, b) => { | ||||||
|                 let a = a.compile(options, global_state, function_state); |                 let a = a.compile(options, global_state, function_state); | ||||||
|                 let b = b.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() { |                             } else if b.is_empty() { | ||||||
|                                 a.clone() |                                 a.clone() | ||||||
|                             } else { |                             } else { | ||||||
|                                 b.clone() |                                 a.clone() + b | ||||||
|                                     .apply_prefix(a.as_str()) |  | ||||||
|                                     .or_forbid_prefix(a.forbids_prefix()) |  | ||||||
|                             } |                             } | ||||||
|                         }) |                         }) | ||||||
|                     }) |                     }) | ||||||
|  | @ -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 { |     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"}}]"#, |             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() |             message.compile() | ||||||
|         ))] |         )] | ||||||
|     } else { |     } else { | ||||||
|         Vec::new() |         Vec::new() | ||||||
|     } |     } | ||||||
|  | @ -192,16 +186,13 @@ fn compile_group( | ||||||
|     options: &CompileOptions, |     options: &CompileOptions, | ||||||
|     global_state: &MutCompilerState, |     global_state: &MutCompilerState, | ||||||
|     function_state: &FunctionCompilerState, |     function_state: &FunctionCompilerState, | ||||||
| ) -> Vec<CompiledCommand> { | ) -> Vec<String> { | ||||||
|     let command_count = commands |     let command_count = commands | ||||||
|         .iter() |         .iter() | ||||||
|         .map(|cmd| cmd.get_count(options)) |         .map(|cmd| cmd.get_count(options)) | ||||||
|         .sum::<usize>(); |         .sum::<usize>(); | ||||||
|     // only create a function if there are more than one command
 |     // only create a function if there are more than one command
 | ||||||
|     match command_count { |     if command_count > 1 { | ||||||
|         0 => Vec::new(), |  | ||||||
|         1 => commands[0].compile(options, global_state, function_state), |  | ||||||
|         _ => { |  | ||||||
|         let uid = function_state.request_uid(); |         let uid = function_state.request_uid(); | ||||||
|         let pass_macros = group_contains_macro(commands); |         let pass_macros = group_contains_macro(commands); | ||||||
| 
 | 
 | ||||||
|  | @ -226,18 +217,20 @@ fn compile_group( | ||||||
|         let mut function_invocation = format!("function {namespace}:{function_path}"); |         let mut function_invocation = format!("function {namespace}:{function_path}"); | ||||||
| 
 | 
 | ||||||
|         if pass_macros { |         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) |             let macros_block = group_get_macros(commands) | ||||||
|                 .into_iter() |                 .into_iter() | ||||||
|                     .map(|m| format!(r#"{m}:"$({m})""#)) |                 .map(|m| format!("{m}:$({m})")) | ||||||
|                 .collect::<Vec<_>>() |                 .collect::<Vec<_>>() | ||||||
|                 .join(","); |                 .join(","); | ||||||
|             function_invocation.push_str(&format!(" {{{macros_block}}}")); |             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!( |         assert_eq!( | ||||||
|             command_a.compile(options, global_state, function_state), |             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_a.get_count(options), 1); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             command_b.compile(options, global_state, function_state), |             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); |         assert_eq!(command_b.get_count(options), 1); | ||||||
|     } |     } | ||||||
|  | @ -415,7 +408,7 @@ mod tests { | ||||||
| 
 | 
 | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             comment.compile(options, global_state, function_state), |             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); |         assert_eq!(comment.get_count(options), 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -68,20 +68,18 @@ impl Function { | ||||||
|                 if c.contains_macro() { |                 if c.contains_macro() { | ||||||
|                     cmds.into_iter() |                     cmds.into_iter() | ||||||
|                         .map(|c| { |                         .map(|c| { | ||||||
|                             if c.contains_macros() { |                             if c.starts_with('#') { | ||||||
|                                 let content = format!("${c}"); |  | ||||||
|                                 c.with_command(content) |  | ||||||
|                             } else { |  | ||||||
|                                 c |                                 c | ||||||
|                             } |  | ||||||
|                             .to_string() |  | ||||||
|                         }) |  | ||||||
|                         .collect::<Vec<_>>() |  | ||||||
|                             } else { |                             } else { | ||||||
|                     cmds.into_iter().map(|c| c.to_string()).collect::<Vec<_>>() |                                 format!("${c}") | ||||||
|                             } |                             } | ||||||
|                         }) |                         }) | ||||||
|             .collect::<Vec<_>>() |                         .collect() | ||||||
|  |                 } else { | ||||||
|  |                     cmds | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             .collect::<Vec<String>>() | ||||||
|             .join("\n"); |             .join("\n"); | ||||||
|         VFile::Text(content) |         VFile::Text(content) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| //! Compile options for the compiler.
 | //! Compile options for the compiler.
 | ||||||
| 
 | 
 | ||||||
| use std::{fmt::Display, ops::Deref, sync::Mutex}; | use std::sync::Mutex; | ||||||
| 
 | 
 | ||||||
| use getset::Getters; | use getset::Getters; | ||||||
| 
 | 
 | ||||||
|  | @ -86,127 +86,3 @@ impl FunctionCompilerState { | ||||||
|         uid |         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