mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-25 19:54:07 +03:00
fix(utils): fix resources map becomes directory (#10293)
* fix(utils): fix resources map becomes directory closes #10187 Fixes the behavior of mapped resources generating extra directory, for example: `"../resources/user.json": "resources/user.json"` generates this resource `resources/user.json/user.json` where it should generate `resources/user.json` This PR includes a refactor of the Iterator implementation which splits it into more scoped functions and relis on recursing instead of a loop which makes the code a lot more readable and easier to maintain. * clippy * cover more cases * clippy * fix glob into directory, not resolving target correctly * return error when resource origin path doesn't exist * fix resources example build * Update .changes/resources-map-becoming-dirs.md --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
parent
8deb1966ac
commit
9e891933d8
5
.changes/resources-map-becoming-dirs.md
Normal file
5
.changes/resources-map-becoming-dirs.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-utils": "patch:bug"
|
||||
---
|
||||
|
||||
Fix `ResourcePaths` iterator returning an unexpected result for mapped resources, for example `"../resources/user.json": "resources/user.json"` generates this resource `resources/user.json/user.json` where it should generate just `resources/user.json`.
|
58
Cargo.lock
generated
58
Cargo.lock
generated
@ -1005,6 +1005,21 @@ dependencies = [
|
||||
"new_debug_unreachable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
@ -1012,6 +1027,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1066,6 +1082,7 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
@ -3174,6 +3191,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scc"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f3281c67bce3cc354216537112a1571d2c28b9e7d744a07ef79b43fad64386c"
|
||||
dependencies = [
|
||||
"sdd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
@ -3220,6 +3246,12 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sdd"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c386cfeafd20018fe07344e72dc4787f3432911e6c35d399457d86d2f146c4"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.0"
|
||||
@ -3388,6 +3420,31 @@ dependencies = [
|
||||
"syn 2.0.74",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"scc",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.74",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serialize-to-javascript"
|
||||
version = "0.1.1"
|
||||
@ -3955,6 +4012,7 @@ dependencies = [
|
||||
"serde-untagged",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serial_test",
|
||||
"serialize-to-javascript",
|
||||
"swift-rs",
|
||||
"thiserror",
|
||||
|
@ -47,6 +47,10 @@ serde-untagged = "0.1"
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
swift-rs = { version = "1.0.6", optional = true, features = [ "build" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
getrandom = { version = "0.2", features = [ "std" ] }
|
||||
serial_test = "3.1"
|
||||
|
||||
[features]
|
||||
build = [
|
||||
"proc-macro2",
|
||||
|
@ -372,6 +372,10 @@ pub enum Error {
|
||||
#[cfg(feature = "resources")]
|
||||
#[error("could not walk directory `{0}`, try changing `allow_walk` to true on the `ResourcePaths` constructor.")]
|
||||
NotAllowedToWalkDir(std::path::PathBuf),
|
||||
/// Resourece path doesn't exist
|
||||
#[cfg(feature = "resources")]
|
||||
#[error("resource path `{0}` doesn't exist")]
|
||||
ResourcePathNotFound(std::path::PathBuf),
|
||||
}
|
||||
|
||||
/// Reconstructs a path from its components using the platform separator then converts it to String and removes UNC prefixes on Windows if it exists.
|
||||
|
@ -7,6 +7,8 @@ use std::{
|
||||
path::{Component, Path, PathBuf},
|
||||
};
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// Given a path (absolute or relative) to a resource file, returns the
|
||||
/// relative path from the bundle resources directory where that resource
|
||||
/// should be stored.
|
||||
@ -24,6 +26,20 @@ pub fn resource_relpath(path: &Path) -> PathBuf {
|
||||
dest
|
||||
}
|
||||
|
||||
fn normalize(path: &Path) -> PathBuf {
|
||||
let mut dest = PathBuf::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Prefix(_) => {}
|
||||
Component::RootDir => dest.push("/"),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => dest.push(".."),
|
||||
Component::Normal(string) => dest.push(string),
|
||||
}
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
/// Parses the external binaries to bundle, adding the target triple suffix to each of them.
|
||||
pub fn external_binaries(external_binaries: &[String], target_triple: &str) -> Vec<String> {
|
||||
let mut paths = Vec::new();
|
||||
@ -42,73 +58,8 @@ pub fn external_binaries(external_binaries: &[String], target_triple: &str) -> V
|
||||
paths
|
||||
}
|
||||
|
||||
enum PatternIter<'a> {
|
||||
Slice(std::slice::Iter<'a, String>),
|
||||
Map(std::collections::hash_map::Iter<'a, String, String>),
|
||||
}
|
||||
|
||||
/// A helper to iterate through resources.
|
||||
pub struct ResourcePaths<'a> {
|
||||
iter: ResourcePathsIter<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ResourcePaths<'a> {
|
||||
/// Creates a new ResourcePaths from a slice of patterns to iterate
|
||||
pub fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
|
||||
ResourcePaths {
|
||||
iter: ResourcePathsIter {
|
||||
pattern_iter: PatternIter::Slice(patterns.iter()),
|
||||
glob_iter: None,
|
||||
walk_iter: None,
|
||||
allow_walk,
|
||||
current_pattern: None,
|
||||
current_pattern_is_valid: false,
|
||||
current_dest: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new ResourcePaths from a slice of patterns to iterate
|
||||
pub fn from_map(patterns: &'a HashMap<String, String>, allow_walk: bool) -> ResourcePaths<'a> {
|
||||
ResourcePaths {
|
||||
iter: ResourcePathsIter {
|
||||
pattern_iter: PatternIter::Map(patterns.iter()),
|
||||
glob_iter: None,
|
||||
walk_iter: None,
|
||||
allow_walk,
|
||||
current_pattern: None,
|
||||
current_pattern_is_valid: false,
|
||||
current_dest: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the resource iterator that yields the source and target paths.
|
||||
/// Needed when using [`Self::from_map`].
|
||||
pub fn iter(self) -> ResourcePathsIter<'a> {
|
||||
self.iter
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator of a [`ResourcePaths`].
|
||||
pub struct ResourcePathsIter<'a> {
|
||||
/// the patterns to iterate.
|
||||
pattern_iter: PatternIter<'a>,
|
||||
/// the glob iterator if the path from the current iteration is a glob pattern.
|
||||
glob_iter: Option<glob::Paths>,
|
||||
/// the walkdir iterator if the path from the current iteration is a directory.
|
||||
walk_iter: Option<walkdir::IntoIter>,
|
||||
/// whether the resource paths allows directories or not.
|
||||
allow_walk: bool,
|
||||
/// the pattern of the current iteration.
|
||||
current_pattern: Option<(String, PathBuf)>,
|
||||
/// whether the current pattern is valid or not.
|
||||
current_pattern_is_valid: bool,
|
||||
/// Current destination path. Only set when the iterator comes from a Map.
|
||||
current_dest: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Information for a resource.
|
||||
#[derive(Debug)]
|
||||
pub struct Resource {
|
||||
path: PathBuf,
|
||||
target: PathBuf,
|
||||
@ -126,6 +77,196 @@ impl Resource {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PatternIter<'a> {
|
||||
Slice(std::slice::Iter<'a, String>),
|
||||
Map(std::collections::hash_map::Iter<'a, String, String>),
|
||||
}
|
||||
|
||||
/// A helper to iterate through resources.
|
||||
pub struct ResourcePaths<'a> {
|
||||
iter: ResourcePathsIter<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ResourcePaths<'a> {
|
||||
/// Creates a new ResourcePaths from a slice of patterns to iterate
|
||||
pub fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
|
||||
ResourcePaths {
|
||||
iter: ResourcePathsIter {
|
||||
pattern_iter: PatternIter::Slice(patterns.iter()),
|
||||
allow_walk,
|
||||
current_path: None,
|
||||
current_pattern: None,
|
||||
current_dest: None,
|
||||
walk_iter: None,
|
||||
glob_iter: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new ResourcePaths from a slice of patterns to iterate
|
||||
pub fn from_map(patterns: &'a HashMap<String, String>, allow_walk: bool) -> ResourcePaths<'a> {
|
||||
ResourcePaths {
|
||||
iter: ResourcePathsIter {
|
||||
pattern_iter: PatternIter::Map(patterns.iter()),
|
||||
allow_walk,
|
||||
current_path: None,
|
||||
current_pattern: None,
|
||||
current_dest: None,
|
||||
walk_iter: None,
|
||||
glob_iter: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the resource iterator that yields the source and target paths.
|
||||
/// Needed when using [`Self::from_map`].
|
||||
pub fn iter(self) -> ResourcePathsIter<'a> {
|
||||
self.iter
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator of a [`ResourcePaths`].
|
||||
#[derive(Debug)]
|
||||
pub struct ResourcePathsIter<'a> {
|
||||
/// the patterns to iterate.
|
||||
pattern_iter: PatternIter<'a>,
|
||||
/// whether the resource paths allows directories or not.
|
||||
allow_walk: bool,
|
||||
|
||||
current_path: Option<PathBuf>,
|
||||
current_pattern: Option<String>,
|
||||
current_dest: Option<PathBuf>,
|
||||
|
||||
walk_iter: Option<walkdir::IntoIter>,
|
||||
glob_iter: Option<glob::Paths>,
|
||||
}
|
||||
|
||||
impl<'a> ResourcePathsIter<'a> {
|
||||
fn next_glob_iter(&mut self) -> Option<crate::Result<Resource>> {
|
||||
let entry = self.glob_iter.as_mut().unwrap().next()?;
|
||||
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => return Some(Err(err.into())),
|
||||
};
|
||||
|
||||
self.current_path = Some(normalize(&entry));
|
||||
self.next_current_path()
|
||||
}
|
||||
|
||||
fn next_walk_iter(&mut self) -> Option<crate::Result<Resource>> {
|
||||
let entry = self.walk_iter.as_mut().unwrap().next()?;
|
||||
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => return Some(Err(err.into())),
|
||||
};
|
||||
|
||||
self.current_path = Some(normalize(entry.path()));
|
||||
self.next_current_path()
|
||||
}
|
||||
|
||||
fn resource_from_path(&mut self, path: &Path) -> crate::Result<Resource> {
|
||||
if !path.exists() {
|
||||
return Err(crate::Error::ResourcePathNotFound(path.to_path_buf()));
|
||||
}
|
||||
|
||||
Ok(Resource {
|
||||
path: path.to_path_buf(),
|
||||
target: self
|
||||
.current_dest
|
||||
.as_ref()
|
||||
.map(|current_dest| {
|
||||
// if processing a directory, preserve directory structure under current_dest
|
||||
if self.walk_iter.is_some() {
|
||||
let current_pattern = self.current_pattern.as_ref().unwrap();
|
||||
current_dest.join(path.strip_prefix(current_pattern).unwrap_or(path))
|
||||
} else if current_dest.components().count() == 0 {
|
||||
// if current_dest is empty while processing a file pattern or glob
|
||||
// we preserve the file name as it is
|
||||
PathBuf::from(path.file_name().unwrap())
|
||||
} else if self.glob_iter.is_some() {
|
||||
// if processing a glob and current_dest is not empty
|
||||
// we put all globbed paths under current_dest
|
||||
// preserving the file name as it is
|
||||
current_dest.join(path.file_name().unwrap())
|
||||
} else {
|
||||
current_dest.clone()
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| resource_relpath(path)),
|
||||
})
|
||||
}
|
||||
|
||||
fn next_current_path(&mut self) -> Option<crate::Result<Resource>> {
|
||||
// should be safe to unwrap since every call to `self.next_current_path()`
|
||||
// is preceeded with assignemt to `self.current_path`
|
||||
let path = self.current_path.take().unwrap();
|
||||
|
||||
let is_dir = path.is_dir();
|
||||
|
||||
if is_dir {
|
||||
if self.glob_iter.is_some() {
|
||||
return self.next();
|
||||
}
|
||||
|
||||
if !self.allow_walk {
|
||||
return Some(Err(crate::Error::NotAllowedToWalkDir(path.to_path_buf())));
|
||||
}
|
||||
|
||||
if self.walk_iter.is_none() {
|
||||
self.walk_iter = Some(WalkDir::new(&path).into_iter());
|
||||
}
|
||||
|
||||
match self.next_walk_iter() {
|
||||
Some(resource) => Some(resource),
|
||||
None => {
|
||||
self.walk_iter = None;
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(self.resource_from_path(&path))
|
||||
}
|
||||
}
|
||||
|
||||
fn next_pattern(&mut self) -> Option<crate::Result<Resource>> {
|
||||
self.current_pattern = None;
|
||||
self.current_dest = None;
|
||||
self.current_path = None;
|
||||
|
||||
let pattern = match &mut self.pattern_iter {
|
||||
PatternIter::Slice(iter) => match iter.next() {
|
||||
Some(pattern) => pattern,
|
||||
None => return None,
|
||||
},
|
||||
PatternIter::Map(iter) => match iter.next() {
|
||||
Some((pattern, dest)) => {
|
||||
self.current_pattern = Some(pattern.clone());
|
||||
self.current_dest = Some(resource_relpath(Path::new(dest)));
|
||||
pattern
|
||||
}
|
||||
None => return None,
|
||||
},
|
||||
};
|
||||
|
||||
if pattern.contains('*') {
|
||||
self.glob_iter = match glob::glob(pattern) {
|
||||
Ok(glob) => Some(glob),
|
||||
Err(error) => return Some(Err(error.into())),
|
||||
};
|
||||
match self.next_glob_iter() {
|
||||
Some(r) => return Some(r),
|
||||
None => self.glob_iter = None,
|
||||
}
|
||||
}
|
||||
|
||||
self.current_path = Some(normalize(Path::new(pattern)));
|
||||
self.next_current_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ResourcePaths<'a> {
|
||||
type Item = crate::Result<PathBuf>;
|
||||
|
||||
@ -134,116 +275,279 @@ impl<'a> Iterator for ResourcePaths<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize(path: &Path) -> PathBuf {
|
||||
let mut dest = PathBuf::new();
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Prefix(_) => {}
|
||||
Component::RootDir => dest.push("/"),
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => dest.push(".."),
|
||||
Component::Normal(string) => dest.push(string),
|
||||
}
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ResourcePathsIter<'a> {
|
||||
type Item = crate::Result<Resource>;
|
||||
|
||||
fn next(&mut self) -> Option<crate::Result<Resource>> {
|
||||
loop {
|
||||
if let Some(ref mut walk_entries) = self.walk_iter {
|
||||
if let Some(entry) = walk_entries.next() {
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(error) => return Some(Err(crate::Error::from(error))),
|
||||
};
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
self.current_pattern_is_valid = true;
|
||||
return Some(Ok(Resource {
|
||||
target: if let (Some(current_dest), Some(current_pattern)) =
|
||||
(&self.current_dest, &self.current_pattern)
|
||||
{
|
||||
if current_pattern.0.contains('*') {
|
||||
current_dest.join(path.file_name().unwrap())
|
||||
} else {
|
||||
current_dest.join(path.strip_prefix(¤t_pattern.1).unwrap())
|
||||
}
|
||||
} else {
|
||||
resource_relpath(path)
|
||||
},
|
||||
path: path.to_path_buf(),
|
||||
}));
|
||||
}
|
||||
if self.current_path.is_some() {
|
||||
return self.next_current_path();
|
||||
}
|
||||
|
||||
if self.walk_iter.is_some() {
|
||||
match self.next_walk_iter() {
|
||||
Some(r) => return Some(r),
|
||||
None => self.walk_iter = None,
|
||||
}
|
||||
self.walk_iter = None;
|
||||
if let Some(ref mut glob_paths) = self.glob_iter {
|
||||
if let Some(glob_result) = glob_paths.next() {
|
||||
let path = match glob_result {
|
||||
Ok(path) => path,
|
||||
Err(error) => return Some(Err(error.into())),
|
||||
};
|
||||
if path.is_dir() {
|
||||
if self.allow_walk {
|
||||
let walk = walkdir::WalkDir::new(path);
|
||||
self.walk_iter = Some(walk.into_iter());
|
||||
continue;
|
||||
} else {
|
||||
return Some(Err(crate::Error::NotAllowedToWalkDir(path)));
|
||||
}
|
||||
}
|
||||
self.current_pattern_is_valid = true;
|
||||
return Some(Ok(Resource {
|
||||
target: if let Some(current_dest) = &self.current_dest {
|
||||
current_dest.join(path.file_name().unwrap())
|
||||
} else {
|
||||
resource_relpath(&path)
|
||||
},
|
||||
path,
|
||||
}));
|
||||
} else if let Some(current_path) = &self.current_pattern {
|
||||
if !self.current_pattern_is_valid {
|
||||
self.glob_iter = None;
|
||||
return Some(Err(crate::Error::GlobPathNotFound(current_path.0.clone())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.glob_iter.is_some() {
|
||||
match self.next_glob_iter() {
|
||||
Some(r) => return Some(r),
|
||||
None => self.glob_iter = None,
|
||||
}
|
||||
self.glob_iter = None;
|
||||
self.current_dest = None;
|
||||
match &mut self.pattern_iter {
|
||||
PatternIter::Slice(iter) => {
|
||||
if let Some(pattern) = iter.next() {
|
||||
self.current_pattern = Some((pattern.to_string(), normalize(Path::new(pattern))));
|
||||
self.current_pattern_is_valid = false;
|
||||
let glob = match glob::glob(pattern) {
|
||||
Ok(glob) => glob,
|
||||
Err(error) => return Some(Err(error.into())),
|
||||
};
|
||||
self.glob_iter = Some(glob);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
PatternIter::Map(iter) => {
|
||||
if let Some((pattern, dest)) = iter.next() {
|
||||
self.current_pattern = Some((pattern.to_string(), normalize(Path::new(pattern))));
|
||||
self.current_pattern_is_valid = false;
|
||||
let glob = match glob::glob(pattern) {
|
||||
Ok(glob) => glob,
|
||||
Err(error) => return Some(Err(error.into())),
|
||||
};
|
||||
self
|
||||
.current_dest
|
||||
.replace(resource_relpath(&PathBuf::from(dest)));
|
||||
self.glob_iter = Some(glob);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.next_pattern()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
impl PartialEq for Resource {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.path == other.path && self.target == other.target
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_resources(resources: &[(&str, &str)]) -> Vec<Resource> {
|
||||
resources
|
||||
.iter()
|
||||
.map(|(path, target)| Resource {
|
||||
path: Path::new(path).components().collect(),
|
||||
target: Path::new(target).components().collect(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn setup_test_dirs() {
|
||||
let mut random = [0; 1];
|
||||
getrandom::getrandom(&mut random).unwrap();
|
||||
|
||||
let temp = std::env::temp_dir();
|
||||
let temp = temp.join(format!("tauri_resource_paths_iter_test_{}", random[0]));
|
||||
|
||||
let _ = fs::remove_dir_all(&temp);
|
||||
fs::create_dir_all(&temp).unwrap();
|
||||
|
||||
std::env::set_current_dir(&temp).unwrap();
|
||||
|
||||
let paths = [
|
||||
Path::new("src-tauri/tauri.conf.json"),
|
||||
Path::new("src-tauri/some-other-json.json"),
|
||||
Path::new("src-tauri/Cargo.toml"),
|
||||
Path::new("src-tauri/Tauri.toml"),
|
||||
Path::new("src-tauri/build.rs"),
|
||||
Path::new("src/assets/javascript.svg"),
|
||||
Path::new("src/assets/tauri.svg"),
|
||||
Path::new("src/assets/rust.svg"),
|
||||
Path::new("src/assets/lang/en.json"),
|
||||
Path::new("src/assets/lang/ar.json"),
|
||||
Path::new("src/sounds/lang/es.wav"),
|
||||
Path::new("src/sounds/lang/fr.wav"),
|
||||
Path::new("src/textures/ground/earth.tex"),
|
||||
Path::new("src/textures/ground/sand.tex"),
|
||||
Path::new("src/textures/water.tex"),
|
||||
Path::new("src/textures/fire.tex"),
|
||||
Path::new("src/tiles/sky/grey.tile"),
|
||||
Path::new("src/tiles/sky/yellow.tile"),
|
||||
Path::new("src/tiles/grass.tile"),
|
||||
Path::new("src/tiles/stones.tile"),
|
||||
Path::new("src/index.html"),
|
||||
Path::new("src/style.css"),
|
||||
Path::new("src/script.js"),
|
||||
];
|
||||
|
||||
for path in paths {
|
||||
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
fs::write(path, "").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn resource_paths_iter_slice_allow_walk() {
|
||||
setup_test_dirs();
|
||||
|
||||
let dir = std::env::current_dir().unwrap().join("src-tauri");
|
||||
let _ = std::env::set_current_dir(dir);
|
||||
|
||||
let resources = ResourcePaths::new(
|
||||
&[
|
||||
"../src/script.js".into(),
|
||||
"../src/assets".into(),
|
||||
"../src/index.html".into(),
|
||||
"../src/sounds".into(),
|
||||
"*.toml".into(),
|
||||
"*.conf.json".into(),
|
||||
],
|
||||
true,
|
||||
)
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected = expected_resources(&[
|
||||
("../src/script.js", "_up_/src/script.js"),
|
||||
(
|
||||
"../src/assets/javascript.svg",
|
||||
"_up_/src/assets/javascript.svg",
|
||||
),
|
||||
("../src/assets/tauri.svg", "_up_/src/assets/tauri.svg"),
|
||||
("../src/assets/rust.svg", "_up_/src/assets/rust.svg"),
|
||||
("../src/assets/lang/en.json", "_up_/src/assets/lang/en.json"),
|
||||
("../src/assets/lang/ar.json", "_up_/src/assets/lang/ar.json"),
|
||||
("../src/index.html", "_up_/src/index.html"),
|
||||
("../src/sounds/lang/es.wav", "_up_/src/sounds/lang/es.wav"),
|
||||
("../src/sounds/lang/fr.wav", "_up_/src/sounds/lang/fr.wav"),
|
||||
("Cargo.toml", "Cargo.toml"),
|
||||
("Tauri.toml", "Tauri.toml"),
|
||||
("tauri.conf.json", "tauri.conf.json"),
|
||||
]);
|
||||
|
||||
assert_eq!(resources.len(), expected.len());
|
||||
for resource in expected {
|
||||
if !resources.contains(&resource) {
|
||||
panic!("{resource:?} was expected but not found in {resources:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn resource_paths_iter_slice_no_walk() {
|
||||
setup_test_dirs();
|
||||
|
||||
let dir = std::env::current_dir().unwrap().join("src-tauri");
|
||||
let _ = std::env::set_current_dir(dir);
|
||||
|
||||
let resources = ResourcePaths::new(
|
||||
&[
|
||||
"../src/script.js".into(),
|
||||
"../src/assets".into(),
|
||||
"../src/index.html".into(),
|
||||
"../src/sounds".into(),
|
||||
"*.toml".into(),
|
||||
"*.conf.json".into(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected = expected_resources(&[
|
||||
("../src/script.js", "_up_/src/script.js"),
|
||||
("../src/index.html", "_up_/src/index.html"),
|
||||
("Cargo.toml", "Cargo.toml"),
|
||||
("Tauri.toml", "Tauri.toml"),
|
||||
("tauri.conf.json", "tauri.conf.json"),
|
||||
]);
|
||||
|
||||
assert_eq!(resources.len(), expected.len());
|
||||
for resource in expected {
|
||||
if !resources.contains(&resource) {
|
||||
panic!("{resource:?} was expected but not found in {resources:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn resource_paths_iter_map_allow_walk() {
|
||||
setup_test_dirs();
|
||||
|
||||
let dir = std::env::current_dir().unwrap().join("src-tauri");
|
||||
let _ = std::env::set_current_dir(dir);
|
||||
|
||||
let resources = ResourcePaths::from_map(
|
||||
&std::collections::HashMap::from_iter([
|
||||
("../src/script.js".into(), "main.js".into()),
|
||||
("../src/assets".into(), "".into()),
|
||||
("../src/index.html".into(), "frontend/index.html".into()),
|
||||
("../src/sounds".into(), "voices".into()),
|
||||
("../src/textures/*".into(), "textures".into()),
|
||||
("../src/tiles/**/*".into(), "tiles".into()),
|
||||
("*.toml".into(), "".into()),
|
||||
("*.conf.json".into(), "json".into()),
|
||||
("../non-existent-file".into(), "asd".into()), // invalid case
|
||||
("../non/*".into(), "asd".into()), // invalid case
|
||||
]),
|
||||
true,
|
||||
)
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected = expected_resources(&[
|
||||
("../src/script.js", "main.js"),
|
||||
("../src/assets/javascript.svg", "javascript.svg"),
|
||||
("../src/assets/tauri.svg", "tauri.svg"),
|
||||
("../src/assets/rust.svg", "rust.svg"),
|
||||
("../src/assets/lang/en.json", "lang/en.json"),
|
||||
("../src/assets/lang/ar.json", "lang/ar.json"),
|
||||
("../src/index.html", "frontend/index.html"),
|
||||
("../src/sounds/lang/es.wav", "voices/lang/es.wav"),
|
||||
("../src/sounds/lang/fr.wav", "voices/lang/fr.wav"),
|
||||
("../src/textures/water.tex", "textures/water.tex"),
|
||||
("../src/textures/fire.tex", "textures/fire.tex"),
|
||||
("../src/tiles/grass.tile", "tiles/grass.tile"),
|
||||
("../src/tiles/stones.tile", "tiles/stones.tile"),
|
||||
("../src/tiles/sky/grey.tile", "tiles/grey.tile"),
|
||||
("../src/tiles/sky/yellow.tile", "tiles/yellow.tile"),
|
||||
("Cargo.toml", "Cargo.toml"),
|
||||
("Tauri.toml", "Tauri.toml"),
|
||||
("tauri.conf.json", "json/tauri.conf.json"),
|
||||
]);
|
||||
|
||||
assert_eq!(resources.len(), expected.len());
|
||||
for resource in expected {
|
||||
if !resources.contains(&resource) {
|
||||
panic!("{resource:?} was expected but not found in {resources:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn resource_paths_iter_map_no_walk() {
|
||||
setup_test_dirs();
|
||||
|
||||
let dir = std::env::current_dir().unwrap().join("src-tauri");
|
||||
let _ = std::env::set_current_dir(dir);
|
||||
|
||||
let resources = ResourcePaths::from_map(
|
||||
&std::collections::HashMap::from_iter([
|
||||
("../src/script.js".into(), "main.js".into()),
|
||||
("../src/assets".into(), "".into()),
|
||||
("../src/index.html".into(), "frontend/index.html".into()),
|
||||
("../src/sounds".into(), "voices".into()),
|
||||
("*.toml".into(), "".into()),
|
||||
("*.conf.json".into(), "json".into()),
|
||||
]),
|
||||
false,
|
||||
)
|
||||
.iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected = expected_resources(&[
|
||||
("../src/script.js", "main.js"),
|
||||
("../src/index.html", "frontend/index.html"),
|
||||
("Cargo.toml", "Cargo.toml"),
|
||||
("Tauri.toml", "Tauri.toml"),
|
||||
("tauri.conf.json", "json/tauri.conf.json"),
|
||||
]);
|
||||
|
||||
assert_eq!(resources.len(), expected.len());
|
||||
for resource in expected {
|
||||
if !resources.contains(&resource) {
|
||||
panic!("{resource:?} was expected but not found in {resources:?}");
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
io::{BufRead, BufReader},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
use tauri::Manager;
|
||||
use tauri::{Emitter, Manager};
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
|
Loading…
Reference in New Issue
Block a user