summaryrefslogtreecommitdiffstats
path: root/src/launcher/jre.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-22 19:03:31 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-22 19:03:31 -0600
commitb2edba152d0256a8921f3a25d67a062163a54f59 (patch)
tree0bdc8b1ea4fedbb4d18c8347da76884c381b45b9 /src/launcher/jre.rs
parentmore jre download stuff (diff)
finish downloaded jres
Diffstat (limited to 'src/launcher/jre.rs')
-rw-r--r--src/launcher/jre.rs126
1 files changed, 106 insertions, 20 deletions
diff --git a/src/launcher/jre.rs b/src/launcher/jre.rs
index 4be4c7b..a09c8f3 100644
--- a/src/launcher/jre.rs
+++ b/src/launcher/jre.rs
@@ -1,20 +1,24 @@
use std::collections::HashSet;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
-use std::path::{Path, PathBuf};
+use std::path::{Component, Path, PathBuf};
use std::sync::Arc;
use futures::{stream, StreamExt, TryStreamExt};
use log::{debug, info, warn};
+use reqwest::Client;
use tokio::{fs, io, io::ErrorKind};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
mod arch;
mod manifest;
+mod download;
use arch::JRE_ARCH;
use manifest::JavaRuntimesManifest;
use manifest::JavaRuntimeManifest;
+use crate::launcher::download::MultiDownloader;
+use crate::launcher::jre::download::{LzmaDownloadError, LzmaDownloadJob};
use crate::launcher::jre::manifest::JavaRuntimeFile;
use crate::util;
use crate::util::{EnsureFileError, FileVerifyError, IntegrityError};
@@ -99,13 +103,17 @@ impl JavaRuntimeRepository {
fn clean_up_runtime_sync(path: &Path, manifest: Arc<JavaRuntimeManifest>) -> Result<(), io::Error> {
for entry in walkdir::WalkDir::new(path).contents_first(true) {
let entry = entry?;
+ let rel_path = entry.path().strip_prefix(path).expect("walkdir escaped root (???)");
+
+ if rel_path.components().filter(|c| !matches!(c, Component::CurDir)).next().is_none() {
+ // if this path is trivial (points at the root), ignore it
+ continue;
+ }
- if !entry.path().strip_prefix(path)
- .expect("walkdir escaped root (???)")
- .to_str().map_or(None, |s| manifest.files.get(s))
- .is_none_or(|f| (f.is_file() != entry.file_type().is_file())
- || (f.is_directory() != entry.file_type().is_dir())
- || (f.is_link() != entry.file_type().is_symlink())) {
+ if !rel_path.to_str().is_some_and(|s| manifest.files.get(s)
+ .is_some_and(|f| (f.is_file() == entry.file_type().is_file())
+ || (f.is_directory() == entry.file_type().is_dir())
+ || (f.is_link() == entry.file_type().is_symlink()))) {
// path is invalid utf-8, extraneous, or of the wrong type
debug!("File {} is extraneous or of wrong type ({:?}). Deleting it.", entry.path().display(), entry.file_type());
@@ -136,7 +144,8 @@ impl JavaRuntimeRepository {
async fn ensure_jre_dirs(&self, path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
stream::iter(manifest.files.iter().filter(|(_, f)| f.is_directory()))
- .map::<Result<_, JavaRuntimeError>, _>(|(name, _)| Ok(async move {
+ .map::<Result<&String, JavaRuntimeError>, _>(|(name, _)| Ok(name))
+ .try_for_each(|name| async move {
let ent_path = util::check_path(name).map_err(JavaRuntimeError::MalformedManifest)?;
let ent_path = [path, ent_path].into_iter().collect::<PathBuf>();
@@ -165,21 +174,92 @@ impl JavaRuntimeRepository {
return Err(JavaRuntimeError::IO { what: "creating directory", error: e });
}
}
- }))
- .try_buffer_unordered(32)
- .try_fold((), |_, _| async { Ok(()) }).await
+ }).await
}
async fn ensure_jre_files(path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
- stream::iter(manifest.files.iter().filter(|(_, f)| f.is_file()))
- .map(|(name, file)| Ok(async move {
+ let mut downloads = Vec::new();
+ for (name, file) in manifest.files.iter().filter(|(_, f)| f.is_file()) {
+ let file_path = util::check_path(name).map_err(JavaRuntimeError::MalformedManifest)?;
+ let file_path = [path, file_path].into_iter().collect::<PathBuf>();
+
+ downloads.push(LzmaDownloadJob::try_from((file, file_path)).map_err(|e| {
+ match e {
+ LzmaDownloadError::MissingURL => JavaRuntimeError::MalformedManifest("runtime manifest missing URL"),
+ LzmaDownloadError::NotAFile => unreachable!("we just made sure this was a file")
+ }
+ })?);
+ }
+
+ let dl = MultiDownloader::new(downloads.iter_mut());
+ let client = Client::new();
+
+ dl.perform(&client).await
+ .inspect_err(|e| warn!("jre file download failed: {e}"))
+ .try_fold((), |_, _| async { Ok(()) })
+ .await
+ .map_err(|_| JavaRuntimeError::MultiDownloadError)
+ }
+
+ async fn ensure_links(root_path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
+ stream::iter(manifest.files.iter().filter(|(_, f)| f.is_link()))
+ .map::<Result<_, JavaRuntimeError>, _>(|(name, file)| Ok(async move {
+ let JavaRuntimeFile::Link { target } = file else {
+ unreachable!();
+ };
+
+ let target_exp = PathBuf::from(target);
+
+ let path = util::check_path(name.as_str()).map_err(JavaRuntimeError::MalformedManifest)?;
+ let link_path = [root_path, path].into_iter().collect::<PathBuf>();
+
+ match fs::read_link(&link_path).await {
+ Ok(target_path) => {
+ if target_path == target_exp {
+ debug!("Symbolic link at {} matches! Nothing to be done.", link_path.display());
+ return Ok(())
+ }
- }));
+ debug!("Symbolic link at {} does not match (exp {}, got {}). Recreating it.", link_path.display(), target_exp.display(), target_path.display());
+ fs::remove_file(&link_path).await.map_err(|e| JavaRuntimeError::IO {
+ what: "deleting bad symlink",
+ error: e
+ })?;
+ }
+ Err(e) if e.kind() == ErrorKind::NotFound => (),
+ Err(e) => return Err(JavaRuntimeError::IO { what: "reading jre symlink", error: e })
+ }
+
+ debug!("Creating symbolic link at {} to {}", link_path.display(), target_exp.display());
+
+ let symlink;
+ #[cfg(unix)]
+ {
+ symlink = |targ, path| async { fs::symlink(targ, path).await };
+ }
+
+ #[cfg(windows)]
+ {
+ symlink = |targ, path| async { fs::symlink_file(targ, path).await };
+ }
+
+ #[cfg(not(any(unix, windows)))]
+ {
+ symlink = |_, _| async { Ok(()) };
+ }
+
+ symlink(target_exp, link_path).await.map_err(|e| JavaRuntimeError::IO {
+ what: "creating symlink",
+ error: e
+ })?;
- todo!()
+ Ok(())
+ }))
+ .try_buffer_unordered(32)
+ .try_fold((), |_, _| async { Ok(()) }).await
}
- async fn ensure_jre(&self, component: &str, manifest: JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
+ pub async fn ensure_jre(&self, component: &str, manifest: JavaRuntimeManifest) -> Result<PathBuf, JavaRuntimeError> {
let runtime_path = self.get_component_dir(component);
let runtime_path = runtime_path.join("runtime");
let manifest = Arc::new(manifest);
@@ -188,15 +268,19 @@ impl JavaRuntimeRepository {
.map_err(|e| JavaRuntimeError::IO { what: "creating runtime directory", error: e })?;
debug!("Cleaning up JRE directory for {component}");
- Self::clean_up_runtime(runtime_path.as_path(), manifest).await
+ Self::clean_up_runtime(runtime_path.as_path(), manifest.clone()).await
.map_err(|e| JavaRuntimeError::IO { what: "cleaning up runtime directory", error: e })?;
debug!("Building directory structure for {component}");
self.ensure_jre_dirs(&runtime_path, manifest.as_ref()).await?;
+ debug!("Downloading JRE files for {component}");
+ Self::ensure_jre_files(&runtime_path, manifest.as_ref()).await?;
+ debug!("Ensuring symbolic links for {component}");
+ Self::ensure_links(&runtime_path, manifest.as_ref()).await?;
- todo!()
+ Ok(runtime_path)
}
}
@@ -209,7 +293,8 @@ pub enum JavaRuntimeError {
UnsupportedArch(&'static str),
UnsupportedComponent { arch: &'static str, component: String },
MalformedManifest(&'static str),
- Integrity(IntegrityError)
+ Integrity(IntegrityError),
+ MultiDownloadError
}
impl Display for JavaRuntimeError {
@@ -222,7 +307,8 @@ impl Display for JavaRuntimeError {
JavaRuntimeError::UnsupportedArch(arch) => write!(f, r#"unsupported architecture "{arch}""#),
JavaRuntimeError::UnsupportedComponent { arch, component } => write!(f, r#"unsupported component "{component}" for architecture "{arch}""#),
JavaRuntimeError::MalformedManifest(what) => write!(f, "malformed runtime manifest: {what} (launcher bug?)"),
- JavaRuntimeError::Integrity(e) => std::fmt::Display::fmt(e, f)
+ JavaRuntimeError::Integrity(e) => std::fmt::Display::fmt(e, f),
+ JavaRuntimeError::MultiDownloadError => f.write_str("error in multi downloader (see logs for more details)")
}
}
}