summaryrefslogtreecommitdiffstats
path: root/src/launcher
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-02-01 00:20:54 -0600
committerLibravatar bigfoot547 <[email protected]>2025-02-01 00:20:54 -0600
commit6a1bb0980facadcc22cbae27f57782050e7924a1 (patch)
treedafdd715f7fc3f42323bd20666bd653a73a30b68 /src/launcher
parentdo clippy stuff and change line endings (diff)
random changes
Diffstat (limited to 'src/launcher')
-rw-r--r--src/launcher/version.rs105
1 files changed, 71 insertions, 34 deletions
diff --git a/src/launcher/version.rs b/src/launcher/version.rs
index 49525b0..0f55223 100644
--- a/src/launcher/version.rs
+++ b/src/launcher/version.rs
@@ -13,17 +13,51 @@ use crate::version::{*, manifest::*};
use super::constants::*;
+#[derive(Debug)]
+pub enum VersionError {
+ IO { what: String, error: io::Error },
+ Request { what: String, error: reqwest::Error },
+ MalformedObject { what: String, error: serde_json::Error },
+ VersionIntegrity { id: String, expect: Digest, got: Digest }
+}
+
+impl Display for VersionError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ VersionError::IO { what, error } => write!(f, "i/o error ({what}): {error}"),
+ VersionError::Request { what, error } => write!(f, "request error ({what}): {error}"),
+ VersionError::MalformedObject { what, error } => write!(f, "malformed {what}: {error}"),
+ VersionError::VersionIntegrity { id, expect, got } => write!(f, "version {id} integrity mismatch (expect {expect}, got {got})")
+ }
+ }
+}
+
+impl Error for VersionError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ VersionError::IO { error, .. } => Some(error),
+ VersionError::Request { error, .. } => Some(error),
+ VersionError::MalformedObject { error, .. } => Some(error),
+ _ => None
+ }
+ }
+}
+
struct RemoteVersionList {
versions: HashMap<String, VersionManifestVersion>,
latest: LatestVersions
}
impl RemoteVersionList {
- async fn new() -> Result<RemoteVersionList, Box<dyn Error>> {
+ async fn new() -> Result<RemoteVersionList, VersionError> {
debug!("Looking up remote version manifest.");
- let text = reqwest::get(URL_VERSION_MANIFEST).await?.error_for_status()?.text().await?;
+ let text = reqwest::get(URL_VERSION_MANIFEST).await
+ .and_then(|r| r.error_for_status())
+ .map_err(|e| VersionError::Request { what: "download version manifest".into(), error: e })?
+ .text().await.map_err(|e| VersionError::Request { what: "download version manifest (decode)".into(), error: e })?;
+
debug!("Parsing version manifest.");
- let manifest: VersionManifest = serde_json::from_str(text.as_str())?;
+ let manifest: VersionManifest = serde_json::from_str(text.as_str()).map_err(|e| VersionError::MalformedObject { what: "version manifest".into(), error: e })?;
let mut versions = HashMap::new();
for v in manifest.versions {
@@ -37,32 +71,37 @@ impl RemoteVersionList {
})
}
- async fn download_version(&self, ver: &VersionManifestVersion, path: &Path) -> Result<CompleteVersion, Box<dyn Error>> {
+ async fn download_version(&self, ver: &VersionManifestVersion, path: &Path) -> Result<CompleteVersion, VersionError> {
// ensure parent directory exists
info!("Downloading version {}.", ver.id);
- if let Err(e) = tokio::fs::create_dir_all(path.parent().expect("version .json has no parent (impossible)")).await {
- warn!("failed to create {} parent dirs: {e}", path.display());
- return Err(e.into());
- }
+ tokio::fs::create_dir_all(path.parent().expect("version .json has no parent (impossible)")).await
+ .inspect_err(|e| warn!("failed to create {} parent dirs: {e}", path.display()))
+ .map_err(|e| VersionError::IO { what: format!("creating version directory for {}", path.display()), error: e })?;
// download it
- let ver_text = reqwest::get(ver.url.as_str()).await?.error_for_status()?.text().await?;
-
+ let ver_text = reqwest::get(ver.url.as_str()).await
+ .and_then(|r| r.error_for_status())
+ .map_err(|e| VersionError::Request { what: format!("download version {} from {}", ver.id, ver.url), error: e })?
+ .text().await.map_err(|e| VersionError::Request { what: format!("download version {} from {} (receive)", ver.id, ver.url), error: e })?;
+
debug!("Validating downloaded {}...", ver.id);
// make sure it's valid
util::verify_sha1(ver.sha1, ver_text.as_str())
- .map_err::<Box<dyn Error>, _>(|e| format!("downloaded version {} has wrong hash! (expect {}, got {})", ver.id.as_str(), &ver.sha1, e).as_str().into())?;
+ .map_err(|e| VersionError::VersionIntegrity {
+ id: ver.id.clone(),
+ expect: ver.sha1,
+ got: e
+ })?;
// make sure it's well-formed
- let cver: CompleteVersion = serde_json::from_str(ver_text.as_str())?;
+ let cver: CompleteVersion = serde_json::from_str(ver_text.as_str()).map_err(|e| VersionError::MalformedObject { what: format!("complete version {}", ver.id), error: e })?;
debug!("Saving version {}...", ver.id);
// write it out
- tokio::fs::write(path, ver_text).await.map_err(|e| {
- warn!("Failed to save version {}: {}", ver.id, e);
- e
- })?;
+ tokio::fs::write(path, ver_text).await
+ .inspect_err(|e| warn!("Failed to save version {}: {}", ver.id, e))
+ .map_err(|e| VersionError::IO { what: format!("writing version file at {}", path.display()), error: e })?;
info!("Done downloading and verifying {}!", ver.id);
@@ -132,13 +171,13 @@ impl LocalVersionList {
}
}
- async fn load_versions(home: &Path, skip: impl Fn(&str) -> bool) -> Result<LocalVersionList, Box<dyn Error>> {
+ async fn load_versions(home: &Path, skip: impl Fn(&str) -> bool) -> Result<LocalVersionList, VersionError> {
info!("Loading local versions.");
- let mut rd = tokio::fs::read_dir(home).await?;
+ let mut rd = tokio::fs::read_dir(home).await.map_err(|e| VersionError::IO { what: format!("open local versions directory {}", home.display()), error: e })?;
let mut versions = BTreeMap::new();
- while let Some(ent) = rd.next_entry().await? {
- if !ent.file_type().await?.is_dir() { continue; }
+ while let Some(ent) = rd.next_entry().await.map_err(|e| VersionError::IO { what: format!("read local versions directory {}", home.display()), error: e })? {
+ if !ent.file_type().await.map_err(|e| VersionError::IO { what: format!("version entry metadata {}", ent.path().display()), error: e} )?.is_dir() { continue; }
// when the code is fugly
let path = match ent.file_name().to_str() {
@@ -215,7 +254,7 @@ impl<'a, T: Into<VersionResult<'a>>> From<Option<T>> for VersionResult<'a> {
pub enum VersionResolveError {
InheritanceLoop(String),
MissingVersion(String),
- Unknown(Box<dyn Error>)
+ VersionLoad(VersionError)
}
impl Display for VersionResolveError {
@@ -223,7 +262,7 @@ impl Display for VersionResolveError {
match self {
VersionResolveError::InheritanceLoop(s) => write!(f, "inheritance loop (saw {s} twice)"),
VersionResolveError::MissingVersion(s) => write!(f, "unknown version {s}"),
- VersionResolveError::Unknown(err) => write!(f, "unknown error: {err}")
+ VersionResolveError::VersionLoad(err) => write!(f, "version load error: {err}")
}
}
}
@@ -235,18 +274,16 @@ impl VersionList {
debug!("Creating versions directory.");
match fs::create_dir(home).await {
Ok(_) => Ok(()),
- Err(e) => match e.kind() {
- ErrorKind::AlreadyExists => Ok(()),
- _ => {
- debug!("failed to create version home: {}", e);
- Err(e)
- }
+ Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()),
+ Err(e) => {
+ debug!("failed to create version home: {}", e);
+ Err(e)
}
}
}
- pub async fn online(home: &Path) -> Result<VersionList, Box<dyn Error>> {
- Self::create_dir_for(home).await?;
+ pub async fn online(home: &Path) -> Result<VersionList, VersionError> {
+ Self::create_dir_for(home).await.map_err(|e| VersionError::IO { what: format!("create version directory {}", home.display()), error: e })?;
let remote = RemoteVersionList::new().await?;
let local = LocalVersionList::load_versions(home, |s| remote.versions.contains_key(s)).await?;
@@ -258,8 +295,8 @@ impl VersionList {
})
}
- pub async fn offline(home: &Path) -> Result<VersionList, Box<dyn Error>> {
- Self::create_dir_for(home).await?;
+ pub async fn offline(home: &Path) -> Result<VersionList, VersionError> {
+ Self::create_dir_for(home).await.map_err(|e| VersionError::IO { what: format!("create version directory {}", home.display()), error: e })?;
let local = LocalVersionList::load_versions(home, |_| false).await?;
@@ -294,7 +331,7 @@ impl VersionList {
remote.versions.get(id)
}
- pub async fn load_remote_version(&self, ver: &VersionManifestVersion) -> Result<CompleteVersion, Box<dyn Error>> {
+ pub async fn load_remote_version(&self, ver: &VersionManifestVersion) -> Result<CompleteVersion, VersionError> {
let remote = self.remote.as_ref().expect("load_remote_version called in offline mode!");
let id = ver.id.as_str();
@@ -340,7 +377,7 @@ impl VersionList {
let inherited_ver = match self.get_version_lazy(inherit.as_str()) {
VersionResult::Complete(v) => Cow::Borrowed(v),
VersionResult::Remote(v) =>
- Cow::Owned(self.load_remote_version(v).await.map_err(VersionResolveError::Unknown)?),
+ Cow::Owned(self.load_remote_version(v).await.map_err(VersionResolveError::VersionLoad)?),
VersionResult::None => {
warn!("Cannot resolve version {}, it inherits an unknown version {inherit}", ver.id);
return Err(VersionResolveError::MissingVersion(inherit));