1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::path::{Path, PathBuf};
use log::{debug, info, warn};
use tokio::{fs, io, io::ErrorKind};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
mod arch;
mod manifest;
use arch::JRE_ARCH;
use manifest::JavaRuntimesManifest;
use crate::launcher::jre::manifest::JavaRuntimeManifest;
use crate::util;
use crate::util::{EnsureFileError, FileVerifyError, 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_ref().map(|s| s.as_str()), 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 })?;
Ok(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().filter(|r| r.availability.progress == 100).next() 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
}
}
#[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)
}
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)
}
}
}
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
}
}
}
|