summaryrefslogtreecommitdiffstats
path: root/src/launcher/jre.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-02-01 23:06:37 -0600
committerLibravatar bigfoot547 <[email protected]>2025-02-01 23:06:37 -0600
commitc19a1077e85334a3e5ba885a60b03d76409a2b2e (patch)
tree5e726e8180770ac9c2f6c415a0437d6d2c29c226 /src/launcher/jre.rs
parentrandom changes (diff)
restructure project
Diffstat (limited to 'src/launcher/jre.rs')
-rw-r--r--src/launcher/jre.rs330
1 files changed, 0 insertions, 330 deletions
diff --git a/src/launcher/jre.rs b/src/launcher/jre.rs
deleted file mode 100644
index 31034b5..0000000
--- a/src/launcher/jre.rs
+++ /dev/null
@@ -1,330 +0,0 @@
-use std::error::Error;
-use std::fmt::{Debug, Display, Formatter};
-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};
-
-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, IntegrityError};
-use crate::version::DownloadInfo;
-use super::constants;
-
-pub struct JavaRuntimeRepository {
- online: bool,
- home: PathBuf,
- manifest: JavaRuntimesManifest
-}
-
-impl JavaRuntimeRepository {
- pub async fn new(home: impl AsRef<Path>, online: bool) -> Result<Self, JavaRuntimeError> {
- info!("Java runtime architecture is \"{}\".", JRE_ARCH);
-
- fs::create_dir_all(&home).await.map_err(|e| JavaRuntimeError::IO { what: "creating home directory", error: e })?;
-
- let manifest_path = home.as_ref().join("manifest.json");
- match util::ensure_file(manifest_path.as_path(), Some(constants::URL_JRE_MANIFEST), None, None, online, true).await {
- Ok(_) => (),
- Err(EnsureFileError::Offline) => {
- info!("Launcher is offline, cannot download runtime manifest.");
- },
- Err(e) => return Err(JavaRuntimeError::EnsureFile(e))
- };
-
- let manifest_file = fs::read_to_string(&manifest_path).await
- .map_err(|e| JavaRuntimeError::IO { what: "reading runtimes manifest", error: e })?;
-
- Ok(JavaRuntimeRepository {
- online,
- home: home.as_ref().to_path_buf(),
- manifest: serde_json::from_str(&manifest_file).map_err(|e| JavaRuntimeError::Deserialize { what: "runtimes manifest", error: e })?,
- })
- }
-
- fn get_component_dir(&self, component: &str) -> PathBuf {
- [self.home.as_path(), Path::new(JRE_ARCH), Path::new(component)].into_iter().collect()
- }
-
- async fn load_runtime_manifest(&self, component: &str, info: &DownloadInfo) -> Result<JavaRuntimeManifest, JavaRuntimeError> {
- let comp_dir = self.get_component_dir(component);
- let manifest_path = comp_dir.join("manifest.json");
-
- debug!("Ensuring manifest for runtime {JRE_ARCH}.{component}");
-
- fs::create_dir_all(comp_dir.as_path()).await
- .inspect_err(|e| warn!("Failed to create directory for JRE component {}: {}", component, e))
- .map_err(|e| JavaRuntimeError::IO { what: "creating component directory", error: e })?;
-
- util::ensure_file(&manifest_path, info.url.as_deref(), info.size, info.sha1, self.online, false).await
- .map_err(JavaRuntimeError::EnsureFile)?;
-
- let manifest_file = fs::read_to_string(&manifest_path).await
- .map_err(|e| JavaRuntimeError::IO { what: "reading runtimes manifest", error: e })?;
-
- serde_json::from_str(&manifest_file).map_err(|e| JavaRuntimeError::Deserialize { what: "runtime manifest", error: e })
- }
-
- // not very descriptive function name
- pub async fn choose_runtime(&self, component: &str) -> Result<JavaRuntimeManifest, JavaRuntimeError> {
- let Some(runtime_components) = self.manifest.get(JRE_ARCH) else {
- return Err(JavaRuntimeError::UnsupportedArch(JRE_ARCH));
- };
-
- let Some(runtime_component) = runtime_components.get(component) else {
- return Err(JavaRuntimeError::UnsupportedComponent { arch: JRE_ARCH, component: component.to_owned() });
- };
-
- let Some(runtime) = runtime_component.iter().find(|r| r.availability.progress == 100) else {
- if !runtime_components.is_empty() {
- warn!("Weird: the only java runtimes in {JRE_ARCH}.{component} has a progress of less than 100!");
- }
-
- return Err(JavaRuntimeError::UnsupportedComponent { arch: JRE_ARCH, component: component.to_owned() });
- };
-
- self.load_runtime_manifest(component, &runtime.manifest).await
- }
-
- 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().any(|c| !matches!(&c, Component::CurDir)) {
- // if this path is trivial (points at the root), ignore it
- continue;
- }
-
- let rel_path_str = if std::path::MAIN_SEPARATOR != '/' {
- rel_path.to_str().map(|s| s.replace(std::path::MAIN_SEPARATOR, "/"))
- } else {
- rel_path.to_str().map(String::from)
- };
-
- if !rel_path_str.as_ref().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());
-
- if entry.file_type().is_dir() {
- std::fs::remove_dir(entry.path())?;
- } else {
- std::fs::remove_file(entry.path())?;
- }
- }
- }
-
- Ok(())
- }
-
- async fn clean_up_runtime(path: &Path, manifest: Arc<JavaRuntimeManifest>) -> Result<(), io::Error> {
- let (tx, rx) = tokio::sync::oneshot::channel();
-
- let path = path.to_owned();
- let manifest = manifest.clone();
-
- tokio::task::spawn_blocking(move || {
- let res = Self::clean_up_runtime_sync(&path, manifest);
- let _ = tx.send(res);
- }).await.expect("clean_up_runtime_sync panicked");
-
- rx.await.expect("clean_up_runtime_sync hung up")
- }
-
- async fn ensure_jre_dirs(&self, path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
- stream::iter(manifest.files.iter().filter(|(_, f)| f.is_directory()))
- .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>();
-
- match fs::metadata(&ent_path).await {
- Ok(meta) => {
- if !meta.is_dir() {
- debug!("Deleting misplaced file at {}", ent_path.display());
- fs::remove_file(&ent_path).await.map_err(|e| JavaRuntimeError::IO {
- what: "deleting misplaced file",
- error: e
- })?;
- }
- },
- Err(e) if e.kind() == ErrorKind::NotFound => (),
- Err(e) => return Err(JavaRuntimeError::IO { what: "'stat'ing directory", error: e })
- }
-
- match fs::create_dir(&ent_path).await {
- Ok(_) => {
- debug!("Created directory at {}", ent_path.display());
- Ok(())
- },
- Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()),
- Err(e) => {
- warn!("Could not create directory {} for JRE!", ent_path.display());
- Err(JavaRuntimeError::IO { what: "creating directory", error: e })
- }
- }
- }).await
- }
-
- async fn ensure_jre_files(path: &Path, manifest: &JavaRuntimeManifest) -> Result<(), JavaRuntimeError> {
- 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
- })?;
-
- Ok(())
- }))
- .try_buffer_unordered(32)
- .try_fold((), |_, _| async { Ok(()) }).await
- }
-
- 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);
-
- fs::create_dir_all(&runtime_path).await
- .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.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?;
-
- Ok(runtime_path)
- }
-}
-
-#[derive(Debug)]
-pub enum JavaRuntimeError {
- EnsureFile(EnsureFileError),
- IO { what: &'static str, error: io::Error },
- Download { what: &'static str, error: reqwest::Error },
- Deserialize { what: &'static str, error: serde_json::Error },
- UnsupportedArch(&'static str),
- UnsupportedComponent { arch: &'static str, component: String },
- MalformedManifest(&'static str),
- Integrity(IntegrityError),
- MultiDownloadError
-}
-
-impl Display for JavaRuntimeError {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- match self {
- JavaRuntimeError::EnsureFile(e) => std::fmt::Display::fmt(e, f),
- JavaRuntimeError::IO { what, error } => write!(f, "i/o error ({}): {}", what, error),
- JavaRuntimeError::Download { what, error } => write!(f, "error downloading {}: {}", what, error),
- JavaRuntimeError::Deserialize { what, error } => write!(f, "error deserializing ({what}): {error}"),
- 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::MultiDownloadError => f.write_str("error in multi downloader (see logs for more details)")
- }
- }
-}
-
-impl Error for JavaRuntimeError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- JavaRuntimeError::EnsureFile(error) => Some(error),
- JavaRuntimeError::IO { error, .. } => Some(error),
- JavaRuntimeError::Download { error, .. } => Some(error),
- JavaRuntimeError::Deserialize { error, .. } => Some(error),
- JavaRuntimeError::Integrity(error) => Some(error),
- _ => None
- }
- }
-}