diff options
Diffstat (limited to 'src/launcher.rs')
| -rw-r--r-- | src/launcher.rs | 126 |
1 files changed, 119 insertions, 7 deletions
diff --git a/src/launcher.rs b/src/launcher.rs index 695406a..27fdf9c 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -1,29 +1,122 @@ -use std::collections::BTreeMap;
-
+use std::{collections::{BTreeMap, HashMap}, error::Error, fmt::Write, io::ErrorKind};
+use std::path::PathBuf;
use chrono::{DateTime, Utc};
-use serde::Deserialize;
+use serde::{Serialize, Deserialize};
+
+use log::{debug, warn};
use super::version::{*, manifest::*};
-#[derive(Deserialize, Debug)]
+mod constants;
+use constants::*;
+
+#[derive(Serialize, Deserialize, Debug)]
struct RemoteVersionIndexEntry {
last_update: DateTime<Utc>
}
-#[derive(Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug)]
struct RemoteVersionIndex {
versions: BTreeMap<String, RemoteVersionIndexEntry>
}
+impl Default for RemoteVersionIndex {
+ fn default() -> Self {
+ RemoteVersionIndex {
+ versions: BTreeMap::new()
+ }
+ }
+}
+
struct RemoteVersionList {
- manifest: VersionManifest,
+ versions: HashMap<String, VersionManifestVersion>,
index: RemoteVersionIndex
}
+impl RemoteVersionList {
+ async fn new(home: &str) -> Result<RemoteVersionList, Box<dyn Error>> {
+ let text = reqwest::get(URL_VERSION_MANIFEST).await?.error_for_status()?.text().await?;
+ let manifest: VersionManifest = serde_json::from_str(text.as_str())?;
+
+ let fname = format!("{home}/.remote.json");
+ let index: RemoteVersionIndex = match tokio::fs::read_to_string(fname).await {
+ Ok(s) => serde_json::from_str(s.as_str())?,
+ Err(e) => {
+ if e.kind() == ErrorKind::NotFound {
+ RemoteVersionIndex::default()
+ } else {
+ return Err(Box::new(e));
+ }
+ }
+ };
+
+ let mut versions = HashMap::new();
+ for v in manifest.versions {
+ versions.insert(v.id.clone(), v);
+ }
+
+ Ok(RemoteVersionList {
+ versions,
+ index
+ })
+ }
+}
+
struct LocalVersionList {
versions: BTreeMap<String, CompleteVersion>
}
+impl LocalVersionList {
+ async fn load_version(path: &PathBuf) -> Result<CompleteVersion, Box<dyn Error>> {
+ serde_json::from_str(tokio::fs::read_to_string(path).await?)?;
+ }
+
+ async fn load_versions(home: &str, skip: impl Fn(&str) -> bool) -> Result<LocalVersionList, Box<dyn Error>> {
+ let mut rd = tokio::fs::read_dir(home).await?;
+ let versions = BTreeMap::new();
+
+ while let Some(ent) = rd.next_entry().await? {
+ // when the code is fugly
+ let path = match ent.file_name().to_str() {
+ Some(s) => {
+ if skip(s) {
+ debug!("Skipping local version {s} because (I assume) it is remotely tracked.");
+ continue
+ }
+
+ /* FIXME: once https://github.com/rust-lang/rust/issues/127292 is closed,
+ * use add_extension to avoid extra heap allocations (they hurt my feelings) */
+ let mut path = ent.path();
+
+ // can't use set_extension since s might contain a . (like 1.8.9)
+ path.push(format!("{s}.json"));
+
+
+ },
+
+ /* We just ignore directories with names that contain invalid unicode. Unfortunately, the laucher
+ * will not be supporting such custom versions. Name your version something sensible please. */
+ None => {
+ warn!("Ignoring a local version because its id contains invalid unicode.");
+ continue
+ }
+ };
+
+ match Self::load_version(path).await {
+ Ok(v) => {
+ versions.insert(v.id.clone(), v);
+ },
+ Err(e) => {
+ // FIXME: just display the filename without to_string_lossy when https://github.com/rust-lang/rust/issues/120048 is closed
+ warn!("Ignoring local version {}: {e}", ent.file_name().to_string_lossy());
+ }
+ }
+ }
+
+ Ok(LocalVersionList { versions })
+ }
+}
+
struct VersionList {
offline: bool,
remote: Option<RemoteVersionList>,
@@ -31,5 +124,24 @@ struct VersionList { }
impl VersionList {
-
+ pub async fn online(home: &str) -> Result<VersionList, Box<dyn Error>> {
+ let remote = RemoteVersionList::new(home).await?;
+ let local = LocalVersionList::load_versions(home, |s| remote.versions.contains_key(s)).await?;
+
+ Ok(VersionList {
+ offline: false,
+ remote: Some(remote),
+ local
+ })
+ }
+
+ pub async fn offline(home: &str) -> Result<VersionList, Box<dyn Error>> {
+ let local = LocalVersionList::load_versions(home, |_| false).await?;
+
+ Ok(VersionList {
+ offline: true,
+ remote: None,
+ local
+ })
+ }
}
|
