fix(lib): keep a missing download directory as the target, not its parent
resolve_download_path treated "not a directory" as "names a file" and fell back to the parent, so a directory that simply did not exist yet redirected the transfer one level up. The CLI and TUI create the folder before downloading and never hit it; a daemon does not, so its transfers landed in ~/Downloads while daemon.status - and the paths download and get report - named ~/Downloads/Soulseek. Green on any machine where the folder already exists, which is how it survived to CI. Only an existing file diverts to the parent now. PartFile::open already creates the directory it streams into.
This commit is contained in:
@@ -75,10 +75,15 @@ fn extract_filename_from_path(full_path: &str) -> &str {
|
|||||||
|
|
||||||
/// Where a download's bytes end up: its configured directory (falling back
|
/// Where a download's bytes end up: its configured directory (falling back
|
||||||
/// to the parent if it names a file) joined with the remote basename.
|
/// to the parent if it names a file) joined with the remote basename.
|
||||||
|
///
|
||||||
|
/// A directory that does not exist yet stays the target — `PartFile::open`
|
||||||
|
/// creates it. Treating it as "not a directory" and dodging to the parent
|
||||||
|
/// made a daemon whose folder was never created download into `~/Downloads`
|
||||||
|
/// while reporting `~/Downloads/Soulseek`.
|
||||||
fn resolve_download_path(download: &Download) -> Result<String, DownloadError> {
|
fn resolve_download_path(download: &Download) -> Result<String, DownloadError> {
|
||||||
let mut expanded_path = expand_tilde(&download.download_directory);
|
let mut expanded_path = expand_tilde(&download.download_directory);
|
||||||
|
|
||||||
if !expanded_path.is_dir() {
|
if expanded_path.is_file() {
|
||||||
expanded_path = expanded_path
|
expanded_path = expanded_path
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
@@ -527,6 +532,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_directory_that_does_not_exist_yet_is_created_not_dodged() {
|
||||||
|
let dir = scratch_dir("fresh").join("Soulseek");
|
||||||
|
|
||||||
|
let mut part = PartFile::open(&download_into(&dir, 4)).unwrap();
|
||||||
|
part.write(b"data").unwrap();
|
||||||
|
let final_path = part.finish().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
std::path::Path::new(&final_path).parent(),
|
||||||
|
Some(dir.as_path()),
|
||||||
|
"the bytes belong in the configured directory, not its parent"
|
||||||
|
);
|
||||||
|
assert_eq!(std::fs::read(&final_path).unwrap(), b"data");
|
||||||
|
let _ = std::fs::remove_dir_all(dir.parent().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_partial_file_sets_the_resume_offset() {
|
fn a_partial_file_sets_the_resume_offset() {
|
||||||
let dir = scratch_dir("resume");
|
let dir = scratch_dir("resume");
|
||||||
|
|||||||
Reference in New Issue
Block a user