Update dependencies and fix warnings
This commit is contained in:
parent
19db26efa2
commit
edcf80d389
|
@ -7,11 +7,12 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
default = ["zip"]
|
||||
serde = ["dep:serde"]
|
||||
zip = ["dep:zip"]
|
||||
|
||||
[dependencies]
|
||||
chksum-md5 = "0.0.0"
|
||||
getset = "0.1.2"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde = { version = "1.0.197", optional = true, features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
zip = { version = "0.6.6", default-features = false, features = ["deflate", "time"], optional = true }
|
||||
zip = { version = "1.1.1", default-features = false, features = ["deflate", "time"], optional = true }
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use std::ops::{BitAnd, BitOr, Not};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState};
|
||||
|
||||
use super::Command;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Execute {
|
||||
Align(String, Box<Execute>),
|
||||
Anchored(String, Box<Execute>),
|
||||
|
@ -49,6 +48,7 @@ impl Execute {
|
|||
.collect()
|
||||
}
|
||||
}
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn compile_internal(
|
||||
&self,
|
||||
prefix: String,
|
||||
|
@ -170,7 +170,7 @@ impl Execute {
|
|||
cond,
|
||||
then.as_ref(),
|
||||
el.as_deref(),
|
||||
prefix,
|
||||
&prefix,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
|
@ -233,15 +233,20 @@ fn compile_if_cond(
|
|||
cond: &Condition,
|
||||
then: &Execute,
|
||||
el: Option<&Execute>,
|
||||
prefix: String,
|
||||
prefix: &str,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &FunctionCompilerState,
|
||||
) -> Vec<(bool, String)> {
|
||||
// TODO: fix conflicting data storage location when nesting if-else conditions
|
||||
|
||||
let str_then =
|
||||
then.compile_internal(prefix.clone(), false, options, global_state, function_state);
|
||||
let str_then = then.compile_internal(
|
||||
prefix.to_string(),
|
||||
false,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
);
|
||||
let str_cond = cond.clone().compile(options, global_state, function_state);
|
||||
let require_grouping = el.is_some() || str_then.len() > 1;
|
||||
let then = if require_grouping {
|
||||
|
@ -272,7 +277,7 @@ fn compile_if_cond(
|
|||
"data modify storage shulkerbox:cond if_success set value true",
|
||||
combine_conditions_commands(
|
||||
str_cond.clone(),
|
||||
vec![(
|
||||
&[(
|
||||
true,
|
||||
"run data modify storage shulkerbox:cond if_success set value true".to_string(),
|
||||
)],
|
||||
|
@ -288,7 +293,7 @@ fn compile_if_cond(
|
|||
} else {
|
||||
str_cond
|
||||
};
|
||||
let then_commands = combine_conditions_commands(successful_cond, then);
|
||||
let then_commands = combine_conditions_commands(successful_cond, &then);
|
||||
let el_commands = el
|
||||
.map(|el| {
|
||||
let else_cond =
|
||||
|
@ -301,7 +306,7 @@ fn compile_if_cond(
|
|||
global_state,
|
||||
function_state,
|
||||
);
|
||||
combine_conditions_commands(else_cond, el)
|
||||
combine_conditions_commands(else_cond, &el)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -323,7 +328,7 @@ fn compile_if_cond(
|
|||
.chain(reset_success_storage)
|
||||
.map(|(use_prefix, cmd)| {
|
||||
let cmd = if use_prefix {
|
||||
prefix.clone() + &cmd
|
||||
prefix.to_string() + &cmd
|
||||
} else {
|
||||
cmd
|
||||
};
|
||||
|
@ -334,7 +339,7 @@ fn compile_if_cond(
|
|||
|
||||
fn combine_conditions_commands(
|
||||
conditions: Vec<String>,
|
||||
commands: Vec<(bool, String)>,
|
||||
commands: &[(bool, String)],
|
||||
) -> Vec<(bool, String)> {
|
||||
conditions
|
||||
.into_iter()
|
||||
|
@ -352,7 +357,8 @@ fn combine_conditions_commands(
|
|||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Condition {
|
||||
Atom(String),
|
||||
Not(Box<Condition>),
|
||||
|
@ -361,11 +367,12 @@ pub enum Condition {
|
|||
}
|
||||
impl Condition {
|
||||
/// Normalize the condition.
|
||||
#[must_use]
|
||||
pub fn normalize(&self) -> Self {
|
||||
match self {
|
||||
Self::Atom(_) => self.clone(),
|
||||
Self::Not(c) => match *c.clone() {
|
||||
Self::Atom(c) => Self::Not(Box::new(Self::Atom(c.clone()))),
|
||||
Self::Atom(c) => Self::Not(Box::new(Self::Atom(c))),
|
||||
Self::Not(c) => c.normalize(),
|
||||
Self::And(c1, c2) => ((!*c1).normalize()) | ((!*c2).normalize()),
|
||||
Self::Or(c1, c2) => ((!*c1).normalize()) & ((!*c2).normalize()),
|
||||
|
@ -376,11 +383,12 @@ impl Condition {
|
|||
}
|
||||
|
||||
/// Compile the condition into a list of strings.
|
||||
#[allow(clippy::only_used_in_recursion)]
|
||||
pub fn compile(
|
||||
&self,
|
||||
_options: &CompileOptions,
|
||||
_global_state: &MutCompilerState,
|
||||
_function_state: &FunctionCompilerState,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &FunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
match self.normalize() {
|
||||
Self::Atom(a) => vec!["if ".to_string() + &a],
|
||||
|
@ -389,16 +397,16 @@ impl Condition {
|
|||
_ => unreachable!("Cannot happen because of normalization"),
|
||||
},
|
||||
Self::And(c1, c2) => {
|
||||
let c1 = c1.compile(_options, _global_state, _function_state);
|
||||
let c2 = c2.compile(_options, _global_state, _function_state);
|
||||
let c1 = c1.compile(options, global_state, function_state);
|
||||
let c2 = c2.compile(options, global_state, function_state);
|
||||
|
||||
c1.into_iter()
|
||||
.flat_map(|c1| c2.iter().map(move |c2| c1.clone() + " " + c2))
|
||||
.collect()
|
||||
}
|
||||
Self::Or(c1, c2) => {
|
||||
let mut c1 = c1.compile(_options, _global_state, _function_state);
|
||||
let c2 = c2.compile(_options, _global_state, _function_state);
|
||||
let mut c1 = c1.compile(options, global_state, function_state);
|
||||
let c2 = c2.compile(options, global_state, function_state);
|
||||
c1.extend(c2);
|
||||
c1
|
||||
}
|
||||
|
@ -408,7 +416,7 @@ impl Condition {
|
|||
|
||||
impl From<&str> for Condition {
|
||||
fn from(s: &str) -> Self {
|
||||
Condition::Atom(s.to_string())
|
||||
Self::Atom(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,21 +424,21 @@ impl Not for Condition {
|
|||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self {
|
||||
Condition::Not(Box::new(self))
|
||||
Self::Not(Box::new(self))
|
||||
}
|
||||
}
|
||||
impl BitAnd for Condition {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self {
|
||||
Condition::And(Box::new(self), Box::new(rhs))
|
||||
Self::And(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
impl BitOr for Condition {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
Condition::Or(Box::new(self), Box::new(rhs))
|
||||
Self::Or(Box::new(self), Box::new(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,11 +478,8 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
(c1.clone() & c2.clone() | c3.clone() & c1.clone()).normalize(),
|
||||
c1.clone() & c2.clone() | c3.clone() & c1.clone()
|
||||
);
|
||||
assert_eq!(
|
||||
(!(c1.clone() | c2.clone())).normalize(),
|
||||
!c1.clone() & !c2.clone()
|
||||
c1.clone() & c2.clone() | c3 & c1.clone()
|
||||
);
|
||||
assert_eq!((!(c1.clone() | c2.clone())).normalize(), !c1 & !c2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ mod execute;
|
|||
pub use execute::{Condition, Execute};
|
||||
|
||||
use chksum_md5 as md5;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState};
|
||||
|
||||
use super::Function;
|
||||
|
||||
/// Represents a command that can be included in a function.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Command {
|
||||
/// A command that is already formatted as a string.
|
||||
Raw(String),
|
||||
|
@ -28,6 +28,7 @@ pub enum Command {
|
|||
|
||||
impl Command {
|
||||
/// Create a new raw command.
|
||||
#[must_use]
|
||||
pub fn raw(command: &str) -> Self {
|
||||
Self::Raw(command.to_string())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Function struct and implementation
|
||||
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
|
||||
|
@ -11,7 +10,8 @@ use crate::{
|
|||
use super::command::Command;
|
||||
|
||||
/// Function that can be called by a command
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, Getters)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Default, Getters)]
|
||||
pub struct Function {
|
||||
commands: Vec<Command>,
|
||||
/// Name of the function
|
||||
|
@ -36,6 +36,7 @@ impl Function {
|
|||
}
|
||||
|
||||
/// Get the commands of the function.
|
||||
#[must_use]
|
||||
pub fn get_commands(&self) -> &Vec<Command> {
|
||||
&self.commands
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ pub mod tag;
|
|||
pub use command::{Command, Condition, Execute};
|
||||
pub use function::Function;
|
||||
pub use namespace::Namespace;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::{collections::HashMap, ops::RangeInclusive, path::Path, sync::Mutex};
|
||||
|
||||
|
@ -17,7 +16,8 @@ use crate::{
|
|||
};
|
||||
|
||||
/// A Minecraft datapack.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Datapack {
|
||||
// TODO: Support filter and overlays
|
||||
name: String,
|
||||
|
@ -32,6 +32,7 @@ pub struct Datapack {
|
|||
|
||||
impl Datapack {
|
||||
/// Create a new Minecraft datapack.
|
||||
#[must_use]
|
||||
pub fn new(name: &str, pack_format: u8) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
|
@ -46,6 +47,7 @@ impl Datapack {
|
|||
}
|
||||
|
||||
/// Set the description of the datapack.
|
||||
#[must_use]
|
||||
pub fn with_description(self, description: &str) -> Self {
|
||||
Self {
|
||||
description: description.to_string(),
|
||||
|
@ -54,6 +56,7 @@ impl Datapack {
|
|||
}
|
||||
|
||||
/// Set the supported pack formats of the datapack.
|
||||
#[must_use]
|
||||
pub fn with_supported_formats(self, supported_formats: RangeInclusive<u8>) -> Self {
|
||||
Self {
|
||||
supported_formats: Some(supported_formats),
|
||||
|
@ -62,6 +65,9 @@ impl Datapack {
|
|||
}
|
||||
|
||||
/// Set the custom files of the datapack.
|
||||
///
|
||||
/// # Errors
|
||||
/// - If loading the directory fails
|
||||
pub fn with_template_folder(self, path: &Path) -> std::io::Result<Self> {
|
||||
let mut template = VFolder::try_from(path)?;
|
||||
template.merge(self.custom_files);
|
||||
|
@ -73,6 +79,7 @@ impl Datapack {
|
|||
}
|
||||
|
||||
/// Get a namespace by name.
|
||||
#[must_use]
|
||||
pub fn namespace(&self, name: &str) -> Option<&Namespace> {
|
||||
self.namespaces.get(name)
|
||||
}
|
||||
|
@ -100,6 +107,7 @@ impl Datapack {
|
|||
}
|
||||
|
||||
/// Compile the pack into a virtual folder.
|
||||
#[must_use]
|
||||
pub fn compile(&self, options: &CompileOptions) -> VFolder {
|
||||
let compiler_state = Mutex::new(CompilerState::default());
|
||||
|
||||
|
@ -152,7 +160,7 @@ fn generate_mcmeta(dp: &Datapack, _options: &CompileOptions, _state: &MutCompile
|
|||
content["pack"]["supported_formats"] = serde_json::json!({
|
||||
"min_inclusive": *supported_formats.start(),
|
||||
"max_inclusive": *supported_formats.end()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
VFile::Text(content.to_string())
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
//! Namespace of a datapack
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
util::{
|
||||
compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
|
||||
extendable_queue::ExtendableQueue,
|
||||
ExtendableQueue,
|
||||
},
|
||||
virtual_fs::VFolder,
|
||||
};
|
||||
|
@ -14,7 +12,8 @@ use super::{function::Function, tag::Tag};
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
/// Namespace of a datapack
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Namespace {
|
||||
name: String,
|
||||
functions: HashMap<String, Function>,
|
||||
|
@ -34,11 +33,13 @@ impl Namespace {
|
|||
}
|
||||
|
||||
/// Get the name of the namespace.
|
||||
#[must_use]
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Get the main function of the namespace.
|
||||
#[must_use]
|
||||
pub fn get_main_function(&self) -> &Function {
|
||||
&self.main_function
|
||||
}
|
||||
|
@ -48,16 +49,19 @@ impl Namespace {
|
|||
}
|
||||
|
||||
/// Get the functions of the namespace.
|
||||
#[must_use]
|
||||
pub fn get_functions(&self) -> &HashMap<String, Function> {
|
||||
&self.functions
|
||||
}
|
||||
|
||||
/// Get the tags of the namespace.
|
||||
#[must_use]
|
||||
pub fn get_tags(&self) -> &HashMap<String, Tag> {
|
||||
&self.tags
|
||||
}
|
||||
|
||||
/// Get a function by name.
|
||||
#[must_use]
|
||||
pub fn function(&self, name: &str) -> Option<&Function> {
|
||||
self.functions.get(name)
|
||||
}
|
||||
|
@ -94,7 +98,7 @@ impl Namespace {
|
|||
while let Some((path, function)) = functions.next() {
|
||||
let function_state = FunctionCompilerState::new(&path, &self.name, functions.clone());
|
||||
root_folder.add_file(
|
||||
&format!("functions/{}.mcfunction", path),
|
||||
&format!("functions/{path}.mcfunction"),
|
||||
function.compile(options, state, &function_state),
|
||||
);
|
||||
}
|
||||
|
@ -102,7 +106,7 @@ impl Namespace {
|
|||
// Compile tags
|
||||
for (path, tag) in &self.tags {
|
||||
let (tag_type, vfile) = tag.compile(options, state);
|
||||
root_folder.add_file(&format!("tags/{}/{}.json", tag_type, path), vfile);
|
||||
root_folder.add_file(&format!("tags/{tag_type}/{path}.json"), vfile);
|
||||
}
|
||||
|
||||
root_folder
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
//! A tag for various types.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
util::compile::{CompileOptions, MutCompilerState},
|
||||
virtual_fs::VFile,
|
||||
};
|
||||
|
||||
/// A tag for various types.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tag {
|
||||
r#type: TagType,
|
||||
replace: bool,
|
||||
|
@ -16,6 +15,7 @@ pub struct Tag {
|
|||
}
|
||||
impl Tag {
|
||||
/// Create a new tag.
|
||||
#[must_use]
|
||||
pub fn new(r#type: TagType, replace: bool) -> Self {
|
||||
Self {
|
||||
r#type,
|
||||
|
@ -48,7 +48,9 @@ impl Tag {
|
|||
}
|
||||
|
||||
/// The type of a tag.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TagType {
|
||||
/// A tag for blocks.
|
||||
Blocks,
|
||||
|
@ -81,7 +83,9 @@ impl ToString for TagType {
|
|||
}
|
||||
|
||||
/// The value of a tag.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TagValue {
|
||||
/// A simple value, either a resource location or an id of another tag.
|
||||
Simple(String),
|
||||
|
@ -100,6 +104,7 @@ impl From<&str> for TagValue {
|
|||
}
|
||||
impl TagValue {
|
||||
/// Compile the tag value into a JSON value.
|
||||
#[must_use]
|
||||
pub fn compile(&self) -> serde_json::Value {
|
||||
match self {
|
||||
Self::Simple(value) => serde_json::Value::String(value.clone()),
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -1,14 +1,15 @@
|
|||
//! Shulkerbox is a library for creating Minecraft data packs.
|
||||
|
||||
#![warn(
|
||||
missing_docs,
|
||||
#![deny(
|
||||
unsafe_code,
|
||||
missing_debug_implementations,
|
||||
nonstandard_style,
|
||||
clippy::complexity,
|
||||
clippy::style,
|
||||
clippy::suspicious
|
||||
missing_copy_implementations,
|
||||
clippy::nursery,
|
||||
rustdoc::broken_intra_doc_links,
|
||||
clippy::missing_errors_doc
|
||||
)]
|
||||
#![deny(unsafe_code)]
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
|
||||
|
||||
pub mod datapack;
|
||||
pub mod util;
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
use std::{cell::Cell, sync::Mutex};
|
||||
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::datapack::Function;
|
||||
|
||||
use super::extendable_queue::ExtendableQueue;
|
||||
|
||||
/// Compile options for the compiler.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(missing_copy_implementations, clippy::module_name_repetitions)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompileOptions {
|
||||
/// Whether to compile in debug mode.
|
||||
pub debug: bool,
|
||||
|
@ -23,7 +24,9 @@ impl Default for CompileOptions {
|
|||
}
|
||||
|
||||
/// State of the compiler that can change during compilation.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[allow(missing_copy_implementations)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CompilerState {}
|
||||
/// Mutex for the compiler state.
|
||||
pub type MutCompilerState = Mutex<CompilerState>;
|
||||
|
@ -48,6 +51,7 @@ type FunctionQueue = ExtendableQueue<(String, Function)>;
|
|||
|
||||
impl FunctionCompilerState {
|
||||
/// Create a new function compiler state.
|
||||
#[must_use]
|
||||
pub fn new(path: &str, namespace: &str, functions: FunctionQueue) -> Self {
|
||||
Self {
|
||||
generated_functions: Cell::new(0),
|
||||
|
|
|
@ -37,11 +37,13 @@ impl<T> ExtendableQueue<T> {
|
|||
}
|
||||
|
||||
/// Get the queue.
|
||||
#[must_use]
|
||||
pub fn get(&self) -> &Arc<RwLock<VecDeque<T>>> {
|
||||
&self.queue
|
||||
}
|
||||
|
||||
/// Get a weak reference to the queue.
|
||||
#[must_use]
|
||||
pub fn get_weak(&self) -> Weak<RwLock<VecDeque<T>>> {
|
||||
Arc::downgrade(&self.queue)
|
||||
}
|
||||
|
@ -52,16 +54,19 @@ impl<T> ExtendableQueue<T> {
|
|||
}
|
||||
|
||||
/// Get the length of the queue.
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.queue.read().unwrap().len()
|
||||
}
|
||||
|
||||
/// Check if the queue is empty.
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.queue.read().unwrap().is_empty()
|
||||
}
|
||||
|
||||
/// Get and remove the next item without needing mutable access.
|
||||
#[must_use]
|
||||
pub fn pop_front(&self) -> Option<T> {
|
||||
self.queue.write().unwrap().pop_front()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
//! Utility functions for the Shulkerbox project.
|
||||
|
||||
pub mod compile;
|
||||
pub mod extendable_queue;
|
||||
mod extendable_queue;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use extendable_queue::ExtendableQueue;
|
||||
|
|
|
@ -2,49 +2,51 @@
|
|||
|
||||
use std::{collections::HashMap, fs, io, path::Path};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "zip")]
|
||||
use zip::ZipWriter;
|
||||
|
||||
/// Folder representation in virtual file system
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct VFolder {
|
||||
folders: HashMap<String, VFolder>,
|
||||
files: HashMap<String, VFile>,
|
||||
}
|
||||
impl VFolder {
|
||||
/// Create a new, empty virtual folder.
|
||||
pub fn new() -> VFolder {
|
||||
VFolder {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
folders: HashMap::new(),
|
||||
files: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all direct subfolders in the folder.
|
||||
pub fn get_folders(&self) -> &HashMap<String, VFolder> {
|
||||
#[must_use]
|
||||
pub fn get_folders(&self) -> &HashMap<String, Self> {
|
||||
&self.folders
|
||||
}
|
||||
/// Get all direct files in the folder.
|
||||
#[must_use]
|
||||
pub fn get_files(&self) -> &HashMap<String, VFile> {
|
||||
&self.files
|
||||
}
|
||||
|
||||
/// Recursively add a new folder to the folder.
|
||||
pub fn add_folder(&mut self, path: &str) {
|
||||
self.add_existing_folder(path, VFolder::new());
|
||||
self.add_existing_folder(path, Self::new());
|
||||
}
|
||||
/// Recursively add an existing folder to the folder.
|
||||
pub fn add_existing_folder(&mut self, path: &str, folder: VFolder) {
|
||||
pub fn add_existing_folder(&mut self, path: &str, folder: Self) {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
if let Some(subfolder) = self.get_folder_mut(head) {
|
||||
subfolder.add_folder(tail);
|
||||
} else {
|
||||
let mut new_folder = VFolder::new();
|
||||
let mut new_folder = Self::new();
|
||||
new_folder.add_folder(tail);
|
||||
self.add_existing_folder(head, new_folder);
|
||||
}
|
||||
|
@ -56,13 +58,12 @@ impl VFolder {
|
|||
pub fn add_file(&mut self, path: &str, file: VFile) {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
if let Some(subfolder) = self.get_folder_mut(head) {
|
||||
subfolder.add_file(tail, file);
|
||||
} else {
|
||||
let mut new_folder = VFolder::new();
|
||||
let mut new_folder = Self::new();
|
||||
new_folder.add_file(tail, file);
|
||||
self.add_existing_folder(head, new_folder);
|
||||
}
|
||||
|
@ -72,11 +73,11 @@ impl VFolder {
|
|||
}
|
||||
|
||||
/// Recursively get a subfolder by path.
|
||||
pub fn get_folder(&self, path: &str) -> Option<&VFolder> {
|
||||
#[must_use]
|
||||
pub fn get_folder(&self, path: &str) -> Option<&Self> {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
self.folders.get(head)?.get_folder(tail)
|
||||
} else {
|
||||
|
@ -84,11 +85,10 @@ impl VFolder {
|
|||
}
|
||||
}
|
||||
/// Recursively get a mutable subfolder by path.
|
||||
pub fn get_folder_mut(&mut self, path: &str) -> Option<&mut VFolder> {
|
||||
pub fn get_folder_mut(&mut self, path: &str) -> Option<&mut Self> {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
self.folders.get_mut(head)?.get_folder_mut(tail)
|
||||
} else {
|
||||
|
@ -96,11 +96,11 @@ impl VFolder {
|
|||
}
|
||||
}
|
||||
/// Recursively get a file by path.
|
||||
#[must_use]
|
||||
pub fn get_file(&self, path: &str) -> Option<&VFile> {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
self.folders.get(head)?.get_file(tail)
|
||||
} else {
|
||||
|
@ -111,8 +111,7 @@ impl VFolder {
|
|||
pub fn get_file_mut(&mut self, path: &str) -> Option<&mut VFile> {
|
||||
let (head, tail) = path
|
||||
.split_once('/')
|
||||
.map(|(h, t)| (h, (!t.is_empty()).then_some(t)))
|
||||
.unwrap_or((path, None));
|
||||
.map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
|
||||
if let Some(tail) = tail {
|
||||
self.folders.get_mut(head)?.get_file_mut(tail)
|
||||
} else {
|
||||
|
@ -121,6 +120,9 @@ impl VFolder {
|
|||
}
|
||||
|
||||
/// Place the folder and its contents on the file system.
|
||||
///
|
||||
/// # Errors
|
||||
/// - If the folder cannot be written
|
||||
pub fn place(&self, path: &Path) -> io::Result<()> {
|
||||
fs::create_dir_all(path)?;
|
||||
for (name, folder) in &self.folders {
|
||||
|
@ -141,6 +143,9 @@ impl VFolder {
|
|||
|
||||
#[cfg(feature = "zip")]
|
||||
/// Zip the folder and its contents into a zip archive.
|
||||
///
|
||||
/// # Errors
|
||||
/// - If the zip archive cannot be written
|
||||
pub fn zip(&self, path: &Path) -> io::Result<()> {
|
||||
use io::Write;
|
||||
|
||||
|
@ -149,7 +154,7 @@ impl VFolder {
|
|||
let virtual_files = self.flatten();
|
||||
|
||||
for (path, file) in virtual_files {
|
||||
writer.start_file(path, Default::default())?;
|
||||
writer.start_file(path, zip::write::SimpleFileOptions::default())?;
|
||||
match file {
|
||||
VFile::Text(text) => {
|
||||
writer.write_all(text.as_bytes())?;
|
||||
|
@ -166,6 +171,7 @@ impl VFolder {
|
|||
}
|
||||
|
||||
/// Flatten the folder and its contents into a list of files with full paths.
|
||||
#[must_use]
|
||||
pub fn flatten(&self) -> Vec<(String, &VFile)> {
|
||||
let mut files = self
|
||||
.files
|
||||
|
@ -177,7 +183,7 @@ impl VFolder {
|
|||
let sub_files = folder
|
||||
.flatten()
|
||||
.into_iter()
|
||||
.map(|(path, file)| (format!("{}/{}", name, path), file))
|
||||
.map(|(path, file)| (format!("{name}/{path}"), file))
|
||||
.collect::<Vec<_>>();
|
||||
files.extend(sub_files);
|
||||
}
|
||||
|
@ -204,19 +210,15 @@ impl TryFrom<&Path> for VFolder {
|
|||
type Error = io::Error;
|
||||
|
||||
fn try_from(value: &Path) -> Result<Self, Self::Error> {
|
||||
let mut root_vfolder = VFolder::new();
|
||||
let root_folder = fs::read_dir(value)?;
|
||||
for dir_entry in root_folder {
|
||||
let mut root_vfolder = Self::new();
|
||||
let fs_root_folder = fs::read_dir(value)?;
|
||||
for dir_entry in fs_root_folder {
|
||||
let dir_entry = dir_entry?;
|
||||
let path = dir_entry.path();
|
||||
let name = dir_entry
|
||||
.file_name()
|
||||
.into_string()
|
||||
.map(Some)
|
||||
.unwrap_or_default();
|
||||
let name = dir_entry.file_name().into_string().ok();
|
||||
if let Some(name) = name {
|
||||
if path.is_dir() {
|
||||
root_vfolder.add_existing_folder(&name, VFolder::try_from(path.as_path())?);
|
||||
root_vfolder.add_existing_folder(&name, Self::try_from(path.as_path())?);
|
||||
} else if path.is_file() {
|
||||
let data = fs::read(path)?;
|
||||
root_vfolder.add_file(&name, VFile::Binary(data));
|
||||
|
@ -236,7 +238,8 @@ impl TryFrom<&Path> for VFolder {
|
|||
}
|
||||
|
||||
/// File representation in virtual file system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VFile {
|
||||
/// Text file
|
||||
Text(String),
|
||||
|
@ -246,17 +249,17 @@ pub enum VFile {
|
|||
|
||||
impl From<String> for VFile {
|
||||
fn from(value: String) -> Self {
|
||||
VFile::Text(value)
|
||||
Self::Text(value)
|
||||
}
|
||||
}
|
||||
impl From<&str> for VFile {
|
||||
fn from(value: &str) -> Self {
|
||||
VFile::Text(value.to_string())
|
||||
Self::Text(value.to_string())
|
||||
}
|
||||
}
|
||||
impl Default for VFile {
|
||||
fn default() -> Self {
|
||||
VFile::Text(String::new())
|
||||
Self::Text(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue