-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
fix: resource path handles ./ path differently
#14662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
51d04a5
fda6f9a
c412a69
3d695bd
b4e9e2d
b23ed48
a60f60a
58915c7
9d881f0
4bf4d56
63803af
9301f63
1dfe146
be7c1e3
388bee9
05d5124
628d6ba
47ff324
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "tauri-utils": patch:bug | ||
| --- | ||
|
|
||
| Fix resource path handles `./` path differently (e.g. `./some-folder` should be the same as `some-folder`) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,9 +97,8 @@ impl<'a> ResourcePaths<'a> { | |
| iter: ResourcePathsIter { | ||
| pattern_iter: PatternIter::Slice(patterns.iter()), | ||
| allow_walk, | ||
| current_pattern: None, | ||
| walk_iter: None, | ||
| glob_iter: None, | ||
| current_dest: None, | ||
| current_iter: None, | ||
| }, | ||
| } | ||
| } | ||
|
|
@@ -110,9 +109,8 @@ impl<'a> ResourcePaths<'a> { | |
| iter: ResourcePathsIter { | ||
| pattern_iter: PatternIter::Map(patterns.iter()), | ||
| allow_walk, | ||
| current_pattern: None, | ||
| walk_iter: None, | ||
| glob_iter: None, | ||
| current_dest: None, | ||
| current_iter: None, | ||
| }, | ||
| } | ||
| } | ||
|
|
@@ -132,122 +130,139 @@ pub struct ResourcePathsIter<'a> { | |
| /// whether the resource paths allows directories or not. | ||
| allow_walk: bool, | ||
|
|
||
| /// The (key, value) of map when `pattern_iter` is a [`PatternIter::Map`], | ||
| /// The value of map when [`Self::pattern_iter`] is a [`PatternIter::Map`], | ||
| /// used for determining [`Resource::target`] | ||
| current_pattern: Option<(String, PathBuf)>, | ||
|
|
||
| walk_iter: Option<walkdir::IntoIter>, | ||
| glob_iter: Option<glob::Paths>, | ||
| current_dest: Option<PathBuf>, | ||
| /// The iter for the current pattern. The cycle goes like this: | ||
| /// [`ResourcePaths::next`] -> [`Self::next`] -> [`Self::pattern_iter::next`] -> [`Self::current_iter::next`] | ||
| current_iter: Option<ResourcePathsInnerIter>, | ||
| } | ||
|
|
||
| impl ResourcePathsIter<'_> { | ||
| fn next_glob_iter(&mut self) -> Option<crate::Result<Resource>> { | ||
| let entry = self.glob_iter.as_mut().unwrap().next()?; | ||
| #[derive(Debug)] | ||
| enum ResourcePathsInnerIter { | ||
| Walk { | ||
| iter: walkdir::IntoIter, | ||
| /// The key of map when [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Map`], | ||
| /// used for determining [`Resource::target`] | ||
| current_pattern: Option<PathBuf>, | ||
| }, | ||
| Glob { | ||
| iter: glob::Paths, | ||
| }, | ||
| } | ||
|
|
||
| let entry = match entry { | ||
| Ok(entry) => entry, | ||
| Err(err) => return Some(Err(err.into())), | ||
| }; | ||
| impl Iterator for ResourcePathsInnerIter { | ||
| type Item = crate::Result<PathBuf>; | ||
|
|
||
| self.next_current_path(normalize(&entry)) | ||
| fn next(&mut self) -> Option<crate::Result<PathBuf>> { | ||
| match self { | ||
| ResourcePathsInnerIter::Walk { iter, .. } => Some( | ||
| iter | ||
| .next()? | ||
| .map(|entry| entry.into_path()) | ||
| .map_err(Into::into), | ||
| ), | ||
| ResourcePathsInnerIter::Glob { iter } => Some(iter.next()?.map_err(Into::into)), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| 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.next_current_path(normalize(entry.path())) | ||
| impl ResourcePathsIter<'_> { | ||
| fn next_current_iter(&mut self) -> Option<crate::Result<Resource>> { | ||
| let current_iter = self.current_iter.as_mut().unwrap(); | ||
| let entry = current_iter.next()?; | ||
|
|
||
| Some(match entry { | ||
| Ok(entry) => { | ||
| // Skip directories | ||
| if entry.is_dir() { | ||
| self.next_current_iter()? | ||
| } else { | ||
| self.resource_from_path(normalize(&entry)) | ||
| } | ||
| } | ||
| Err(error) => Err(error), | ||
| }) | ||
| } | ||
|
|
||
| fn resource_from_path(&mut self, path: &Path) -> crate::Result<Resource> { | ||
| fn resource_from_path(&self, path: PathBuf) -> crate::Result<Resource> { | ||
| if !path.exists() { | ||
| return Err(crate::Error::ResourcePathNotFound(path.to_path_buf())); | ||
| return Err(crate::Error::ResourcePathNotFound(path)); | ||
| } | ||
|
|
||
| Ok(Resource { | ||
| path: path.to_path_buf(), | ||
| target: if let Some((pattern, dest)) = &self.current_pattern { | ||
| // if processing a directory, preserve directory structure under current_dest | ||
| if self.walk_iter.is_some() { | ||
| dest.join(path.strip_prefix(pattern).unwrap_or(path)) | ||
| } else if dest.components().count() == 0 { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this possible? We have normalized this path and |
||
| // 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 | ||
| dest.join(path.file_name().unwrap()) | ||
| } else { | ||
| dest.clone() | ||
| target: if let Some(dest) = &self.current_dest { | ||
| match &self.current_iter { | ||
| Some(current_iter) => match current_iter { | ||
| // if processing a directory, preserve directory structure under current_dest | ||
| ResourcePathsInnerIter::Walk { | ||
| current_pattern, .. | ||
| } => { | ||
| if let Some(pattern) = current_pattern { | ||
| dest.join(path.strip_prefix(pattern).unwrap_or(&path)) | ||
| } else { | ||
| dest.join(&path) | ||
| } | ||
| } | ||
| // 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 | ||
| ResourcePathsInnerIter::Glob { .. } => dest.join(path.file_name().unwrap()), | ||
| }, | ||
| None => dest.clone(), | ||
| } | ||
| } else { | ||
| // If `pattern_iter` is a [`PatternIter::Slice`] | ||
| resource_relpath(path) | ||
| // If [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Slice`] | ||
| resource_relpath(&path) | ||
| }, | ||
| path, | ||
| }) | ||
| } | ||
|
|
||
| fn next_current_path(&mut self, path: PathBuf) -> Option<crate::Result<Resource>> { | ||
| 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; | ||
|
|
||
| let pattern = match &mut self.pattern_iter { | ||
| PatternIter::Slice(iter) => iter.next()?, | ||
| PatternIter::Map(iter) => { | ||
| let (pattern, dest) = iter.next()?; | ||
| self.current_pattern = Some((pattern.clone(), resource_relpath(Path::new(dest)))); | ||
| self.current_dest = Some(resource_relpath(Path::new(dest))); | ||
| pattern | ||
| } | ||
| }; | ||
|
|
||
| if pattern.contains('*') { | ||
| self.glob_iter = match glob::glob(pattern) { | ||
| Ok(glob) => Some(glob), | ||
| self.current_iter = match glob::glob(pattern) { | ||
| Ok(glob) => Some(ResourcePathsInnerIter::Glob { iter: glob }), | ||
| Err(error) => return Some(Err(error.into())), | ||
| }; | ||
| match self.next_glob_iter() { | ||
| match self.next_current_iter() { | ||
| Some(r) => return Some(r), | ||
| None => { | ||
| self.glob_iter = None; | ||
| self.current_iter = None; | ||
| return Some(Err(crate::Error::GlobPathNotFound(pattern.clone()))); | ||
| } | ||
| } | ||
| } else { | ||
| let path = normalize(Path::new(pattern)); | ||
| if path.is_dir() { | ||
| if !self.allow_walk { | ||
| return Some(Err(crate::Error::NotAllowedToWalkDir(path))); | ||
| } | ||
| self.current_iter = Some(ResourcePathsInnerIter::Walk { | ||
| iter: WalkDir::new(&path).into_iter(), | ||
| current_pattern: if matches!(self.pattern_iter, PatternIter::Map(_)) { | ||
| Some(path) | ||
| } else { | ||
| None | ||
| }, | ||
| }); | ||
| } else { | ||
| return Some(self.resource_from_path(path)); | ||
| } | ||
|
Comment on lines
+247
to
+262
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This replaced |
||
| } | ||
|
|
||
| self.next_current_path(normalize(Path::new(pattern))) | ||
| self.next_current_iter() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -263,17 +278,10 @@ impl Iterator for ResourcePathsIter<'_> { | |
| type Item = crate::Result<Resource>; | ||
|
|
||
| fn next(&mut self) -> Option<crate::Result<Resource>> { | ||
| if self.walk_iter.is_some() { | ||
| match self.next_walk_iter() { | ||
| if self.current_iter.is_some() { | ||
| match self.next_current_iter() { | ||
| Some(r) => return Some(r), | ||
| None => self.walk_iter = None, | ||
| } | ||
| } | ||
|
|
||
| if self.glob_iter.is_some() { | ||
| match self.next_glob_iter() { | ||
| Some(r) => return Some(r), | ||
| None => self.glob_iter = None, | ||
| None => self.current_iter = None, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -322,6 +330,7 @@ mod tests { | |
| "src-tauri/Cargo.toml", | ||
| "src-tauri/Tauri.toml", | ||
| "src-tauri/build.rs", | ||
| "src-tauri/some-folder/some-file.txt", | ||
| "src/assets/javascript.svg", | ||
| "src/assets/tauri.svg", | ||
| "src/assets/rust.svg", | ||
|
|
@@ -403,11 +412,11 @@ mod tests { | |
| // From `../src/textures/**/*` | ||
| ( | ||
| "../src/textures/ground/earth.tex", | ||
| "_up_/src/textures/earth.tex", | ||
| "_up_/src/textures/ground/earth.tex", | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We didn't run those tests before and they were failing from the very beginning... |
||
| ), | ||
| ( | ||
| "../src/textures/ground/sand.tex", | ||
| "_up_/src/textures/sand.tex", | ||
| "_up_/src/textures/ground/sand.tex", | ||
| ), | ||
| ("../src/textures/water.tex", "_up_/src/textures/water.tex"), | ||
| ("../src/textures/fire.tex", "_up_/src/textures/fire.tex"), | ||
|
|
@@ -483,6 +492,7 @@ mod tests { | |
| ("../src/tiles/**/*", "tiles"), | ||
| ("*.toml", ""), | ||
| ("*.conf.json", "json"), | ||
| ("./some-folder/", "some-target-folder/"), | ||
| ("../non-existent-file", "asd"), // invalid case | ||
| ("../non/*", "asd"), // invalid case | ||
| ]), | ||
|
|
@@ -511,6 +521,10 @@ mod tests { | |
| ("Cargo.toml", "Cargo.toml"), | ||
| ("Tauri.toml", "Tauri.toml"), | ||
| ("tauri.conf.json", "json/tauri.conf.json"), | ||
| ( | ||
| "some-folder/some-file.txt", | ||
| "some-target-folder/some-file.txt", | ||
| ), | ||
| ]); | ||
|
|
||
| assert_eq!(resources.len(), expected.len()); | ||
|
|
@@ -584,7 +598,7 @@ mod tests { | |
| .iter() | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| assert_eq!(resources.len(), 4); | ||
| assert_eq!(resources.len(), 5); | ||
|
|
||
| assert!(resources.iter().all(|r| r.is_err())); | ||
|
|
||
|
|
@@ -617,7 +631,7 @@ mod tests { | |
| .iter() | ||
| .filter(|r| matches!(r, Err(crate::Error::GlobPathNotFound(_)))) | ||
| .count(), | ||
| 1 | ||
| 2 | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grouped walk iter and glob iter into a enum since they're mutually exclusive