summaryrefslogtreecommitdiffstats
path: root/src/launcher/version.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-12 03:58:01 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-12 03:58:01 -0600
commitc0986823af246ccee2247b881974a2b7ce6ee491 (patch)
tree9dcb8a66692d5c0067450c60e1de72bd4fce92a5 /src/launcher/version.rs
parentuse a macro for map_err (diff)
add some logging and stuff
Diffstat (limited to 'src/launcher/version.rs')
-rw-r--r--src/launcher/version.rs70
1 files changed, 58 insertions, 12 deletions
diff --git a/src/launcher/version.rs b/src/launcher/version.rs
index f4cdd6c..411ac59 100644
--- a/src/launcher/version.rs
+++ b/src/launcher/version.rs
@@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use log::{debug, info, warn};
use sha1_smol::Digest;
+use tokio::{fs, io};
use crate::util;
use crate::version::{*, manifest::*};
@@ -18,7 +19,9 @@ struct RemoteVersionList {
impl RemoteVersionList {
async fn new() -> Result<RemoteVersionList, Box<dyn Error>> {
+ debug!("Looking up remote version manifest.");
let text = reqwest::get(URL_VERSION_MANIFEST).await?.error_for_status()?.text().await?;
+ debug!("Parsing version manifest.");
let manifest: VersionManifest = serde_json::from_str(text.as_str())?;
let mut versions = HashMap::new();
@@ -26,6 +29,7 @@ impl RemoteVersionList {
versions.insert(v.id.clone(), v);
}
+ debug!("Done loading remote versions!");
Ok(RemoteVersionList {
versions,
latest: manifest.latest
@@ -34,6 +38,7 @@ impl RemoteVersionList {
async fn download_version(&self, ver: &VersionManifestVersion, path: &Path) -> Result<CompleteVersion, Box<dyn Error>> {
// ensure parent directory exists
+ info!("Downloading version {}.", ver.id);
match tokio::fs::create_dir_all(path.parent().expect("version .json has no parent (impossible)")).await {
Err(e) => {
if e.kind() != ErrorKind::AlreadyExists {
@@ -47,6 +52,7 @@ impl RemoteVersionList {
// download it
let ver_text = reqwest::get(ver.url.as_str()).await?.error_for_status()?.text().await?;
+ 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())?;
@@ -54,9 +60,13 @@ impl RemoteVersionList {
// make sure it's well-formed
let cver: CompleteVersion = serde_json::from_str(ver.url.as_str())?;
+ debug!("Saving version {}...", ver.id);
+
// write it out
tokio::fs::write(path, ver_text).await?;
+ info!("Done downloading and verifying {}!", ver.id);
+
Ok(cver)
}
}
@@ -93,13 +103,21 @@ impl Error for LocalVersionError {}
impl LocalVersionList {
async fn load_version(path: &Path, sha1: Option<Digest>) -> Result<CompleteVersion, LocalVersionError> {
// grumble grumble I don't like reading in the whole file at once
+ info!("Loading local version at {}.", path.display());
let ver = tokio::fs::read_to_string(path).await.map_err(|e| LocalVersionError::Unknown(Box::new(e)))?;
if let Some(digest_exp) = sha1 {
+ debug!("Verifying local version {}.", path.display());
util::verify_sha1(digest_exp, ver.as_str())
- .map_err(|got| LocalVersionError::Sha1Mismatch { exp: digest_exp.to_owned(), got })?;
+ .map_err(|got| {
+ warn!("Local version sha1 mismatch: {} (exp: {}, got: {})", path.display(), digest_exp, got);
+ LocalVersionError::Sha1Mismatch { exp: digest_exp.to_owned(), got }
+ })?;
}
- let ver: CompleteVersion = serde_json::from_str(ver.as_str()).map_err(|e| LocalVersionError::Unknown(Box::new(e)))?;
+ let ver: CompleteVersion = serde_json::from_str(ver.as_str()).map_err(|e| {
+ warn!("Invalid version JSON {}: {}", path.display(), e);
+ LocalVersionError::Unknown(Box::new(e))
+ })?;
let fname_id = path.file_stem()
.expect("tried to load a local version with no path") // should be impossible
@@ -107,13 +125,16 @@ impl LocalVersionList {
.expect("tried to load a local version with invalid UTF-8 filename"); // we already checked if the filename is valid UTF-8 at this point
if fname_id == ver.id.as_str() {
+ info!("Loaded local version {}.", ver.id);
Ok(ver)
} else {
+ warn!("Local version {} has a version ID conflict (filename: {}, json: {})!", path.display(), fname_id, ver.id);
Err(LocalVersionError::VersionMismatch { fname: fname_id.to_owned(), json: ver.id })
}
}
async fn load_versions(home: &Path, skip: impl Fn(&str) -> bool) -> Result<LocalVersionList, Box<dyn Error>> {
+ info!("Loading local versions.");
let mut rd = tokio::fs::read_dir(home).await?;
let mut versions = BTreeMap::new();
@@ -156,6 +177,7 @@ impl LocalVersionList {
}
}
+ info!("Loaded {} local version(s).", versions.len());
Ok(LocalVersionList { versions })
}
}
@@ -200,23 +222,35 @@ pub enum VersionResolveError {
impl Display for VersionResolveError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
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::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}")
}
}
}
impl Error for VersionResolveError {}
+
+
impl VersionList {
+ async fn create_dir_for(home: &Path) -> Result<(), io::Error> {
+ 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)
+ }
+ }
+ }
+ }
+
pub async fn online(home: &Path) -> Result<VersionList, Box<dyn Error>> {
+ Self::create_dir_for(home).await?;
+
let remote = RemoteVersionList::new().await?;
let local = LocalVersionList::load_versions(home.as_ref(), |s| remote.versions.contains_key(s)).await?;
@@ -228,6 +262,8 @@ impl VersionList {
}
pub async fn offline(home: &Path) -> Result<VersionList, Box<dyn Error>> {
+ Self::create_dir_for(home).await?;
+
let local = LocalVersionList::load_versions(home, |_| false).await?;
Ok(VersionList {
@@ -259,10 +295,12 @@ impl VersionList {
let mut ver_path = self.home.join(id);
ver_path.push(format!("{id}.json"));
+ debug!("Loading local copy of remote version {}", ver.id);
+
match LocalVersionList::load_version(ver_path.as_path(), Some(ver.sha1)).await {
Ok(v) => return Ok(v),
Err(e) => {
- info!("redownloading {id}, since the local copy could not be loaded: {e}");
+ info!("Redownloading {id}, since the local copy could not be loaded: {e}");
}
}
@@ -277,11 +315,19 @@ impl VersionList {
return Ok(Cow::Borrowed(ver));
};
+ if *inherit == ver.id {
+ warn!("Version {} directly inherits from itself!", ver.id);
+ return Err(VersionResolveError::InheritanceLoop(ver.id.clone()));
+ }
+
+ debug!("Resolving version inheritance: {} (inherits from {})", ver.id, inherit);
+
let mut ver = ver.clone();
let mut inherit = inherit.clone();
loop {
if seen.insert(inherit.clone()) {
+ warn!("Version inheritance loop detected in {}: {} transitively inherits from itself.", ver.id, inherit);
return Err(VersionResolveError::InheritanceLoop(inherit));
}