Skip to content

Commit c3cbff3

Browse files
fix: resource path handles ./ path differently (#14662)
* fix: resource path handles `./` path differently * Setup CI for tauri utils * Wrong job name * Fix tests * Always run tests and don't run doc tests * Add change file * Re-use `test-core` workflow * Format * Avoid path clone by calculating target first * Test tauri-utils first with step instead of matrix * Use `matrix.platform.command` * Document `current_dest` and `current_pattern` * More docs * Merge remote-tracking branch 'upstream/dev' into refactor-resource-path-iter * Test with doc tests * Revert "Test with doc tests" This reverts commit 388bee9. * Merge branch 'dev' into refactor-resource-path-iter * Merge branch 'dev' into refactor-resource-path-iter
1 parent 33754ae commit c3cbff3

3 files changed

Lines changed: 130 additions & 99 deletions

File tree

‎.changes/resource-path-cur-dir.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-utils": patch:bug
3+
---
4+
5+
Fix resource path handles `./` path differently (e.g. `./some-folder` should be the same as `some-folder`)

‎.github/workflows/test-core.yml‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,23 @@ jobs:
9393
prefix-key: v3
9494
save-if: ${{ matrix.features.key == 'all' }}
9595

96-
- name: test
96+
- name: test tauri-utils
97+
if: ${{ !matrix.platform.cross }}
98+
# Using --lib --bins --tests to skip doc tests
99+
run: cargo ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --lib --bins --tests --manifest-path crates/tauri-utils/Cargo.toml
100+
101+
- name: test tauri-utils (using cross)
102+
if: ${{ matrix.platform.cross }}
103+
# Using --lib --bins --tests to skip doc tests
104+
run: |
105+
cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked
106+
cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --lib --bins --tests --manifest-path crates/tauri-utils/Cargo.toml
107+
108+
- name: test tauri
97109
if: ${{ !matrix.platform.cross }}
98110
run: cargo ${{ matrix.features.key == 'no-default' && 'check' || matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml
99111

100-
- name: test (using cross)
112+
- name: test tauri (using cross)
101113
if: ${{ matrix.platform.cross }}
102114
run: |
103115
cargo install cross --git https://github.com/cross-rs/cross --rev 51f46f296253d8122c927c5bb933e3c4f27cc317 --locked

‎crates/tauri-utils/src/resources.rs‎

Lines changed: 111 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,8 @@ impl<'a> ResourcePaths<'a> {
9797
iter: ResourcePathsIter {
9898
pattern_iter: PatternIter::Slice(patterns.iter()),
9999
allow_walk,
100-
current_pattern: None,
101-
walk_iter: None,
102-
glob_iter: None,
100+
current_dest: None,
101+
current_iter: None,
103102
},
104103
}
105104
}
@@ -110,9 +109,8 @@ impl<'a> ResourcePaths<'a> {
110109
iter: ResourcePathsIter {
111110
pattern_iter: PatternIter::Map(patterns.iter()),
112111
allow_walk,
113-
current_pattern: None,
114-
walk_iter: None,
115-
glob_iter: None,
112+
current_dest: None,
113+
current_iter: None,
116114
},
117115
}
118116
}
@@ -132,122 +130,139 @@ pub struct ResourcePathsIter<'a> {
132130
/// whether the resource paths allows directories or not.
133131
allow_walk: bool,
134132

135-
/// The (key, value) of map when `pattern_iter` is a [`PatternIter::Map`],
133+
/// The value of map when [`Self::pattern_iter`] is a [`PatternIter::Map`],
136134
/// used for determining [`Resource::target`]
137-
current_pattern: Option<(String, PathBuf)>,
138-
139-
walk_iter: Option<walkdir::IntoIter>,
140-
glob_iter: Option<glob::Paths>,
135+
current_dest: Option<PathBuf>,
136+
/// The iter for the current pattern. The cycle goes like this:
137+
/// [`ResourcePaths::next`] -> [`Self::next`] -> [`Self::pattern_iter::next`] -> [`Self::current_iter::next`]
138+
current_iter: Option<ResourcePathsInnerIter>,
141139
}
142140

