mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-10 04:22:13 +10:00
QoL Download Manager (#108)
* feat: retry specific download errors * fix: potential fix for cmd window on Windows * feat: add disk space check for download * fix: update game fix formatting * fix: clippy
This commit is contained in:
54
src-tauri/Cargo.lock
generated
54
src-tauri/Cargo.lock
generated
@ -1301,6 +1301,7 @@ dependencies = [
|
|||||||
"hex 0.4.3",
|
"hex 0.4.3",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-serde 2.1.1",
|
"http-serde 2.1.1",
|
||||||
|
"humansize",
|
||||||
"known-folders",
|
"known-folders",
|
||||||
"log",
|
"log",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
@ -1324,6 +1325,7 @@ dependencies = [
|
|||||||
"sha1",
|
"sha1",
|
||||||
"shared_child",
|
"shared_child",
|
||||||
"slice-deque",
|
"slice-deque",
|
||||||
|
"sysinfo",
|
||||||
"tar",
|
"tar",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
@ -2310,6 +2312,15 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humansize"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@ -2805,9 +2816,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.174"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@ -2819,6 +2830,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -3190,6 +3207,15 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -3386,6 +3412,16 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-io-kit"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-io-surface"
|
name = "objc2-io-surface"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -5317,6 +5353,20 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sysinfo"
|
||||||
|
version = "0.36.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"ntapi",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
"objc2-io-kit",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|||||||
@ -71,6 +71,8 @@ bitcode = "0.6.6"
|
|||||||
reqwest-websocket = "0.5.0"
|
reqwest-websocket = "0.5.0"
|
||||||
futures-lite = "2.6.0"
|
futures-lite = "2.6.0"
|
||||||
page_size = "0.6.0"
|
page_size = "0.6.0"
|
||||||
|
sysinfo = "0.36.1"
|
||||||
|
humansize = "2.1.3"
|
||||||
# tailscale = { path = "./tailscale" }
|
# tailscale = { path = "./tailscale" }
|
||||||
|
|
||||||
[dependencies.dynfmt]
|
[dependencies.dynfmt]
|
||||||
|
|||||||
@ -26,6 +26,7 @@ pub struct ProgressObject {
|
|||||||
rolling: RollingProgressWindow<250>,
|
rolling: RollingProgressWindow<250>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ProgressHandle {
|
pub struct ProgressHandle {
|
||||||
progress: Arc<AtomicUsize>,
|
progress: Arc<AtomicUsize>,
|
||||||
progress_object: Arc<ProgressObject>,
|
progress_object: Arc<ProgressObject>,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use serde_with::SerializeDisplay;
|
use serde_with::SerializeDisplay;
|
||||||
|
use humansize::{format_size, BINARY};
|
||||||
|
|
||||||
use super::remote_access_error::RemoteAccessError;
|
use super::remote_access_error::RemoteAccessError;
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ use super::remote_access_error::RemoteAccessError;
|
|||||||
#[derive(Debug, SerializeDisplay)]
|
#[derive(Debug, SerializeDisplay)]
|
||||||
pub enum ApplicationDownloadError {
|
pub enum ApplicationDownloadError {
|
||||||
Communication(RemoteAccessError),
|
Communication(RemoteAccessError),
|
||||||
|
DiskFull(u64, u64),
|
||||||
Checksum,
|
Checksum,
|
||||||
Lock,
|
Lock,
|
||||||
IoError(io::ErrorKind),
|
IoError(io::ErrorKind),
|
||||||
@ -20,11 +22,25 @@ pub enum ApplicationDownloadError {
|
|||||||
impl Display for ApplicationDownloadError {
|
impl Display for ApplicationDownloadError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
ApplicationDownloadError::DiskFull(required, available) => write!(
|
||||||
|
f,
|
||||||
|
"Game requires {}, {} remaining left on disk.",
|
||||||
|
format_size(*required, BINARY),
|
||||||
|
format_size(*available, BINARY),
|
||||||
|
),
|
||||||
ApplicationDownloadError::Communication(error) => write!(f, "{error}"),
|
ApplicationDownloadError::Communication(error) => write!(f, "{error}"),
|
||||||
ApplicationDownloadError::Lock => write!(f, "failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
|
ApplicationDownloadError::Lock => write!(
|
||||||
ApplicationDownloadError::Checksum => write!(f, "checksum failed to validate for download"),
|
f,
|
||||||
|
"failed to acquire lock. Something has gone very wrong internally. Please restart the application"
|
||||||
|
),
|
||||||
|
ApplicationDownloadError::Checksum => {
|
||||||
|
write!(f, "checksum failed to validate for download")
|
||||||
|
}
|
||||||
ApplicationDownloadError::IoError(error) => write!(f, "io error: {error}"),
|
ApplicationDownloadError::IoError(error) => write!(f, "io error: {error}"),
|
||||||
ApplicationDownloadError::DownloadError => write!(f, "download failed. See Download Manager status for specific error"),
|
ApplicationDownloadError::DownloadError => write!(
|
||||||
|
f,
|
||||||
|
"download failed. See Download Manager status for specific error"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{db::borrow_db_checked, models::data::GameDownloadStatus},
|
database::{db::borrow_db_checked, models::data::GameDownloadStatus},
|
||||||
download_manager::{
|
download_manager::
|
||||||
download_manager_frontend::DownloadManagerSignal, downloadable::Downloadable,
|
downloadable::Downloadable
|
||||||
},
|
,
|
||||||
error::download_manager_error::DownloadManagerError,
|
error::application_download_error::ApplicationDownloadError,
|
||||||
AppState,
|
AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,26 +20,28 @@ pub fn download_game(
|
|||||||
game_version: String,
|
game_version: String,
|
||||||
install_dir: usize,
|
install_dir: usize,
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
) -> Result<(), DownloadManagerError<DownloadManagerSignal>> {
|
) -> Result<(), ApplicationDownloadError> {
|
||||||
let sender = state.lock().unwrap().download_manager.get_sender();
|
let sender = state.lock().unwrap().download_manager.get_sender();
|
||||||
let game_download_agent = Arc::new(Box::new(GameDownloadAgent::new_from_index(
|
let game_download_agent = GameDownloadAgent::new_from_index(
|
||||||
game_id,
|
game_id,
|
||||||
game_version,
|
game_version,
|
||||||
install_dir,
|
install_dir,
|
||||||
sender,
|
sender,
|
||||||
)) as Box<dyn Downloadable + Send + Sync>);
|
)?;
|
||||||
Ok(state
|
let game_download_agent = Arc::new(Box::new(game_download_agent) as Box<dyn Downloadable + Send + Sync>);
|
||||||
.lock()
|
state
|
||||||
.unwrap()
|
.lock()
|
||||||
.download_manager
|
.unwrap()
|
||||||
.queue_download(game_download_agent)?)
|
.download_manager
|
||||||
|
.queue_download(game_download_agent).unwrap();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn resume_download(
|
pub fn resume_download(
|
||||||
game_id: String,
|
game_id: String,
|
||||||
state: tauri::State<'_, Mutex<AppState>>,
|
state: tauri::State<'_, Mutex<AppState>>,
|
||||||
) -> Result<(), DownloadManagerError<DownloadManagerSignal>> {
|
) -> Result<(), ApplicationDownloadError> {
|
||||||
let s = borrow_db_checked()
|
let s = borrow_db_checked()
|
||||||
.applications
|
.applications
|
||||||
.game_statuses
|
.game_statuses
|
||||||
@ -56,17 +58,21 @@ pub fn resume_download(
|
|||||||
install_dir,
|
install_dir,
|
||||||
} => (version_name, install_dir),
|
} => (version_name, install_dir),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sender = state.lock().unwrap().download_manager.get_sender();
|
let sender = state.lock().unwrap().download_manager.get_sender();
|
||||||
let parent_dir: PathBuf = install_dir.into();
|
let parent_dir: PathBuf = install_dir.into();
|
||||||
|
|
||||||
let game_download_agent = Arc::new(Box::new(GameDownloadAgent::new(
|
let game_download_agent = Arc::new(Box::new(GameDownloadAgent::new(
|
||||||
game_id,
|
game_id,
|
||||||
version_name.clone(),
|
version_name.clone(),
|
||||||
parent_dir.parent().unwrap().to_path_buf(),
|
parent_dir.parent().unwrap().to_path_buf(),
|
||||||
sender,
|
sender,
|
||||||
)) as Box<dyn Downloadable + Send + Sync>);
|
)?) as Box<dyn Downloadable + Send + Sync>);
|
||||||
Ok(state
|
|
||||||
.lock()
|
state
|
||||||
.unwrap()
|
.lock()
|
||||||
.download_manager
|
.unwrap()
|
||||||
.queue_download(game_download_agent)?)
|
.download_manager
|
||||||
|
.queue_download(game_download_agent).unwrap();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,13 +13,12 @@ use crate::error::application_download_error::ApplicationDownloadError;
|
|||||||
use crate::error::remote_access_error::RemoteAccessError;
|
use crate::error::remote_access_error::RemoteAccessError;
|
||||||
use crate::games::downloads::manifest::{DropDownloadContext, DropManifest};
|
use crate::games::downloads::manifest::{DropDownloadContext, DropManifest};
|
||||||
use crate::games::downloads::validate::validate_game_chunk;
|
use crate::games::downloads::validate::validate_game_chunk;
|
||||||
use crate::games::library::{
|
use crate::games::library::{on_game_complete, push_game_update, set_partially_installed};
|
||||||
on_game_complete, push_game_update, set_partially_installed,
|
|
||||||
};
|
|
||||||
use crate::games::state::GameStatusManager;
|
use crate::games::state::GameStatusManager;
|
||||||
|
use crate::process::utils::get_disk_available;
|
||||||
use crate::remote::requests::make_request;
|
use crate::remote::requests::make_request;
|
||||||
use crate::remote::utils::DROP_CLIENT_SYNC;
|
use crate::remote::utils::DROP_CLIENT_SYNC;
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info, warn};
|
||||||
use rayon::ThreadPoolBuilder;
|
use rayon::ThreadPoolBuilder;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{OpenOptions, create_dir_all};
|
use std::fs::{OpenOptions, create_dir_all};
|
||||||
@ -35,6 +34,8 @@ use rustix::fs::{FallocateFlags, fallocate};
|
|||||||
use super::download_logic::download_game_chunk;
|
use super::download_logic::download_game_chunk;
|
||||||
use super::drop_data::DropData;
|
use super::drop_data::DropData;
|
||||||
|
|
||||||
|
static RETRY_COUNT: usize = 3;
|
||||||
|
|
||||||
pub struct GameDownloadAgent {
|
pub struct GameDownloadAgent {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@ -54,7 +55,7 @@ impl GameDownloadAgent {
|
|||||||
version: String,
|
version: String,
|
||||||
target_download_dir: usize,
|
target_download_dir: usize,
|
||||||
sender: Sender<DownloadManagerSignal>,
|
sender: Sender<DownloadManagerSignal>,
|
||||||
) -> Self {
|
) -> Result<Self, ApplicationDownloadError> {
|
||||||
let db_lock = borrow_db_checked();
|
let db_lock = borrow_db_checked();
|
||||||
let base_dir = db_lock.applications.install_dirs[target_download_dir].clone();
|
let base_dir = db_lock.applications.install_dirs[target_download_dir].clone();
|
||||||
drop(db_lock);
|
drop(db_lock);
|
||||||
@ -66,7 +67,7 @@ impl GameDownloadAgent {
|
|||||||
version: String,
|
version: String,
|
||||||
base_dir: PathBuf,
|
base_dir: PathBuf,
|
||||||
sender: Sender<DownloadManagerSignal>,
|
sender: Sender<DownloadManagerSignal>,
|
||||||
) -> Self {
|
) -> Result<Self, ApplicationDownloadError> {
|
||||||
// Don't run by default
|
// Don't run by default
|
||||||
let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop);
|
let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop);
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ impl GameDownloadAgent {
|
|||||||
let stored_manifest =
|
let stored_manifest =
|
||||||
DropData::generate(id.clone(), version.clone(), data_base_dir_path.clone());
|
DropData::generate(id.clone(), version.clone(), data_base_dir_path.clone());
|
||||||
|
|
||||||
Self {
|
let result = Self {
|
||||||
id,
|
id,
|
||||||
version,
|
version,
|
||||||
control_flag,
|
control_flag,
|
||||||
@ -87,7 +88,31 @@ impl GameDownloadAgent {
|
|||||||
sender,
|
sender,
|
||||||
dropdata: stored_manifest,
|
dropdata: stored_manifest,
|
||||||
status: Mutex::new(DownloadStatus::Queued),
|
status: Mutex::new(DownloadStatus::Queued),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.ensure_manifest_exists()?;
|
||||||
|
|
||||||
|
let required_space = result
|
||||||
|
.manifest
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.map(|e| e.lengths.iter().sum::<usize>())
|
||||||
|
.sum::<usize>()
|
||||||
|
as u64;
|
||||||
|
|
||||||
|
let available_space = get_disk_available(data_base_dir_path)? as u64;
|
||||||
|
|
||||||
|
if required_space > available_space {
|
||||||
|
return Err(ApplicationDownloadError::DiskFull(
|
||||||
|
required_space,
|
||||||
|
available_space,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocking
|
// Blocking
|
||||||
@ -315,15 +340,40 @@ impl GameDownloadAgent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scope.spawn(move |_| {
|
scope.spawn(move |_| {
|
||||||
match download_game_chunk(context, &self.control_flag, progress_handle, request)
|
// 3 attempts
|
||||||
{
|
for i in 0..RETRY_COUNT {
|
||||||
Ok(true) => {
|
let loop_progress_handle = progress_handle.clone();
|
||||||
completed_indexes.push(context.checksum.clone());
|
match download_game_chunk(
|
||||||
}
|
context,
|
||||||
Ok(false) => {}
|
&self.control_flag,
|
||||||
Err(e) => {
|
loop_progress_handle,
|
||||||
error!("{e}");
|
request.try_clone().unwrap(),
|
||||||
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
) {
|
||||||
|
Ok(true) => {
|
||||||
|
completed_indexes.push(context.checksum.clone());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(false) => return,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("game download agent error: {e}");
|
||||||
|
|
||||||
|
let retry = match &e {
|
||||||
|
ApplicationDownloadError::Communication(
|
||||||
|
_remote_access_error,
|
||||||
|
) => true,
|
||||||
|
ApplicationDownloadError::Checksum => true,
|
||||||
|
ApplicationDownloadError::Lock => true,
|
||||||
|
ApplicationDownloadError::IoError(_error_kind) => false,
|
||||||
|
ApplicationDownloadError::DownloadError => false,
|
||||||
|
ApplicationDownloadError::DiskFull(_, _) => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if i == RETRY_COUNT - 1 || !retry {
|
||||||
|
warn!("retry logic failed, not re-attempting.");
|
||||||
|
sender.send(DownloadManagerSignal::Error(e)).unwrap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -452,8 +502,6 @@ impl GameDownloadAgent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.dropdata.write();
|
self.dropdata.write();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
pub mod commands;
|
pub mod commands;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub mod compat;
|
|
||||||
pub mod process_manager;
|
pub mod process_manager;
|
||||||
pub mod process_handlers;
|
pub mod process_handlers;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
pub mod utils;
|
||||||
@ -336,9 +336,9 @@ impl ProcessManager<'_> {
|
|||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
let mut command = Command::new("cmd");
|
let mut command = Command::new("start");
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
command.raw_arg(format!("/C \"{}\"", &launch_string));
|
command.raw_arg(format!("/min cmd /C \"{}\"", &launch_string));
|
||||||
|
|
||||||
info!("launching (in {install_dir}): {launch_string}",);
|
info!("launching (in {install_dir}): {launch_string}",);
|
||||||
|
|
||||||
|
|||||||
27
src-tauri/src/process/utils.rs
Normal file
27
src-tauri/src/process/utils.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use futures_lite::io;
|
||||||
|
use sysinfo::{Disk, DiskRefreshKind, Disks};
|
||||||
|
|
||||||
|
use crate::error::application_download_error::ApplicationDownloadError;
|
||||||
|
|
||||||
|
pub fn get_disk_available(mount_point: PathBuf) -> Result<u64, ApplicationDownloadError> {
|
||||||
|
let disks = Disks::new_with_refreshed_list_specifics(DiskRefreshKind::nothing().with_storage());
|
||||||
|
|
||||||
|
let mut disk_iter = disks.into_iter().collect::<Vec<&Disk>>();
|
||||||
|
disk_iter.sort_by(|a, b| {
|
||||||
|
b.mount_point()
|
||||||
|
.to_string_lossy()
|
||||||
|
.len()
|
||||||
|
.cmp(&a.mount_point().to_string_lossy().len())
|
||||||
|
});
|
||||||
|
|
||||||
|
for disk in disk_iter {
|
||||||
|
if mount_point.starts_with(disk.mount_point()) {
|
||||||
|
return Ok(disk.available_space());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ApplicationDownloadError::IoError(io::Error::other(
|
||||||
|
"could not find disk of path",
|
||||||
|
).kind()))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user