Update dependencies and fix warnings

This commit is contained in:
Moritz Hölting 2024-04-25 14:15:35 +02:00
parent 19db26efa2
commit edcf80d389
12 changed files with 140 additions and 99 deletions

View File

@ -7,11 +7,12 @@ edition = "2021"
[features] [features]
default = ["zip"] default = ["zip"]
serde = ["dep:serde"]
zip = ["dep:zip"] zip = ["dep:zip"]
[dependencies] [dependencies]
chksum-md5 = "0.0.0" chksum-md5 = "0.0.0"
getset = "0.1.2" 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" 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 }

View File

@ -1,13 +1,12 @@
use std::ops::{BitAnd, BitOr, Not}; use std::ops::{BitAnd, BitOr, Not};
use serde::{Deserialize, Serialize};
use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}; use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState};
use super::Command; use super::Command;
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum Execute { pub enum Execute {
Align(String, Box<Execute>), Align(String, Box<Execute>),
Anchored(String, Box<Execute>), Anchored(String, Box<Execute>),
@ -49,6 +48,7 @@ impl Execute {
.collect() .collect()
} }
} }
#[allow(clippy::too_many_lines)]
fn compile_internal( fn compile_internal(
&self, &self,
prefix: String, prefix: String,
@ -170,7 +170,7 @@ impl Execute {
cond, cond,
then.as_ref(), then.as_ref(),
el.as_deref(), el.as_deref(),
prefix, &prefix,
options, options,
global_state, global_state,
function_state, function_state,
@ -233,15 +233,20 @@ fn compile_if_cond(
cond: &Condition, cond: &Condition,
then: &Execute, then: &Execute,
el: Option<&Execute>, el: Option<&Execute>,
prefix: String, prefix: &str,
options: &CompileOptions, options: &CompileOptions,
global_state: &MutCompilerState, global_state: &MutCompilerState,
function_state: &FunctionCompilerState, function_state: &FunctionCompilerState,
) -> Vec<(bool, String)> { ) -> Vec<(bool, String)> {
// TODO: fix conflicting data storage location when nesting if-else conditions // TODO: fix conflicting data storage location when nesting if-else conditions
let str_then = let str_then = then.compile_internal(
then.compile_internal(prefix.clone(), false, options, global_state, function_state); prefix.to_string(),
false,
options,
global_state,
function_state,
);
let str_cond = cond.clone().compile(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 require_grouping = el.is_some() || str_then.len() > 1;
let then = if require_grouping { let then = if require_grouping {
@ -272,7 +277,7 @@ fn compile_if_cond(
"data modify storage shulkerbox:cond if_success set value true", "data modify storage shulkerbox:cond if_success set value true",
combine_conditions_commands( combine_conditions_commands(
str_cond.clone(), str_cond.clone(),
vec![( &[(
true, true,
"run data modify storage shulkerbox:cond if_success set value true".to_string(), "run data modify storage shulkerbox:cond if_success set value true".to_string(),
)], )],
@ -288,7 +293,7 @@ fn compile_if_cond(
} else { } else {
str_cond str_cond
}; };
let then_commands = combine_conditions_commands(successful_cond, then); let then_commands = combine_conditions_commands(successful_cond, &then);
let el_commands = el let el_commands = el
.map(|el| { .map(|el| {
let else_cond = let else_cond =
@ -301,7 +306,7 @@ fn compile_if_cond(
global_state, global_state,
function_state, function_state,
); );
combine_conditions_commands(else_cond, el) combine_conditions_commands(else_cond, &el)
}) })
.unwrap_or_default(); .unwrap_or_default();
@ -323,7 +328,7 @@ fn compile_if_cond(
.chain(reset_success_storage) .chain(reset_success_storage)
.map(|(use_prefix, cmd)| { .map(|(use_prefix, cmd)| {
let cmd = if use_prefix { let cmd = if use_prefix {
prefix.clone() + &cmd prefix.to_string() + &cmd
} else { } else {
cmd cmd
}; };
@ -334,7 +339,7 @@ fn compile_if_cond(
fn combine_conditions_commands( fn combine_conditions_commands(
conditions: Vec<String>, conditions: Vec<String>,
commands: Vec<(bool, String)>, commands: &[(bool, String)],
) -> Vec<(bool, String)> { ) -> Vec<(bool, String)> {
conditions conditions
.into_iter() .into_iter()
@ -352,7 +357,8 @@ fn combine_conditions_commands(
} }
#[allow(missing_docs)] #[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 { pub enum Condition {
Atom(String), Atom(String),
Not(Box<Condition>), Not(Box<Condition>),
@ -361,11 +367,12 @@ pub enum Condition {
} }
impl Condition { impl Condition {
/// Normalize the condition. /// Normalize the condition.
#[must_use]
pub fn normalize(&self) -> Self { pub fn normalize(&self) -> Self {
match self { match self {
Self::Atom(_) => self.clone(), Self::Atom(_) => self.clone(),
Self::Not(c) => match *c.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::Not(c) => c.normalize(),
Self::And(c1, c2) => ((!*c1).normalize()) | ((!*c2).normalize()), Self::And(c1, c2) => ((!*c1).normalize()) | ((!*c2).normalize()),
Self::Or(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. /// Compile the condition into a list of strings.
#[allow(clippy::only_used_in_recursion)]
pub fn compile( pub fn compile(
&self, &self,
_options: &CompileOptions, options: &CompileOptions,
_global_state: &MutCompilerState, global_state: &MutCompilerState,
_function_state: &FunctionCompilerState, function_state: &FunctionCompilerState,
) -> Vec<String> { ) -> Vec<String> {
match self.normalize() { match self.normalize() {
Self::Atom(a) => vec!["if ".to_string() + &a], Self::Atom(a) => vec!["if ".to_string() + &a],
@ -389,16 +397,16 @@ impl Condition {
_ => unreachable!("Cannot happen because of normalization"), _ => unreachable!("Cannot happen because of normalization"),
}, },
Self::And(c1, c2) => { Self::And(c1, c2) => {
let c1 = c1.compile(_options, _global_state, _function_state); let c1 = c1.compile(options, global_state, function_state);
let c2 = c2.compile(_options, _global_state, _function_state); let c2 = c2.compile(options, global_state, function_state);
c1.into_iter() c1.into_iter()
.flat_map(|c1| c2.iter().map(move |c2| c1.clone() + " " + c2)) .flat_map(|c1| c2.iter().map(move |c2| c1.clone() + " " + c2))
.collect() .collect()
} }
Self::Or(c1, c2) => { Self::Or(c1, c2) => {
let mut c1 = c1.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); let c2 = c2.compile(options, global_state, function_state);
c1.extend(c2); c1.extend(c2);
c1 c1
} }
@ -408,7 +416,7 @@ impl Condition {
impl From<&str> for Condition { impl From<&str> for Condition {
fn from(s: &str) -> Self { 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; type Output = Self;
fn not(self) -> Self { fn not(self) -> Self {
Condition::Not(Box::new(self)) Self::Not(Box::new(self))
} }
} }
impl BitAnd for Condition { impl BitAnd for Condition {
type Output = Self; type Output = Self;
fn bitand(self, rhs: Self) -> 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 { impl BitOr for Condition {
type Output = Self; type Output = Self;
fn bitor(self, rhs: Self) -> 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!( assert_eq!(
(c1.clone() & c2.clone() | c3.clone() & c1.clone()).normalize(), (c1.clone() & c2.clone() | c3.clone() & c1.clone()).normalize(),
c1.clone() & c2.clone() | c3.clone() & c1.clone() c1.clone() & c2.clone() | c3 & c1.clone()
);
assert_eq!(
(!(c1.clone() | c2.clone())).normalize(),
!c1.clone() & !c2.clone()
); );
assert_eq!((!(c1.clone() | c2.clone())).normalize(), !c1 & !c2);
} }
} }

View File

@ -5,14 +5,14 @@ mod execute;
pub use execute::{Condition, Execute}; pub use execute::{Condition, Execute};
use chksum_md5 as md5; use chksum_md5 as md5;
use serde::{Deserialize, Serialize};
use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}; use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState};
use super::Function; use super::Function;
/// Represents a command that can be included in a 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 { pub enum Command {
/// A command that is already formatted as a string. /// A command that is already formatted as a string.
Raw(String), Raw(String),
@ -28,6 +28,7 @@ pub enum Command {
impl Command { impl Command {
/// Create a new raw command. /// Create a new raw command.
#[must_use]
pub fn raw(command: &str) -> Self { pub fn raw(command: &str) -> Self {
Self::Raw(command.to_string()) Self::Raw(command.to_string())
} }

View File

@ -1,7 +1,6 @@
//! Function struct and implementation //! Function struct and implementation
use getset::Getters; use getset::Getters;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
@ -11,7 +10,8 @@ use crate::{
use super::command::Command; use super::command::Command;
/// Function that can be called by a 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 { pub struct Function {
commands: Vec<Command>, commands: Vec<Command>,
/// Name of the function /// Name of the function
@ -36,6 +36,7 @@ impl Function {
} }
/// Get the commands of the function. /// Get the commands of the function.
#[must_use]
pub fn get_commands(&self) -> &Vec<Command> { pub fn get_commands(&self) -> &Vec<Command> {
&self.commands &self.commands
} }

View File

@ -7,7 +7,6 @@ pub mod tag;
pub use command::{Command, Condition, Execute}; pub use command::{Command, Condition, Execute};
pub use function::Function; pub use function::Function;
pub use namespace::Namespace; pub use namespace::Namespace;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, ops::RangeInclusive, path::Path, sync::Mutex}; use std::{collections::HashMap, ops::RangeInclusive, path::Path, sync::Mutex};
@ -17,7 +16,8 @@ use crate::{
}; };
/// A Minecraft datapack. /// A Minecraft datapack.
#[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct Datapack { pub struct Datapack {
// TODO: Support filter and overlays // TODO: Support filter and overlays
name: String, name: String,
@ -32,6 +32,7 @@ pub struct Datapack {
impl Datapack { impl Datapack {
/// Create a new Minecraft datapack. /// Create a new Minecraft datapack.
#[must_use]
pub fn new(name: &str, pack_format: u8) -> Self { pub fn new(name: &str, pack_format: u8) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
@ -46,6 +47,7 @@ impl Datapack {
} }
/// Set the description of the datapack. /// Set the description of the datapack.
#[must_use]
pub fn with_description(self, description: &str) -> Self { pub fn with_description(self, description: &str) -> Self {
Self { Self {
description: description.to_string(), description: description.to_string(),
@ -54,6 +56,7 @@ impl Datapack {
} }
/// Set the supported pack formats of the datapack. /// Set the supported pack formats of the datapack.
#[must_use]
pub fn with_supported_formats(self, supported_formats: RangeInclusive<u8>) -> Self { pub fn with_supported_formats(self, supported_formats: RangeInclusive<u8>) -> Self {
Self { Self {
supported_formats: Some(supported_formats), supported_formats: Some(supported_formats),
@ -62,6 +65,9 @@ impl Datapack {
} }
/// Set the custom files of the 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> { pub fn with_template_folder(self, path: &Path) -> std::io::Result<Self> {
let mut template = VFolder::try_from(path)?; let mut template = VFolder::try_from(path)?;
template.merge(self.custom_files); template.merge(self.custom_files);
@ -73,6 +79,7 @@ impl Datapack {
} }
/// Get a namespace by name. /// Get a namespace by name.
#[must_use]
pub fn namespace(&self, name: &str) -> Option<&Namespace> { pub fn namespace(&self, name: &str) -> Option<&Namespace> {
self.namespaces.get(name) self.namespaces.get(name)
} }
@ -100,6 +107,7 @@ impl Datapack {
} }
/// Compile the pack into a virtual folder. /// Compile the pack into a virtual folder.
#[must_use]
pub fn compile(&self, options: &CompileOptions) -> VFolder { pub fn compile(&self, options: &CompileOptions) -> VFolder {
let compiler_state = Mutex::new(CompilerState::default()); 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!({ content["pack"]["supported_formats"] = serde_json::json!({
"min_inclusive": *supported_formats.start(), "min_inclusive": *supported_formats.start(),
"max_inclusive": *supported_formats.end() "max_inclusive": *supported_formats.end()
}) });
} }
VFile::Text(content.to_string()) VFile::Text(content.to_string())

View File

@ -1,11 +1,9 @@
//! Namespace of a datapack //! Namespace of a datapack
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
util::{ util::{
compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
extendable_queue::ExtendableQueue, ExtendableQueue,
}, },
virtual_fs::VFolder, virtual_fs::VFolder,
}; };
@ -14,7 +12,8 @@ use super::{function::Function, tag::Tag};
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
/// Namespace of a datapack /// Namespace of a datapack
#[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct Namespace { pub struct Namespace {
name: String, name: String,
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
@ -34,11 +33,13 @@ impl Namespace {
} }
/// Get the name of the namespace. /// Get the name of the namespace.
#[must_use]
pub fn get_name(&self) -> &str { pub fn get_name(&self) -> &str {
&self.name &self.name
} }
/// Get the main function of the namespace. /// Get the main function of the namespace.
#[must_use]
pub fn get_main_function(&self) -> &Function { pub fn get_main_function(&self) -> &Function {
&self.main_function &self.main_function
} }
@ -48,16 +49,19 @@ impl Namespace {
} }
/// Get the functions of the namespace. /// Get the functions of the namespace.
#[must_use]
pub fn get_functions(&self) -> &HashMap<String, Function> { pub fn get_functions(&self) -> &HashMap<String, Function> {
&self.functions &self.functions
} }
/// Get the tags of the namespace. /// Get the tags of the namespace.
#[must_use]
pub fn get_tags(&self) -> &HashMap<String, Tag> { pub fn get_tags(&self) -> &HashMap<String, Tag> {
&self.tags &self.tags
} }
/// Get a function by name. /// Get a function by name.
#[must_use]
pub fn function(&self, name: &str) -> Option<&Function> { pub fn function(&self, name: &str) -> Option<&Function> {
self.functions.get(name) self.functions.get(name)
} }
@ -94,7 +98,7 @@ impl Namespace {
while let Some((path, function)) = functions.next() { while let Some((path, function)) = functions.next() {
let function_state = FunctionCompilerState::new(&path, &self.name, functions.clone()); let function_state = FunctionCompilerState::new(&path, &self.name, functions.clone());
root_folder.add_file( root_folder.add_file(
&format!("functions/{}.mcfunction", path), &format!("functions/{path}.mcfunction"),
function.compile(options, state, &function_state), function.compile(options, state, &function_state),
); );
} }
@ -102,7 +106,7 @@ impl Namespace {
// Compile tags // Compile tags
for (path, tag) in &self.tags { for (path, tag) in &self.tags {
let (tag_type, vfile) = tag.compile(options, state); 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 root_folder

View File

@ -1,14 +1,13 @@
//! A tag for various types. //! A tag for various types.
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
util::compile::{CompileOptions, MutCompilerState}, util::compile::{CompileOptions, MutCompilerState},
virtual_fs::VFile, virtual_fs::VFile,
}; };
/// A tag for various types. /// 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 { pub struct Tag {
r#type: TagType, r#type: TagType,
replace: bool, replace: bool,
@ -16,6 +15,7 @@ pub struct Tag {
} }
impl Tag { impl Tag {
/// Create a new tag. /// Create a new tag.
#[must_use]
pub fn new(r#type: TagType, replace: bool) -> Self { pub fn new(r#type: TagType, replace: bool) -> Self {
Self { Self {
r#type, r#type,
@ -48,7 +48,9 @@ impl Tag {
} }
/// The type of a 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 { pub enum TagType {
/// A tag for blocks. /// A tag for blocks.
Blocks, Blocks,
@ -81,7 +83,9 @@ impl ToString for TagType {
} }
/// The value of a tag. /// 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 { pub enum TagValue {
/// A simple value, either a resource location or an id of another tag. /// A simple value, either a resource location or an id of another tag.
Simple(String), Simple(String),
@ -100,6 +104,7 @@ impl From<&str> for TagValue {
} }
impl TagValue { impl TagValue {
/// Compile the tag value into a JSON value. /// Compile the tag value into a JSON value.
#[must_use]
pub fn compile(&self) -> serde_json::Value { pub fn compile(&self) -> serde_json::Value {
match self { match self {
Self::Simple(value) => serde_json::Value::String(value.clone()), Self::Simple(value) => serde_json::Value::String(value.clone()),

View File

@ -1,14 +1,15 @@
//! Shulkerbox is a library for creating Minecraft data packs. //! Shulkerbox is a library for creating Minecraft data packs.
#![warn( #![deny(
missing_docs, unsafe_code,
missing_debug_implementations, missing_debug_implementations,
nonstandard_style, missing_copy_implementations,
clippy::complexity, clippy::nursery,
clippy::style, rustdoc::broken_intra_doc_links,
clippy::suspicious 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 datapack;
pub mod util; pub mod util;

View File

@ -3,14 +3,15 @@
use std::{cell::Cell, sync::Mutex}; use std::{cell::Cell, sync::Mutex};
use getset::Getters; use getset::Getters;
use serde::{Deserialize, Serialize};
use crate::datapack::Function; use crate::datapack::Function;
use super::extendable_queue::ExtendableQueue; use super::extendable_queue::ExtendableQueue;
/// Compile options for the compiler. /// 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 { pub struct CompileOptions {
/// Whether to compile in debug mode. /// Whether to compile in debug mode.
pub debug: bool, pub debug: bool,
@ -23,7 +24,9 @@ impl Default for CompileOptions {
} }
/// State of the compiler that can change during compilation. /// 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 {} pub struct CompilerState {}
/// Mutex for the compiler state. /// Mutex for the compiler state.
pub type MutCompilerState = Mutex<CompilerState>; pub type MutCompilerState = Mutex<CompilerState>;
@ -48,6 +51,7 @@ type FunctionQueue = ExtendableQueue<(String, Function)>;
impl FunctionCompilerState { impl FunctionCompilerState {
/// Create a new function compiler state. /// Create a new function compiler state.
#[must_use]
pub fn new(path: &str, namespace: &str, functions: FunctionQueue) -> Self { pub fn new(path: &str, namespace: &str, functions: FunctionQueue) -> Self {
Self { Self {
generated_functions: Cell::new(0), generated_functions: Cell::new(0),

View File

@ -37,11 +37,13 @@ impl<T> ExtendableQueue<T> {
} }
/// Get the queue. /// Get the queue.
#[must_use]
pub fn get(&self) -> &Arc<RwLock<VecDeque<T>>> { pub fn get(&self) -> &Arc<RwLock<VecDeque<T>>> {
&self.queue &self.queue
} }
/// Get a weak reference to the queue. /// Get a weak reference to the queue.
#[must_use]
pub fn get_weak(&self) -> Weak<RwLock<VecDeque<T>>> { pub fn get_weak(&self) -> Weak<RwLock<VecDeque<T>>> {
Arc::downgrade(&self.queue) Arc::downgrade(&self.queue)
} }
@ -52,16 +54,19 @@ impl<T> ExtendableQueue<T> {
} }
/// Get the length of the queue. /// Get the length of the queue.
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.queue.read().unwrap().len() self.queue.read().unwrap().len()
} }
/// Check if the queue is empty. /// Check if the queue is empty.
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.queue.read().unwrap().is_empty() self.queue.read().unwrap().is_empty()
} }
/// Get and remove the next item without needing mutable access. /// Get and remove the next item without needing mutable access.
#[must_use]
pub fn pop_front(&self) -> Option<T> { pub fn pop_front(&self) -> Option<T> {
self.queue.write().unwrap().pop_front() self.queue.write().unwrap().pop_front()
} }

View File

@ -1,4 +1,7 @@
//! Utility functions for the Shulkerbox project. //! Utility functions for the Shulkerbox project.
pub mod compile; pub mod compile;
pub mod extendable_queue; mod extendable_queue;
#[doc(inline)]
pub use extendable_queue::ExtendableQueue;

View File

@ -2,49 +2,51 @@
use std::{collections::HashMap, fs, io, path::Path}; use std::{collections::HashMap, fs, io, path::Path};
use serde::{Deserialize, Serialize};
#[cfg(feature = "zip")] #[cfg(feature = "zip")]
use zip::ZipWriter; use zip::ZipWriter;
/// Folder representation in virtual file system /// 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 { pub struct VFolder {
folders: HashMap<String, VFolder>, folders: HashMap<String, VFolder>,
files: HashMap<String, VFile>, files: HashMap<String, VFile>,
} }
impl VFolder { impl VFolder {
/// Create a new, empty virtual folder. /// Create a new, empty virtual folder.
pub fn new() -> VFolder { #[must_use]
VFolder { pub fn new() -> Self {
Self {
folders: HashMap::new(), folders: HashMap::new(),
files: HashMap::new(), files: HashMap::new(),
} }
} }
/// Get all direct subfolders in the folder. /// 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 &self.folders
} }
/// Get all direct files in the folder. /// Get all direct files in the folder.
#[must_use]
pub fn get_files(&self) -> &HashMap<String, VFile> { pub fn get_files(&self) -> &HashMap<String, VFile> {
&self.files &self.files
} }
/// Recursively add a new folder to the folder. /// Recursively add a new folder to the folder.
pub fn add_folder(&mut self, path: &str) { 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. /// 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 let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
if let Some(subfolder) = self.get_folder_mut(head) { if let Some(subfolder) = self.get_folder_mut(head) {
subfolder.add_folder(tail); subfolder.add_folder(tail);
} else { } else {
let mut new_folder = VFolder::new(); let mut new_folder = Self::new();
new_folder.add_folder(tail); new_folder.add_folder(tail);
self.add_existing_folder(head, new_folder); self.add_existing_folder(head, new_folder);
} }
@ -56,13 +58,12 @@ impl VFolder {
pub fn add_file(&mut self, path: &str, file: VFile) { pub fn add_file(&mut self, path: &str, file: VFile) {
let (head, tail) = path let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
if let Some(subfolder) = self.get_folder_mut(head) { if let Some(subfolder) = self.get_folder_mut(head) {
subfolder.add_file(tail, file); subfolder.add_file(tail, file);
} else { } else {
let mut new_folder = VFolder::new(); let mut new_folder = Self::new();
new_folder.add_file(tail, file); new_folder.add_file(tail, file);
self.add_existing_folder(head, new_folder); self.add_existing_folder(head, new_folder);
} }
@ -72,11 +73,11 @@ impl VFolder {
} }
/// Recursively get a subfolder by path. /// 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 let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
self.folders.get(head)?.get_folder(tail) self.folders.get(head)?.get_folder(tail)
} else { } else {
@ -84,11 +85,10 @@ impl VFolder {
} }
} }
/// Recursively get a mutable subfolder by path. /// 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 let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
self.folders.get_mut(head)?.get_folder_mut(tail) self.folders.get_mut(head)?.get_folder_mut(tail)
} else { } else {
@ -96,11 +96,11 @@ impl VFolder {
} }
} }
/// Recursively get a file by path. /// Recursively get a file by path.
#[must_use]
pub fn get_file(&self, path: &str) -> Option<&VFile> { pub fn get_file(&self, path: &str) -> Option<&VFile> {
let (head, tail) = path let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
self.folders.get(head)?.get_file(tail) self.folders.get(head)?.get_file(tail)
} else { } else {
@ -111,8 +111,7 @@ impl VFolder {
pub fn get_file_mut(&mut self, path: &str) -> Option<&mut VFile> { pub fn get_file_mut(&mut self, path: &str) -> Option<&mut VFile> {
let (head, tail) = path let (head, tail) = path
.split_once('/') .split_once('/')
.map(|(h, t)| (h, (!t.is_empty()).then_some(t))) .map_or((path, None), |(h, t)| (h, (!t.is_empty()).then_some(t)));
.unwrap_or((path, None));
if let Some(tail) = tail { if let Some(tail) = tail {
self.folders.get_mut(head)?.get_file_mut(tail) self.folders.get_mut(head)?.get_file_mut(tail)
} else { } else {
@ -121,6 +120,9 @@ impl VFolder {
} }
/// Place the folder and its contents on the file system. /// 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<()> { pub fn place(&self, path: &Path) -> io::Result<()> {
fs::create_dir_all(path)?; fs::create_dir_all(path)?;
for (name, folder) in &self.folders { for (name, folder) in &self.folders {
@ -141,6 +143,9 @@ impl VFolder {
#[cfg(feature = "zip")] #[cfg(feature = "zip")]
/// Zip the folder and its contents into a zip archive. /// 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<()> { pub fn zip(&self, path: &Path) -> io::Result<()> {
use io::Write; use io::Write;
@ -149,7 +154,7 @@ impl VFolder {
let virtual_files = self.flatten(); let virtual_files = self.flatten();
for (path, file) in virtual_files { for (path, file) in virtual_files {
writer.start_file(path, Default::default())?; writer.start_file(path, zip::write::SimpleFileOptions::default())?;
match file { match file {
VFile::Text(text) => { VFile::Text(text) => {
writer.write_all(text.as_bytes())?; 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. /// Flatten the folder and its contents into a list of files with full paths.
#[must_use]
pub fn flatten(&self) -> Vec<(String, &VFile)> { pub fn flatten(&self) -> Vec<(String, &VFile)> {
let mut files = self let mut files = self
.files .files
@ -177,7 +183,7 @@ impl VFolder {
let sub_files = folder let sub_files = folder
.flatten() .flatten()
.into_iter() .into_iter()
.map(|(path, file)| (format!("{}/{}", name, path), file)) .map(|(path, file)| (format!("{name}/{path}"), file))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
files.extend(sub_files); files.extend(sub_files);
} }
@ -204,19 +210,15 @@ impl TryFrom<&Path> for VFolder {
type Error = io::Error; type Error = io::Error;
fn try_from(value: &Path) -> Result<Self, Self::Error> { fn try_from(value: &Path) -> Result<Self, Self::Error> {
let mut root_vfolder = VFolder::new(); let mut root_vfolder = Self::new();
let root_folder = fs::read_dir(value)?; let fs_root_folder = fs::read_dir(value)?;
for dir_entry in root_folder { for dir_entry in fs_root_folder {
let dir_entry = dir_entry?; let dir_entry = dir_entry?;
let path = dir_entry.path(); let path = dir_entry.path();
let name = dir_entry let name = dir_entry.file_name().into_string().ok();
.file_name()
.into_string()
.map(Some)
.unwrap_or_default();
if let Some(name) = name { if let Some(name) = name {
if path.is_dir() { 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() { } else if path.is_file() {
let data = fs::read(path)?; let data = fs::read(path)?;
root_vfolder.add_file(&name, VFile::Binary(data)); root_vfolder.add_file(&name, VFile::Binary(data));
@ -236,7 +238,8 @@ impl TryFrom<&Path> for VFolder {
} }
/// File representation in virtual file system /// 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 { pub enum VFile {
/// Text file /// Text file
Text(String), Text(String),
@ -246,17 +249,17 @@ pub enum VFile {
impl From<String> for VFile { impl From<String> for VFile {
fn from(value: String) -> Self { fn from(value: String) -> Self {
VFile::Text(value) Self::Text(value)
} }
} }
impl From<&str> for VFile { impl From<&str> for VFile {
fn from(value: &str) -> Self { fn from(value: &str) -> Self {
VFile::Text(value.to_string()) Self::Text(value.to_string())
} }
} }
impl Default for VFile { impl Default for VFile {
fn default() -> Self { fn default() -> Self {
VFile::Text(String::new()) Self::Text(String::new())
} }
} }