143-
impl ResourcePathsIter<'_> {
144-
fn next_glob_iter(&mut self) -> Option<crate::Result<Resource>> {
145-
let entry = self.glob_iter.as_mut().unwrap().next()?;
141+
#[derive(Debug)]
142+
enum ResourcePathsInnerIter {
143+
Walk {
144+
iter: walkdir::IntoIter,
145+
/// The key of map when [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Map`],
146+
/// used for determining [`Resource::target`]
147+
current_pattern: Option<PathBuf>,
148+
},
149+
Glob {
150+
iter: glob::Paths,
151+
},
152+
}
146153

147-
let entry = match entry {
148-
Ok(entry) => entry,
149-
Err(err) => return Some(Err(err.into())),
150-
};
154+
impl Iterator for ResourcePathsInnerIter {
155+
type Item = crate::Result<PathBuf>;
151156

152-
self.next_current_path(normalize(&entry))
157+
fn next(&mut self) -> Option<crate::Result<PathBuf>> {
158+
match self {
159+
ResourcePathsInnerIter::Walk { iter, .. } => Some(
160+
iter
161+
.next()?
162+
.map(|entry| entry.into_path())
163+
.map_err(Into::into),
164+
),
165+
ResourcePathsInnerIter::Glob { iter } => Some(iter.next()?.map_err(Into::into)),
166+
}
153167
}
168+
}
154169

155-
fn next_walk_iter(&mut self) -> Option<crate::Result<Resource>> {
156-
let entry = self.walk_iter.as_mut().unwrap().next()?;
157-
158-
let entry = match entry {
159-
Ok(entry) => entry,
160-
Err(err) => return Some(Err(err.into())),
161-
};
162-
163-
self.next_current_path(normalize(entry.path()))
170+
impl ResourcePathsIter<'_> {
171+
fn next_current_iter(&mut self) -> Option<crate::Result<Resource>> {
172+
let current_iter = self.current_iter.as_mut().unwrap();
173+
let entry = current_iter.next()?;
174+
175+
Some(match entry {
176+
Ok(entry) => {
177+
// Skip directories
178+
if entry.is_dir() {
179+
self.next_current_iter()?
180+
} else {
181+
self.resource_from_path(normalize(&entry))
182+
}
183+
}
184+
Err(error) => Err(error),
185+
})
164186
}
165187

166-
fn resource_from_path(&mut self, path: &Path) -> crate::Result<Resource> {
188+
fn resource_from_path(&self, path: PathBuf) -> crate::Result<Resource> {
167189
if !path.exists() {
168-
return Err(crate::Error::ResourcePathNotFound(path.to_path_buf()));
190+
return Err(crate::Error::ResourcePathNotFound(path));
169191
}
170192

171193
Ok(Resource {
172-
path: path.to_path_buf(),
173-
target: if let Some((pattern, dest)) = &self.current_pattern {
174-
// if processing a directory, preserve directory structure under current_dest
175-
if self.walk_iter.is_some() {
176-
dest.join(path.strip_prefix(pattern).unwrap_or(path))
177-
} else if dest.components().count() == 0 {
178-
// if current_dest is empty while processing a file pattern or glob
179-
// we preserve the file name as it is
180-
PathBuf::from(path.file_name().unwrap())
181-
} else if self.glob_iter.is_some() {
182-
// if processing a glob and current_dest is not empty
183-
// we put all globbed paths under current_dest
184-
// preserving the file name as it is
185-
dest.join(path.file_name().unwrap())
186-
} else {
187-
dest.clone()
194+
target: if let Some(dest) = &self.current_dest {
195+
match &self.current_iter {
196+
Some(current_iter) => match current_iter {
197+
// if processing a directory, preserve directory structure under current_dest
198+
ResourcePathsInnerIter::Walk {
199+
current_pattern, ..
200+
} => {
201+
if let Some(pattern) = current_pattern {
202+
dest.join(path.strip_prefix(pattern).unwrap_or(&path))
203+
} else {
204+
dest.join(&path)
205+
}
206+
}
207+
// if processing a glob and current_dest is not empty
208+
// we put all globbed paths under current_dest
209+
// preserving the file name as it is
210+
ResourcePathsInnerIter::Glob { .. } => dest.join(path.file_name().unwrap()),
211+
},
212+
None => dest.clone(),
188213
}
189214
} else {
190-
// If `pattern_iter` is a [`PatternIter::Slice`]
191-
resource_relpath(path)
215+
// If [`ResourcePathsIter::pattern_iter`] is a [`PatternIter::Slice`]
216+
resource_relpath(&path)
192217
},
218+
path,
193219
})
194220
}
195221

196-
fn next_current_path(&mut self, path: PathBuf) -> Option<crate::Result<Resource>> {
197-
let is_dir = path.is_dir();
198-
199-
if is_dir {
200-
if self.glob_iter.is_some() {
201-
return self.next();
202-
}
203-
204-
if !self.allow_walk {
205-
return Some(Err(crate::Error::NotAllowedToWalkDir(path.to_path_buf())));
206-
}
207-
208-
if self.walk_iter.is_none() {
209-
self.walk_iter = Some(WalkDir::new(&path).into_iter());
210-
}
211-
212-
match self.next_walk_iter() {
213-
Some(resource) => Some(resource),
214-
None => {
215-
self.walk_iter = None;
216-
self.next()
217-
}
218-
}
219-
} else {
220-
Some(self.resource_from_path(&path))
221-
}
222-
}
223-
224222
fn next_pattern(&mut self) -> Option<crate::Result<Resource>> {
225-
self.current_pattern = None;
223+
self.current_dest = None;
226224

227225
let pattern = match &mut self.pattern_iter {
228226
PatternIter::Slice(iter) => iter.next()?,
229227
PatternIter::Map(iter) => {
230228
let (pattern, dest) = iter.next()?;
231-
self.current_pattern = Some((pattern.clone(), resource_relpath(Path::new(dest))));
229+
self.current_dest = Some(resource_relpath(Path::new(dest)));
232230
pattern
233231
}
234232
};
235233

236234
if pattern.contains('*') {
237-
self.glob_iter = match glob::glob(pattern) {
238-
Ok(glob) => Some(glob),
235+
self.current_iter = match glob::glob(pattern) {
236+
Ok(glob) => Some(ResourcePathsInnerIter::Glob { iter: glob }),
239237
Err(error) => return Some(Err(error.into())),
240238
};
241-
match self.next_glob_iter() {
239+
match self.next_current_iter() {
242240
Some(r) => return Some(r),
243241
None => {
244-
self.glob_iter = None;
242+
self.current_iter = None;
245243
return Some(Err(crate::Error::GlobPathNotFound(pattern.clone())));
246244
}
247245
}
246+
} else {
247+
let path = normalize(Path::new(pattern));
248+
if path.is_dir() {
249+
if !self.allow_walk {
250+
return Some(Err(crate::Error::NotAllowedToWalkDir(path)));
251+
}
252+
self.current_iter = Some(ResourcePathsInnerIter::Walk {
253+
iter: WalkDir::new(&path).into_iter(),
254+
current_pattern: if matches!(self.pattern_iter, PatternIter::Map(_)) {
255+
Some(path)
256+
} else {
257+
None
258+
},
259+
});
260+
} else {
261+
return Some(self.resource_from_path(path));
262+
}
248263
}
249264

250-
self.next_current_path(normalize(Path::new(pattern)))
265+
self.next_current_iter()
251266
}
252267
}
253268

@@ -263,17 +278,10 @@ impl Iterator for ResourcePathsIter<'_> {
263278
type Item = crate::Result<Resource>;
264279

265280
fn next(&mut self) -> Option<crate::Result<Resource>> {
266-
if self.walk_iter.is_some() {
267-
match self.next_walk_iter() {
281+
if self.current_iter.is_some() {
282+
match self.next_current_iter() {
268283
Some(r) => return Some(r),
269-
None => self.walk_iter = None,
270-
}
271-
}
272-
273-
if self.glob_iter.is_some() {
274-
match self.next_glob_iter() {
275-
Some(r) => return Some(r),
276-
None => self.glob_iter = None,
284+
None => self.current_iter = None,
277285
}
278286
}
279287

@@ -322,6 +330,7 @@ mod tests {
322330
"src-tauri/Cargo.toml",
323331
"src-tauri/Tauri.toml",
324332
"src-tauri/build.rs",
333+
"src-tauri/some-folder/some-file.txt",
325334
"src/assets/javascript.svg",
326335
"src/assets/tauri.svg",
327336
"src/assets/rust.svg",
@@ -403,11 +412,11 @@ mod tests {
403412
// From `../src/textures/**/*`
404413
(
405414
"../src/textures/ground/earth.tex",
406-
"_up_/src/textures/earth.tex",
415+
"_up_/src/textures/ground/earth.tex",
407416
),
408417
(
409418
"../src/textures/ground/sand.tex",
410-
"_up_/src/textures/sand.tex",
419+
"_up_/src/textures/ground/sand.tex",
411420
),
412421
("../src/textures/water.tex", "_up_/src/textures/water.tex"),
413422
("../src/textures/fire.tex", "_up_/src/textures/fire.tex"),
@@ -483,6 +492,7 @@ mod tests {
483492
("../src/tiles/**/*", "tiles"),
484493
("*.toml", ""),
485494
("*.conf.json", "json"),
495+
("./some-folder/", "some-target-folder/"),
486496
("../non-existent-file", "asd"), // invalid case
487497
("../non/*", "asd"), // invalid case
488498
]),
@@ -511,6 +521,10 @@ mod tests {
511521
("Cargo.toml", "Cargo.toml"),
512522
("Tauri.toml", "Tauri.toml"),
513523
("tauri.conf.json", "json/tauri.conf.json"),
524+
(
525+
"some-folder/some-file.txt",
526+
"some-target-folder/some-file.txt",
527+
),
514528
]);
515529

516530
assert_eq!(resources.len(), expected.len());
@@ -584,7 +598,7 @@ mod tests {
584598
.iter()
585599
.collect::<Vec<_>>();
586600

587-
assert_eq!(resources.len(), 4);
601+
assert_eq!(resources.len(), 5);
588602

589603
assert!(resources.iter().all(|r| r.is_err()));
590604

@@ -617,7 +631,7 @@ mod tests {
617631
.iter()
618632
.filter(|r| matches!(r, Err(crate::Error::GlobPathNotFound(_))))
619633
.count(),
620-
1
634+
2
621635
);
622636
}
623637
}

0 commit comments

Comments
 (0)