Merge branch 'feature/scoreboard_management' into develop
This commit is contained in:
commit
ed62e75e89
|
@ -3,7 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- development
|
- develop
|
||||||
- 'releases/**'
|
- 'releases/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- support for commands using macros
|
- support for commands using macros
|
||||||
|
- support for registering scoreboards (automatic creation and deletion)
|
||||||
|
|
||||||
### 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
|
||||||
|
- update latest datapack format to 61
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -20,12 +20,16 @@ serde = ["dep:serde"]
|
||||||
zip = ["dep:zip"]
|
zip = ["dep:zip"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chksum-md5 = "0.0.0"
|
chksum-md5 = "0.1.0"
|
||||||
getset = "0.1.2"
|
getset = "0.1.5"
|
||||||
serde = { version = "1.0.197", optional = true, features = ["derive"] }
|
serde = { version = "1.0.219", optional = true, features = ["derive"] }
|
||||||
serde_json = "1.0.114"
|
serde_json = "1.0.140"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.41"
|
||||||
zip = { version = "2.1.3", default-features = false, features = ["deflate", "time"], optional = true }
|
zip = { version = "2.6.1", default-features = false, features = ["deflate", "time"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.13.0"
|
tempfile = "3.19.1"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
|
@ -18,7 +18,7 @@ use shulkerbox::{
|
||||||
util::compile::CompileOptions,
|
util::compile::CompileOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut dp = Datapack::new("shulkerpack", 20) // Create a new datapack with the name "shulkerpack" and the pack format 20
|
let mut dp = Datapack::new("shulker", 20) // Create a new datapack with the name "shulkerpack" and the pack format 20
|
||||||
.with_description("I created this datapack with rust") // Add a description to the datapack
|
.with_description("I created this datapack with rust") // Add a description to the datapack
|
||||||
.with_supported_formats(16..=20) // Add the supported formats of the datapack
|
.with_supported_formats(16..=20) // Add the supported formats of the datapack
|
||||||
.with_template_folder(Path::new("./template")) // Add a template folder to the datapack. This will include all files in the template folder in the root of the datapack and can be used for including the "pack.png" file
|
.with_template_folder(Path::new("./template")) // Add a template folder to the datapack. This will include all files in the template folder in the root of the datapack and can be used for including the "pack.png" file
|
||||||
|
|
|
@ -5,10 +5,12 @@ use shulkerbox::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// create a new datapack
|
// create a new datapack
|
||||||
let mut dp = Datapack::new(16).with_supported_formats(16..=20);
|
let mut dp = Datapack::new("example", 16).with_supported_formats(16..=20);
|
||||||
|
|
||||||
// get the namespace "test"
|
dp.register_scoreboard("example_scoreboard", None::<&str>, None::<&str>);
|
||||||
let namespace = dp.namespace_mut("test");
|
|
||||||
|
// get the namespace "example"
|
||||||
|
let namespace = dp.namespace_mut("example");
|
||||||
|
|
||||||
// get the function "foo" of the namespace "test" and add some commands
|
// get the function "foo" of the namespace "test" and add some commands
|
||||||
let foo_function = namespace.function_mut("foo");
|
let foo_function = namespace.function_mut("foo");
|
||||||
|
@ -32,7 +34,7 @@ fn main() {
|
||||||
)),
|
)),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
dp.add_load("test:foo");
|
dp.add_load("example:foo");
|
||||||
|
|
||||||
// compile the datapack
|
// compile the datapack
|
||||||
let v_folder = dp.compile(&CompileOptions::default());
|
let v_folder = dp.compile(&CompileOptions::default());
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
// import the prelude to get all the necessary structs
|
||||||
|
use shulkerbox::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut dp = Datapack::new("main", 20).with_supported_formats(16..=20);
|
||||||
|
|
||||||
|
// get the namespace "test"
|
||||||
|
let namespace = dp.namespace_mut("test");
|
||||||
|
|
||||||
|
// get the function "foo" of the namespace "test" and add some commands
|
||||||
|
let foo_function = namespace.function_mut("foo");
|
||||||
|
|
||||||
|
let ex = Execute::If(
|
||||||
|
Condition::from("entity A"),
|
||||||
|
Box::new(Execute::Run(Box::new("say A".into()))),
|
||||||
|
Some(Box::new(Execute::If(
|
||||||
|
Condition::from("entity B"),
|
||||||
|
Box::new(Execute::Run(Box::new("say B".into()))),
|
||||||
|
Some(Box::new(Execute::Run(Box::new("say C".into())))),
|
||||||
|
))),
|
||||||
|
);
|
||||||
|
foo_function.add_command(ex);
|
||||||
|
|
||||||
|
// compile the datapack
|
||||||
|
let v_folder = dp.compile(&CompileOptions::default());
|
||||||
|
|
||||||
|
// place the compiled datapack in the "./dist" folder
|
||||||
|
v_folder.place(Path::new("./dist")).unwrap();
|
||||||
|
}
|
|
@ -439,7 +439,7 @@ impl Condition {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn to_truth_table(&self) -> Vec<Self> {
|
pub fn to_truth_table(&self) -> Vec<Self> {
|
||||||
match self.normalize() {
|
match self.normalize() {
|
||||||
Self::Atom(_) | Self::Not(_) => vec![self.clone()],
|
Self::Atom(_) | Self::Not(_) => vec![self.normalize()],
|
||||||
Self::Or(a, b) => a
|
Self::Or(a, b) => a
|
||||||
.to_truth_table()
|
.to_truth_table()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -461,7 +461,7 @@ impl Condition {
|
||||||
|
|
||||||
/// Convert the condition into a [`MacroString`].
|
/// Convert the condition into a [`MacroString`].
|
||||||
///
|
///
|
||||||
/// Will fail if the condition contains an `Or` variant. Use `compile` instead.
|
/// Will fail if the condition contains an `Or` or double nested `Not` variant. Use `compile` instead.
|
||||||
fn str_cond(&self) -> Option<MacroString> {
|
fn str_cond(&self) -> Option<MacroString> {
|
||||||
match self {
|
match self {
|
||||||
Self::Atom(s) => Some(MacroString::from("if ") + s.clone()),
|
Self::Atom(s) => Some(MacroString::from("if ") + s.clone()),
|
||||||
|
|
|
@ -234,7 +234,7 @@ impl Execute {
|
||||||
Self::If(_, next, el) => {
|
Self::If(_, next, el) => {
|
||||||
pack_formats.start() >= &4
|
pack_formats.start() >= &4
|
||||||
&& next.validate(pack_formats)
|
&& next.validate(pack_formats)
|
||||||
&& el.as_deref().map_or(true, |el| el.validate(pack_formats))
|
&& el.as_deref().is_none_or(|el| el.validate(pack_formats))
|
||||||
}
|
}
|
||||||
Self::Summon(_, next) | Self::On(_, next) => {
|
Self::Summon(_, next) | Self::On(_, next) => {
|
||||||
pack_formats.start() >= &12 && next.validate(pack_formats)
|
pack_formats.start() >= &12 && next.validate(pack_formats)
|
||||||
|
@ -261,7 +261,7 @@ impl Execute {
|
||||||
Self::If(cond, then, el) => {
|
Self::If(cond, then, el) => {
|
||||||
cond.contains_macro()
|
cond.contains_macro()
|
||||||
|| then.contains_macro()
|
|| then.contains_macro()
|
||||||
|| el.as_deref().map_or(false, Self::contains_macro)
|
|| el.as_deref().is_some_and(Self::contains_macro)
|
||||||
}
|
}
|
||||||
Self::Run(cmd) => cmd.contains_macro(),
|
Self::Run(cmd) => cmd.contains_macro(),
|
||||||
Self::Runs(cmds) => cmds.iter().any(super::Command::contains_macro),
|
Self::Runs(cmds) => cmds.iter().any(super::Command::contains_macro),
|
||||||
|
|
|
@ -363,8 +363,8 @@ fn validate_raw_cmd(cmd: &str, pack_formats: &RangeInclusive<u8>) -> bool {
|
||||||
map
|
map
|
||||||
});
|
});
|
||||||
|
|
||||||
cmd.split_ascii_whitespace().next().map_or(true, |cmd| {
|
cmd.split_ascii_whitespace().next().is_none_or(|cmd| {
|
||||||
cmd_formats.get(cmd).map_or(true, |range| {
|
cmd_formats.get(cmd).is_none_or(|range| {
|
||||||
let start_cmd = range.start();
|
let start_cmd = range.start();
|
||||||
let end_cmd = range.end();
|
let end_cmd = range.end();
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@ pub struct Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub(in crate::datapack) fn new(namespace: &str, name: &str) -> Self {
|
pub(in crate::datapack) fn new(namespace: impl Into<String>, name: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
commands: Vec::new(),
|
commands: Vec::new(),
|
||||||
name: name.to_string(),
|
name: name.into(),
|
||||||
namespace: namespace.to_string(),
|
namespace: namespace.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Add a command to the function.
|
/// Add a command to the function.
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub use command::{Command, Condition, Execute};
|
||||||
pub use function::Function;
|
pub use function::Function;
|
||||||
pub use namespace::Namespace;
|
pub use namespace::Namespace;
|
||||||
|
|
||||||
use std::{collections::HashMap, ops::RangeInclusive, sync::Mutex};
|
use std::{borrow::Cow, collections::BTreeMap, ops::RangeInclusive, sync::Mutex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
util::compile::{CompileOptions, CompilerState, MutCompilerState},
|
util::compile::{CompileOptions, CompilerState, MutCompilerState},
|
||||||
|
@ -23,21 +23,28 @@ pub struct Datapack {
|
||||||
description: String,
|
description: String,
|
||||||
pack_format: u8,
|
pack_format: u8,
|
||||||
supported_formats: Option<RangeInclusive<u8>>,
|
supported_formats: Option<RangeInclusive<u8>>,
|
||||||
namespaces: HashMap<String, Namespace>,
|
main_namespace_name: String,
|
||||||
|
namespaces: BTreeMap<String, Namespace>,
|
||||||
|
/// Scoreboard name -> (criteria, display name)
|
||||||
|
scoreboards: BTreeMap<String, (Option<String>, Option<String>)>,
|
||||||
|
uninstall_commands: Vec<Command>,
|
||||||
custom_files: VFolder,
|
custom_files: VFolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Datapack {
|
impl Datapack {
|
||||||
pub const LATEST_FORMAT: u8 = 48;
|
pub const LATEST_FORMAT: u8 = 61;
|
||||||
|
|
||||||
/// Create a new Minecraft datapack.
|
/// Create a new Minecraft datapack.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(pack_format: u8) -> Self {
|
pub fn new(main_namespace_name: impl Into<String>, pack_format: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
description: String::from("A Minecraft datapack created with shulkerbox"),
|
description: String::from("A Minecraft datapack created with shulkerbox"),
|
||||||
pack_format,
|
pack_format,
|
||||||
supported_formats: None,
|
supported_formats: None,
|
||||||
namespaces: HashMap::new(),
|
main_namespace_name: main_namespace_name.into(),
|
||||||
|
namespaces: BTreeMap::new(),
|
||||||
|
scoreboards: BTreeMap::new(),
|
||||||
|
uninstall_commands: Vec::new(),
|
||||||
custom_files: VFolder::new(),
|
custom_files: VFolder::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +72,7 @@ impl Datapack {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If loading the directory fails
|
/// - If loading the directory fails
|
||||||
#[cfg(feature = "fs_access")]
|
#[cfg(feature = "fs_access")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
|
||||||
pub fn with_template_folder<P>(self, path: P) -> std::io::Result<Self>
|
pub fn with_template_folder<P>(self, path: P) -> std::io::Result<Self>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<std::path::Path>,
|
||||||
|
@ -90,24 +98,49 @@ impl Datapack {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutably get a namespace by name or create a new one if it doesn't exist.
|
/// Mutably get a namespace by name or create a new one if it doesn't exist.
|
||||||
pub fn namespace_mut(&mut self, name: &str) -> &mut Namespace {
|
pub fn namespace_mut(&mut self, name: impl Into<String>) -> &mut Namespace {
|
||||||
|
let name = name.into();
|
||||||
self.namespaces
|
self.namespaces
|
||||||
.entry(name.to_string())
|
.entry(name.clone())
|
||||||
.or_insert_with(|| Namespace::new(name))
|
.or_insert_with(|| Namespace::new(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a function to the tick function list.
|
/// Add a function to the tick function list.
|
||||||
pub fn add_tick(&mut self, function: &str) {
|
pub fn add_tick(&mut self, function: impl Into<String>) {
|
||||||
self.namespace_mut("minecraft")
|
self.namespace_mut("minecraft")
|
||||||
.tag_mut("tick", tag::TagType::Function)
|
.tag_mut("tick", tag::TagType::Function)
|
||||||
.add_value(tag::TagValue::Simple(function.to_string()));
|
.add_value(tag::TagValue::Simple(function.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a function to the load function list.
|
/// Add a function to the load function list.
|
||||||
pub fn add_load(&mut self, function: &str) {
|
pub fn add_load(&mut self, function: impl Into<String>) {
|
||||||
self.namespace_mut("minecraft")
|
self.namespace_mut("minecraft")
|
||||||
.tag_mut("load", tag::TagType::Function)
|
.tag_mut("load", tag::TagType::Function)
|
||||||
.add_value(tag::TagValue::Simple(function.to_string()));
|
.add_value(tag::TagValue::Simple(function.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a scoreboard.
|
||||||
|
pub fn register_scoreboard(
|
||||||
|
&mut self,
|
||||||
|
name: impl Into<String>,
|
||||||
|
criteria: Option<impl Into<String>>,
|
||||||
|
display_name: Option<impl Into<String>>,
|
||||||
|
) {
|
||||||
|
self.scoreboards.insert(
|
||||||
|
name.into(),
|
||||||
|
(criteria.map(Into::into), display_name.map(Into::into)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scoreboards registered in the datapack.
|
||||||
|
#[must_use]
|
||||||
|
pub fn scoreboards(&self) -> &BTreeMap<String, (Option<String>, Option<String>)> {
|
||||||
|
&self.scoreboards
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add commands to the uninstall function.
|
||||||
|
pub fn add_uninstall_commands(&mut self, commands: Vec<Command>) {
|
||||||
|
self.uninstall_commands.extend(commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a custom file to the datapack.
|
/// Add a custom file to the datapack.
|
||||||
|
@ -132,8 +165,71 @@ impl Datapack {
|
||||||
root_folder.add_file("pack.mcmeta", mcmeta);
|
root_folder.add_file("pack.mcmeta", mcmeta);
|
||||||
let mut data_folder = VFolder::new();
|
let mut data_folder = VFolder::new();
|
||||||
|
|
||||||
|
let mut modified_namespaces = self
|
||||||
|
.namespaces
|
||||||
|
.iter()
|
||||||
|
.map(|(name, namespace)| (name.as_str(), Cow::Borrowed(namespace)))
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
|
let mut uninstall_commands = options
|
||||||
|
.uninstall_function
|
||||||
|
.then_some(Cow::Borrowed(&self.uninstall_commands));
|
||||||
|
|
||||||
|
if !self.scoreboards.is_empty() {
|
||||||
|
let main_namespace = modified_namespaces
|
||||||
|
.entry(&self.main_namespace_name)
|
||||||
|
.or_insert_with(|| Cow::Owned(Namespace::new(&self.main_namespace_name)));
|
||||||
|
let register_scoreboard_function = main_namespace
|
||||||
|
.to_mut()
|
||||||
|
.function_mut("sb/register_scoreboards");
|
||||||
|
for (name, (criteria, display_name)) in &self.scoreboards {
|
||||||
|
let mut creation_command = format!(
|
||||||
|
"scoreboard objectives add {name} {criteria}",
|
||||||
|
criteria = criteria.as_deref().unwrap_or("dummy")
|
||||||
|
);
|
||||||
|
if let Some(display_name) = display_name {
|
||||||
|
creation_command.push(' ');
|
||||||
|
creation_command.push_str(display_name);
|
||||||
|
}
|
||||||
|
register_scoreboard_function.add_command(Command::Raw(creation_command));
|
||||||
|
|
||||||
|
if let Some(uninstall_commands) = uninstall_commands.as_mut() {
|
||||||
|
uninstall_commands
|
||||||
|
.to_mut()
|
||||||
|
.push(Command::Raw(format!("scoreboard objectives remove {name}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let minecraft_namespace = modified_namespaces
|
||||||
|
.entry("minecraft")
|
||||||
|
.or_insert_with(|| Cow::Owned(Namespace::new("minecraft")));
|
||||||
|
minecraft_namespace
|
||||||
|
.to_mut()
|
||||||
|
.tag_mut("load", tag::TagType::Function)
|
||||||
|
.values_mut()
|
||||||
|
.insert(
|
||||||
|
0,
|
||||||
|
tag::TagValue::Simple(format!(
|
||||||
|
"{}:sb/register_scoreboards",
|
||||||
|
self.main_namespace_name
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(uninstall_commands) = uninstall_commands {
|
||||||
|
if !uninstall_commands.is_empty() {
|
||||||
|
let main_namespace = modified_namespaces
|
||||||
|
.entry(&self.main_namespace_name)
|
||||||
|
.or_insert_with(|| Cow::Owned(Namespace::new(&self.main_namespace_name)));
|
||||||
|
let uninstall_function = main_namespace.to_mut().function_mut("uninstall");
|
||||||
|
uninstall_function
|
||||||
|
.get_commands_mut()
|
||||||
|
.extend(uninstall_commands.into_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compile namespaces
|
// Compile namespaces
|
||||||
for (name, namespace) in &self.namespaces {
|
for (name, namespace) in modified_namespaces {
|
||||||
let namespace_folder = namespace.compile(&options, &compiler_state);
|
let namespace_folder = namespace.compile(&options, &compiler_state);
|
||||||
data_folder.add_existing_folder(name, namespace_folder);
|
data_folder.add_existing_folder(name, namespace_folder);
|
||||||
}
|
}
|
||||||
|
@ -180,7 +276,7 @@ mod tests {
|
||||||
fn test_datapack() {
|
fn test_datapack() {
|
||||||
let template_dir = tempfile::tempdir().expect("error creating tempdir");
|
let template_dir = tempfile::tempdir().expect("error creating tempdir");
|
||||||
|
|
||||||
let mut dp = Datapack::new(Datapack::LATEST_FORMAT)
|
let mut dp = Datapack::new("main", Datapack::LATEST_FORMAT)
|
||||||
.with_description("My datapack")
|
.with_description("My datapack")
|
||||||
.with_template_folder(template_dir.path())
|
.with_template_folder(template_dir.path())
|
||||||
.expect("error reading template folder");
|
.expect("error reading template folder");
|
||||||
|
@ -193,7 +289,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generate_mcmeta() {
|
fn test_generate_mcmeta() {
|
||||||
let dp = &Datapack::new(Datapack::LATEST_FORMAT).with_description("foo");
|
let dp = &Datapack::new("main", Datapack::LATEST_FORMAT).with_description("foo");
|
||||||
let state = Mutex::new(CompilerState::default());
|
let state = Mutex::new(CompilerState::default());
|
||||||
let mcmeta = generate_mcmeta(dp, &CompileOptions::default(), &state);
|
let mcmeta = generate_mcmeta(dp, &CompileOptions::default(), &state);
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,9 @@ pub struct Namespace {
|
||||||
|
|
||||||
impl Namespace {
|
impl Namespace {
|
||||||
/// Create a new namespace.
|
/// Create a new namespace.
|
||||||
pub(in crate::datapack) fn new(name: &str) -> Self {
|
pub(in crate::datapack) fn new(name: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.into(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
tags: HashMap::new(),
|
tags: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
@ -62,23 +62,24 @@ impl Namespace {
|
||||||
|
|
||||||
/// Mutably get a function by name or create a new one if it doesn't exist.
|
/// Mutably get a function by name or create a new one if it doesn't exist.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn function_mut(&mut self, name: &str) -> &mut Function {
|
pub fn function_mut(&mut self, name: impl Into<String>) -> &mut Function {
|
||||||
|
let name = name.into();
|
||||||
self.functions
|
self.functions
|
||||||
.entry(name.to_string())
|
.entry(name.clone())
|
||||||
.or_insert_with(|| Function::new(&self.name, name))
|
.or_insert_with(|| Function::new(&self.name, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a tag by name and type.
|
/// Get a tag by name and type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn tag(&self, name: &str, tag_type: TagType) -> Option<&Tag> {
|
pub fn tag(&self, name: impl Into<String>, tag_type: TagType) -> Option<&Tag> {
|
||||||
self.tags.get(&(name.to_string(), tag_type))
|
self.tags.get(&(name.into(), tag_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutably get a tag by name and type or create a new one if it doesn't exist.
|
/// Mutably get a tag by name and type or create a new one if it doesn't exist.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn tag_mut(&mut self, name: &str, tag_type: TagType) -> &mut Tag {
|
pub fn tag_mut(&mut self, name: impl Into<String>, tag_type: TagType) -> &mut Tag {
|
||||||
self.tags
|
self.tags
|
||||||
.entry((name.to_string(), tag_type))
|
.entry((name.into(), tag_type))
|
||||||
.or_insert_with(|| Tag::new(false))
|
.or_insert_with(|| Tag::new(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,16 @@ impl Tag {
|
||||||
|
|
||||||
/// Get the values of the tag.
|
/// Get the values of the tag.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_values(&self) -> &Vec<TagValue> {
|
pub fn values(&self) -> &[TagValue] {
|
||||||
&self.values
|
&self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the values of the tag.
|
||||||
|
#[must_use]
|
||||||
|
pub fn values_mut(&mut self) -> &mut Vec<TagValue> {
|
||||||
|
&mut self.values
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a value to the tag.
|
/// Add a value to the tag.
|
||||||
pub fn add_value(&mut self, value: TagValue) {
|
pub fn add_value(&mut self, value: TagValue) {
|
||||||
self.values.push(value);
|
self.values.push(value);
|
||||||
|
@ -186,7 +192,7 @@ mod tests {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(tag.get_values().len(), 2);
|
assert_eq!(tag.values().len(), 2);
|
||||||
|
|
||||||
let compiled = tag.compile(&CompileOptions::default(), &MutCompilerState::default());
|
let compiled = tag.compile(&CompileOptions::default(), &MutCompilerState::default());
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
)]
|
)]
|
||||||
#![warn(clippy::all, clippy::pedantic, clippy::perf)]
|
#![warn(clippy::all, clippy::pedantic, clippy::perf)]
|
||||||
#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
|
#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
pub mod datapack;
|
pub mod datapack;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
|
@ -17,6 +17,8 @@ pub struct CompileOptions {
|
||||||
pub(crate) pack_format: u8,
|
pub(crate) pack_format: u8,
|
||||||
/// Whether to compile in debug mode.
|
/// Whether to compile in debug mode.
|
||||||
pub(crate) debug: bool,
|
pub(crate) debug: bool,
|
||||||
|
/// Whether to generate an uninstall function.
|
||||||
|
pub(crate) uninstall_function: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileOptions {
|
impl CompileOptions {
|
||||||
|
@ -25,6 +27,15 @@ impl CompileOptions {
|
||||||
pub fn with_debug(self, debug: bool) -> Self {
|
pub fn with_debug(self, debug: bool) -> Self {
|
||||||
Self { debug, ..self }
|
Self { debug, ..self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set whether to generate an uninstall function.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_uninstall_function(self, uninstall_function: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
uninstall_function,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompileOptions {
|
impl Default for CompileOptions {
|
||||||
|
@ -32,6 +43,7 @@ impl Default for CompileOptions {
|
||||||
Self {
|
Self {
|
||||||
pack_format: Datapack::LATEST_FORMAT,
|
pack_format: Datapack::LATEST_FORMAT,
|
||||||
debug: true,
|
debug: true,
|
||||||
|
uninstall_function: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,7 @@ impl VFolder {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If the folder cannot be written
|
/// - If the folder cannot be written
|
||||||
#[cfg(feature = "fs_access")]
|
#[cfg(feature = "fs_access")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
|
||||||
pub fn place<P>(&self, path: P) -> std::io::Result<()>
|
pub fn place<P>(&self, path: P) -> std::io::Result<()>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<std::path::Path>,
|
||||||
|
@ -162,6 +163,7 @@ impl VFolder {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If the zip archive cannot be written
|
/// - If the zip archive cannot be written
|
||||||
#[cfg(all(feature = "fs_access", feature = "zip"))]
|
#[cfg(all(feature = "fs_access", feature = "zip"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "fs_access", feature = "zip"))))]
|
||||||
pub fn zip<P>(&self, path: P) -> std::io::Result<()>
|
pub fn zip<P>(&self, path: P) -> std::io::Result<()>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<std::path::Path>,
|
||||||
|
@ -174,6 +176,7 @@ impl VFolder {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If the zip archive cannot be written
|
/// - If the zip archive cannot be written
|
||||||
#[cfg(all(feature = "fs_access", feature = "zip"))]
|
#[cfg(all(feature = "fs_access", feature = "zip"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "fs_access", feature = "zip"))))]
|
||||||
pub fn zip_with_comment<P, S>(&self, path: P, comment: S) -> std::io::Result<()>
|
pub fn zip_with_comment<P, S>(&self, path: P, comment: S) -> std::io::Result<()>
|
||||||
where
|
where
|
||||||
P: AsRef<std::path::Path>,
|
P: AsRef<std::path::Path>,
|
||||||
|
@ -246,14 +249,14 @@ impl VFolder {
|
||||||
/// Recursively merge another folder into this folder.
|
/// Recursively merge another folder into this folder.
|
||||||
/// Returns a list of paths that were replaced by other.
|
/// Returns a list of paths that were replaced by other.
|
||||||
pub fn merge(&mut self, other: Self) -> Vec<String> {
|
pub fn merge(&mut self, other: Self) -> Vec<String> {
|
||||||
self._merge(other, "")
|
self.merge_(other, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _merge(&mut self, other: Self, prefix: &str) -> Vec<String> {
|
fn merge_(&mut self, other: Self, prefix: &str) -> Vec<String> {
|
||||||
let mut replaced = Vec::new();
|
let mut replaced = Vec::new();
|
||||||
for (name, folder) in other.folders {
|
for (name, folder) in other.folders {
|
||||||
if let Some(existing_folder) = self.folders.get_mut(&name) {
|
if let Some(existing_folder) = self.folders.get_mut(&name) {
|
||||||
let replaced_folder = existing_folder._merge(folder, &format!("{prefix}{name}/"));
|
let replaced_folder = existing_folder.merge_(folder, &format!("{prefix}{name}/"));
|
||||||
replaced.extend(replaced_folder);
|
replaced.extend(replaced_folder);
|
||||||
} else {
|
} else {
|
||||||
self.folders.insert(name, folder);
|
self.folders.insert(name, folder);
|
||||||
|
@ -271,6 +274,7 @@ impl VFolder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fs_access")]
|
#[cfg(feature = "fs_access")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
|
||||||
impl TryFrom<&std::path::Path> for VFolder {
|
impl TryFrom<&std::path::Path> for VFolder {
|
||||||
type Error = std::io::Error;
|
type Error = std::io::Error;
|
||||||
|
|
||||||
|
@ -374,6 +378,7 @@ impl From<&[u8]> for VFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fs_access")]
|
#[cfg(feature = "fs_access")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "fs_access")))]
|
||||||
impl TryFrom<&std::path::Path> for VFile {
|
impl TryFrom<&std::path::Path> for VFile {
|
||||||
type Error = std::io::Error;
|
type Error = std::io::Error;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue