From 2829316abeaa9b1f4deb13296b518139613635bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:08:12 +0200 Subject: [PATCH] implement return in while --- Cargo.toml | 11 ++-- src/datapack/command/mod.rs | 122 +++++++++++++++++++++++++++++------- src/lib.rs | 2 +- src/virtual_fs.rs | 2 +- 4 files changed, 109 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2f9cb4d..eaaec5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,15 +21,18 @@ zip = ["dep:zip"] [dependencies] chksum-md5 = "0.1.0" -getset = "0.1.5" +getset = "0.1.6" serde = { version = "1.0.219", optional = true, features = ["derive"] } -serde_json = "1.0.140" +serde_json = "1.0.143" tracing = "0.1.41" -zip = { version = "4.0.0", default-features = false, features = ["deflate", "time"], optional = true } +zip = { version = "5.1.0", default-features = false, features = ["deflate", "time"], optional = true } [dev-dependencies] -tempfile = "3.20.0" +tempfile = "3.22.0" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.cargo-feature-combinations] +exclude_features = [ "default" ] \ No newline at end of file diff --git a/src/datapack/command/mod.rs b/src/datapack/command/mod.rs index 7cde20e..001900d 100644 --- a/src/datapack/command/mod.rs +++ b/src/datapack/command/mod.rs @@ -334,7 +334,6 @@ impl Group { let (prepare_data_storage, function_invocation) = get_group_invocation_commands( &function_path, namespace, - commands, &contained_macros, block_pass_macros, macro_data_storage_name, @@ -570,7 +569,6 @@ impl Hash for Group { fn get_group_invocation_commands( function_path: &str, namespace: &str, - commands: &[Command], contained_macros: &HashSet<&str>, block_pass_macros: Option<&HashSet>, macro_data_storage_name: Option<&str>, @@ -579,25 +577,27 @@ fn get_group_invocation_commands( let mut function_invocation = format!("function {namespace}:{function_path}"); - if commands.contains_macros() { + if contained_macros.is_empty() { + (None, Command::Raw(function_invocation)) + } else { // 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 mut macros_parts = contained_macros .iter() .filter(|&&m| block_pass_macros.is_none_or(|b| !b.contains(m))) - .flat_map(|&m| { - vec![ + .map(|&m| { + [ MacroStringPart::String(format!(r#"{m}:""#)), MacroStringPart::MacroUsage(m.to_string()), MacroStringPart::String(r#"""#.to_string()), ] }) - .fold(Vec::new(), |mut acc, part| { + .fold(Vec::new(), |mut acc, parts| { if !acc.is_empty() { acc.push(MacroStringPart::String(",".to_string())); } - acc.push(part); + acc.extend(parts); acc }); @@ -632,8 +632,6 @@ fn get_group_invocation_commands( (None, Command::UsesMacro(macro_string)) } - } else { - (None, Command::Raw(function_invocation)) } } @@ -777,16 +775,11 @@ impl While { for cmd in &self.commands { macros.extend(cmd.get_macros()); } + macros.extend(self.condition.get_macros()); macros }; - let (prepare_data_storage, function_invocation) = get_group_invocation_commands( - &function_path, - namespace, - &self.commands, - &contained_macros, - None, - None, - ); + let (prepare_data_storage, function_invocation) = + get_group_invocation_commands(&function_path, namespace, &contained_macros, None, None); let execute_tail = if let Some(prepare_datastorage_cmd) = prepare_data_storage { Execute::Runs(vec![prepare_datastorage_cmd, function_invocation]) @@ -807,20 +800,105 @@ impl While { let contains_return = commands.contains_return(); if contains_return { - todo!("While loops with return commands are not yet supported"); - } + let full_path = format!( + "{namespace}:{function_path}", + namespace = function_state.namespace() + ); + let return_data_path = md5::hash(&full_path).to_hex_lowercase(); - conditional_run_cmd.compile(options, global_state, function_state) + let mut pre_cmds = Command::Raw(format!( + "data remove storage shulkerbox:return {return_data_path}" + )) + .compile(options, global_state, function_state) + .into_iter() + .map(|c| c.with_forbid_prefix(true)) + .collect::>(); + let post_condition = Condition::Atom( + format!("data storage shulkerbox:return {return_data_path}").into(), + ); + + let post_cmd_store = global_state + .read() + .unwrap() + .functions_with_special_return + .get(&format!( + "{}:{}", + function_state.namespace(), + function_state.path() + )) + .cloned().map(|parent_return_data_path| { + Command::Execute(Execute::If( + post_condition.clone(), + Box::new(Execute::Run(Box::new(Command::Raw(format!( + "data modify storage shulkerbox:return {parent_return_data_path} set from storage shulkerbox:return {return_data_path}" + ))))), + None, + )) + }); + + let post_cmd_return = Command::Execute(Execute::If( + post_condition, + Box::new(Execute::Run(Box::new(Command::Raw(format!( + "return run data get storage shulkerbox:return {return_data_path}" + ))))), + None, + )); + + let post_cmds = post_cmd_store + .into_iter() + .chain(std::iter::once(post_cmd_return)) + .flat_map(|cmd| cmd.compile(options, global_state, function_state)) + .map(|c| c.with_forbid_prefix(true)) + .collect::>(); + + global_state + .write() + .unwrap() + .functions_with_special_return + .insert(full_path, return_data_path); + + pre_cmds.extend(conditional_run_cmd.compile(options, global_state, function_state)); + pre_cmds.extend(post_cmds); + pre_cmds + } else { + conditional_run_cmd.compile(options, global_state, function_state) + } } #[must_use] pub fn get_count(&self, options: &CompileOptions) -> usize { - Execute::If( + let if_count = Execute::If( self.condition.clone(), Box::new(Execute::Run(Box::new(Command::Raw(String::new())))), None, ) - .get_count(options) + .get_count(options); + + let additional_return_cmds = if self.contains_return() { + let post_cmd_store = Command::Execute(Execute::If( + Condition::Atom("".into()), + Box::new(Execute::Run(Box::new(Command::Raw(String::new())))), + None, + )) + .get_count(options); + + let post_cmd_return = Command::Execute(Execute::If( + Condition::Atom("".into()), + Box::new(Execute::Run(Box::new(Command::Raw(String::new())))), + None, + )) + .get_count(options); + + let post_cmds = post_cmd_store + post_cmd_return; + + Some(post_cmds + 1) + } else { + None + }; + + additional_return_cmds.map_or(if_count, |additional_return_cmds| { + additional_return_cmds + if_count + }) } #[must_use] diff --git a/src/lib.rs b/src/lib.rs index c54ee34..c4db1ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ )] #![warn(clippy::all, clippy::pedantic, clippy::perf)] #![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod datapack; pub mod util; diff --git a/src/virtual_fs.rs b/src/virtual_fs.rs index ec29335..7165362 100644 --- a/src/virtual_fs.rs +++ b/src/virtual_fs.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -#[cfg(feature = "zip")] +#[cfg(all(feature = "fs_access", feature = "zip"))] use zip::ZipWriter; /// Folder representation in virtual file system