diff --git a/Cargo.toml b/Cargo.toml index fca809d..a018eb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } diff --git a/src/datapack/command/execute.rs b/src/datapack/command/execute.rs index 7314f8e..03d6e7c 100644 --- a/src/datapack/command/execute.rs +++ b/src/datapack/command/execute.rs @@ -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), Anchored(String, Box), @@ -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, - 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), @@ -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 { 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); } } diff --git a/src/datapack/command/mod.rs b/src/datapack/command/mod.rs index 1e14e2f..f910d11 100644 --- a/src/datapack/command/mod.rs +++ b/src/datapack/command/mod.rs @@ -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()) } diff --git a/src/datapack/function.rs b/src/datapack/function.rs index 51dea10..923f036 100644 --- a/src/datapack/function.rs +++ b/src/datapack/function.rs @@ -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, /// Name of the function @@ -36,6 +36,7 @@ impl Function { } /// Get the commands of the function. + #[must_use] pub fn get_commands(&self) -> &Vec { &self.commands } diff --git a/src/datapack/mod.rs b/src/datapack/mod.rs index 335e258..3ab5276 100644 --- a/src/datapack/mod.rs +++ b/src/datapack/mod.rs @@ -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) -> 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 { 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()) diff --git a/src/datapack/namespace.rs b/src/datapack/namespace.rs index bc79c90..f4b4f0a 100644 --- a/src/datapack/namespace.rs +++ b/src/datapack/namespace.rs @@ -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, @@ -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 { &self.functions } /// Get the tags of the namespace. + #[must_use] pub fn get_tags(&self) -> &HashMap { &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 diff --git a/src/datapack/tag.rs b/src/datapack/tag.rs index a57ac36..270424b 100644 --- a/src/datapack/tag.rs +++ b/src/datapack/tag.rs @@ -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()), diff --git a/src/lib.rs b/src/lib.rs index cacfa40..630506b 100644 --- a/src/lib.rs +++ b/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; diff --git a/src/util/compile.rs b/src/util/compile.rs index 7d76d18..4f4d9a1 100644 --- a/src/util/compile.rs +++ b/src/util/compile.rs @@ -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; @@ -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), diff --git a/src/util/extendable_queue.rs b/src/util/extendable_queue.rs index bdda66b..76420cd 100644 --- a/src/util/extendable_queue.rs +++ b/src/util/extendable_queue.rs @@ -37,11 +37,13 @@ impl ExtendableQueue { } /// Get the queue. + #[must_use] pub fn get(&self) -> &Arc>> { &self.queue } /// Get a weak reference to the queue. + #[must_use] pub fn get_weak(&self) -> Weak>> { Arc::downgrade(&self.queue) } @@ -52,16 +54,19 @@ impl ExtendableQueue { } /// 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 { self.queue.write().unwrap().pop_front() } diff --git a/src/util/mod.rs b/src/util/mod.rs index 538f1c5..78121e0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -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; diff --git a/src/virtual_fs.rs b/src/virtual_fs.rs index 1e453fb..786983d 100644 --- a/src/virtual_fs.rs +++ b/src/virtual_fs.rs @@ -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, files: HashMap, } 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 { + #[must_use] + pub fn get_folders(&self) -> &HashMap { &self.folders } /// Get all direct files in the folder. + #[must_use] pub fn get_files(&self) -> &HashMap { &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::>(); files.extend(sub_files); } @@ -204,19 +210,15 @@ impl TryFrom<&Path> for VFolder { type Error = io::Error; fn try_from(value: &Path) -> Result { - 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 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()) } }