From 64be4ddee9e2663ddd19e46f4159a3340d254b52 Mon Sep 17 00:00:00 2001 From: Danil Karpenko Date: Sun, 19 Nov 2023 14:09:46 +0100 Subject: [PATCH] chore: fmt --- crates/common/src/env/mod.rs | 1 + crates/common/src/jre/all.rs | 60 +-- crates/common/src/jre/manifest.rs | 34 +- crates/common/src/lib.rs | 4 +- crates/common/src/libutils/mod.rs | 42 +-- crates/common/src/manifest/mod.rs | 462 +++++++++++------------ crates/download/src/lib.rs | 372 +++++++++--------- crates/download/src/main.rs | 15 +- crates/ipc/src/lib.rs | 2 +- crates/ipc/src/lookup/mod.rs | 6 +- crates/launcher/src/from.rs | 146 ++++--- crates/launcher/src/launcher.rs | 292 +++++++------- crates/launcher/src/lib.rs | 2 +- crates/launcher/src/main.rs | 24 +- crates/lookup/src/main.rs | 4 +- crates/lookup/src/versions/mod.rs | 52 +-- crates/lookup/src/versions/overview.rs | 9 +- crates/profile/src/lib.rs | 42 +-- crates/providers/mojang/src/api.rs | 76 ++-- crates/providers/mojang/src/install.rs | 501 +++++++++++++------------ crates/providers/mojang/src/main.rs | 44 +-- src-tauri/src/main.rs | 34 +- 22 files changed, 1115 insertions(+), 1109 deletions(-) diff --git a/crates/common/src/env/mod.rs b/crates/common/src/env/mod.rs index e69de29..8b13789 100644 --- a/crates/common/src/env/mod.rs +++ b/crates/common/src/env/mod.rs @@ -0,0 +1 @@ + diff --git a/crates/common/src/jre/all.rs b/crates/common/src/jre/all.rs index f66d131..50da116 100644 --- a/crates/common/src/jre/all.rs +++ b/crates/common/src/jre/all.rs @@ -5,31 +5,31 @@ use { #[derive(Debug, Deserialize, PartialEq, Eq, Hash)] pub enum Target { - #[serde(rename = "gamecore")] - GameCore, - #[serde(rename = "linux")] - Linux, - #[serde(rename = "linux-i386")] - LinuxI386, - #[serde(rename = "mac-os")] - Macos, - #[serde(rename = "mac-os-arm64")] - MacosArm64, - #[serde(rename = "windows-arm64")] - WindowsArm64, - #[serde(rename = "windows-x64")] - WindowsX64, - #[serde(rename = "windows-x86")] - WindowsX86, + #[serde(rename = "gamecore")] + GameCore, + #[serde(rename = "linux")] + Linux, + #[serde(rename = "linux-i386")] + LinuxI386, + #[serde(rename = "mac-os")] + Macos, + #[serde(rename = "mac-os-arm64")] + MacosArm64, + #[serde(rename = "windows-arm64")] + WindowsArm64, + #[serde(rename = "windows-x64")] + WindowsX64, + #[serde(rename = "windows-x86")] + WindowsX86, } #[derive(Debug, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] pub enum ComponentType { - JavaRuntimeAlpha, - JavaRuntimeBeta, - JavaRuntimeGamma, - JavaRuntimeGammaSnapshot, + JavaRuntimeAlpha, + JavaRuntimeBeta, + JavaRuntimeGamma, + JavaRuntimeGammaSnapshot, JreLegacy, MinecraftJavaExe, } @@ -51,28 +51,28 @@ impl ComponentType { #[derive(Debug, Deserialize)] pub struct Manifest { - pub sha1: String, - pub size: u32, - pub url: String, + pub sha1: String, + pub size: u32, + pub url: String, } #[derive(Debug, Deserialize)] pub struct Availability { - pub group: u32, - pub progress: u32, + pub group: u32, + pub progress: u32, } #[derive(Debug, Deserialize)] pub struct Version { - pub name: String, - pub released: String, + pub name: String, + pub released: String, } #[derive(Debug, Deserialize)] pub struct Component { - pub availability: Availability, - pub manifest: Manifest, - pub version: Version, + pub availability: Availability, + pub manifest: Manifest, + pub version: Version, } pub type JavaRuntime = HashMap>>; diff --git a/crates/common/src/jre/manifest.rs b/crates/common/src/jre/manifest.rs index 9113a75..8e5782b 100644 --- a/crates/common/src/jre/manifest.rs +++ b/crates/common/src/jre/manifest.rs @@ -1,39 +1,39 @@ -use url::Url; use { - serde::Deserialize, - std::{ - collections::HashMap, - path::PathBuf, - }, + serde::Deserialize, + std::{ + collections::HashMap, + path::PathBuf, + }, + url::Url, }; #[derive(Debug, Deserialize)] pub struct File { - pub sha1: String, - pub size: u64, - pub url: Url, + pub sha1: String, + pub size: u64, + pub url: Url, } #[derive(Debug, Deserialize)] pub struct Downloads { - pub lzma: Option, - pub raw: File, + pub lzma: Option, + pub raw: File, } #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase", tag = "type")] pub enum Entry { Link, - Directory, - File { - executable: bool, - downloads: Downloads, - }, + Directory, + File { + executable: bool, + downloads: Downloads, + }, } pub type Files = HashMap; #[derive(Debug, Deserialize)] pub struct Manifest { - pub files: Files, + pub files: Files, } diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 3c246c2..aa6c0a2 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -1,4 +1,4 @@ -pub mod manifest; -pub mod jre; pub mod env; +pub mod jre; pub mod libutils; +pub mod manifest; diff --git a/crates/common/src/libutils/mod.rs b/crates/common/src/libutils/mod.rs index 536312e..6fa4ca6 100644 --- a/crates/common/src/libutils/mod.rs +++ b/crates/common/src/libutils/mod.rs @@ -1,30 +1,30 @@ use { - regex::Regex, - std::{ - iter, - path::PathBuf, - }, + regex::Regex, + std::{ + iter, + path::PathBuf, + }, }; pub fn libname_to_path(name: &str) -> Option { - let re = Regex::new(r"^([^:]+):([^:]+):(.+)").unwrap(); + let re = Regex::new(r"^([^:]+):([^:]+):(.+)").unwrap(); - let ca = match re.captures(name) { - Some(it) => it, - None => return None, - }; + let ca = match re.captures(name) { + Some(it) => it, + None => return None, + }; - let package = &ca[0]; - let artifact = &ca[1]; - let version = &ca[2]; + let package = &ca[0]; + let artifact = &ca[1]; + let version = &ca[2]; - let sub = format!("{}-{}.jar", artifact, version); + let sub = format!("{}-{}.jar", artifact, version); - Some(PathBuf::from_iter( - package - .split('.') - .chain(iter::once(artifact)) - .chain(iter::once(version)) - .chain(iter::once(sub.as_str())), - )) + Some(PathBuf::from_iter( + package + .split('.') + .chain(iter::once(artifact)) + .chain(iter::once(version)) + .chain(iter::once(sub.as_str())), + )) } diff --git a/crates/common/src/manifest/mod.rs b/crates/common/src/manifest/mod.rs index 4811bb2..dd314ec 100644 --- a/crates/common/src/manifest/mod.rs +++ b/crates/common/src/manifest/mod.rs @@ -1,152 +1,152 @@ use { - serde::Deserialize, - std::{ - collections::{ - HashMap, - HashSet, - }, - path::PathBuf, - }, - url::Url, + serde::Deserialize, + std::{ + collections::{ + HashMap, + HashSet, + }, + path::PathBuf, + }, + url::Url, }; #[derive(Debug, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] pub enum Os { - Linux, - Windows, - Osx, + Linux, + Windows, + Osx, } #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Arch { - X64, - X86, + X64, + X86, } #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum ConditionalArgument { - Single(String), - List(Vec), + Single(String), + List(Vec), } #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum Argument { - Constant(String), - Conditional { - rules: Vec, - value: ConditionalArgument, - }, + Constant(String), + Conditional { + rules: Vec, + value: ConditionalArgument, + }, } #[derive(Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum RuleAction { - Allow, - Disallow, + Allow, + Disallow, } #[derive(Debug, Deserialize)] pub struct Condition { - pub os: Option, - pub features: Option, + pub os: Option, + pub features: Option, } #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub struct RuleOsCondition { - pub name: Option, - pub version: Option, - pub arch: Option, + pub name: Option, + pub version: Option, + pub arch: Option, } pub type RuleFeaturesCondition = HashMap; #[derive(Debug, Deserialize)] pub struct Rule { - pub action: RuleAction, - #[serde(flatten)] - pub condition: Condition, + pub action: RuleAction, + #[serde(flatten)] + pub condition: Condition, } impl Rule { - fn check_os_condition(&self) -> bool { - let mut allow = true; - - if let Some(os_condition) = &self.condition.os { - if let Some(os_name) = &os_condition.name { - allow = match os_name { - #[cfg(target_os = "linux")] - Os::Linux => true, - #[cfg(target_os = "windows")] - Os::Windows => true, - #[cfg(target_os = "macos")] - Os::Osx => true, - #[allow(unreachable_patterns)] - _ => false, - }; - } - - if let Some(os_arch) = &os_condition.arch { - allow = match os_arch { - #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - Arch::X64 => true, - #[cfg(target_arch = "x86")] - Arch::X86 => true, - #[allow(unreachable_patterns)] - _ => false, - }; - } - } - - allow - } - - pub fn unwrap_featured(&self, features: &HashSet<&str>) -> bool { - let mut allow = self.check_os_condition(); - - if let Some(features_condition) = &self.condition.features { - if features_condition.is_empty() { - allow = false - } else { - allow = features_condition - .keys() - .all(|it| features.contains(it.as_str())); - } - } - - match self.action { - RuleAction::Allow => allow, - RuleAction::Disallow => !allow, - } - } - - pub fn unwrap(&self) -> bool { - let allow = self.check_os_condition(); - - match self.action { - RuleAction::Allow => allow, - RuleAction::Disallow => !allow, - } - } + fn check_os_condition(&self) -> bool { + let mut allow = true; + + if let Some(os_condition) = &self.condition.os { + if let Some(os_name) = &os_condition.name { + allow = match os_name { + #[cfg(target_os = "linux")] + Os::Linux => true, + #[cfg(target_os = "windows")] + Os::Windows => true, + #[cfg(target_os = "macos")] + Os::Osx => true, + #[allow(unreachable_patterns)] + _ => false, + }; + } + + if let Some(os_arch) = &os_condition.arch { + allow = match os_arch { + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + Arch::X64 => true, + #[cfg(target_arch = "x86")] + Arch::X86 => true, + #[allow(unreachable_patterns)] + _ => false, + }; + } + } + + allow + } + + pub fn unwrap_featured(&self, features: &HashSet<&str>) -> bool { + let mut allow = self.check_os_condition(); + + if let Some(features_condition) = &self.condition.features { + if features_condition.is_empty() { + allow = false + } else { + allow = features_condition + .keys() + .all(|it| features.contains(it.as_str())); + } + } + + match self.action { + RuleAction::Allow => allow, + RuleAction::Disallow => !allow, + } + } + + pub fn unwrap(&self) -> bool { + let allow = self.check_os_condition(); + + match self.action { + RuleAction::Allow => allow, + RuleAction::Disallow => !allow, + } + } } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Artifact { - pub path: PathBuf, - pub sha1: String, - pub size: u64, - pub url: Url, + pub path: PathBuf, + pub sha1: String, + pub size: u64, + pub url: Url, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct LibraryDownloadEntry { - pub artifact: Artifact, + pub artifact: Artifact, } #[derive(Debug, Deserialize)] @@ -159,217 +159,217 @@ pub struct NativeDownloadEntry { #[derive(Debug, Deserialize)] #[serde(untagged, rename_all = "camelCase")] pub enum Library { - Custom { - name: String, - url: Url, - }, - Native { - downloads: NativeDownloadEntry, - name: String, - rules: Vec, - natives: HashMap, - }, - Seminative { - downloads: LibraryDownloadEntry, - name: String, - rules: Vec, - }, - Default { - downloads: LibraryDownloadEntry, - name: String, - }, + Custom { + name: String, + url: Url, + }, + Native { + downloads: NativeDownloadEntry, + name: String, + rules: Vec, + natives: HashMap, + }, + Seminative { + downloads: LibraryDownloadEntry, + name: String, + rules: Vec, + }, + Default { + downloads: LibraryDownloadEntry, + name: String, + }, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AssetIndexResource { - pub id: String, - pub sha1: String, - pub size: i32, - pub total_size: i32, - pub url: Url, + pub id: String, + pub sha1: String, + pub size: i32, + pub total_size: i32, + pub url: Url, } #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub struct PackageDownloads { - pub client: Resource, - pub client_mappings: Resource, - pub server: Resource, - pub server_mappings: Resource, + pub client: Resource, + pub client_mappings: Resource, + pub server: Resource, + pub server_mappings: Resource, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Resource { - pub sha1: String, - pub size: u64, - pub url: Url, + pub sha1: String, + pub size: u64, + pub url: Url, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct JavaVersion { - pub component: String, - pub major_version: i32, + pub component: String, + pub major_version: i32, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ClientLogging { - pub argument: String, - #[serde(rename = "type")] - pub log_type: String, + pub argument: String, + #[serde(rename = "type")] + pub log_type: String, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Logging { - pub client: ClientLogging, + pub client: ClientLogging, } #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] pub enum VersionType { - Release, - Snapshot, - OldBeta, - OldAlpha, + Release, + Snapshot, + OldBeta, + OldAlpha, } #[derive(Debug, Deserialize)] pub struct Args { - pub game: Vec, - pub jvm: Vec, + pub game: Vec, + pub jvm: Vec, } #[derive(Debug, Deserialize)] pub struct ModernArgs { - pub arguments: Args, + pub arguments: Args, } impl From for ModernArgs { - fn from(value: String) -> Self { - Self { - arguments: Args { - game: value - .split_whitespace() - .map(|it| Argument::Constant(it.to_owned())) - .collect(), - jvm: Vec::new(), - }, - } - } + fn from(value: String) -> Self { + Self { + arguments: Args { + game: value + .split_whitespace() + .map(|it| Argument::Constant(it.to_owned())) + .collect(), + jvm: Vec::new(), + }, + } + } } #[derive(Debug, Deserialize)] pub struct LegacyArgs { - #[serde(rename = "minecraft_arguments")] - pub arguments: String, + #[serde(rename = "minecraft_arguments")] + pub arguments: String, } impl From for ModernArgs { - fn from(value: LegacyArgs) -> Self { - value.arguments.into() - } + fn from(value: LegacyArgs) -> Self { + value.arguments.into() + } } #[derive(Debug, Deserialize)] #[serde(untagged, rename_all = "camelCase")] pub enum ArgsContainer { - Modern(ModernArgs), - Legacy(LegacyArgs), + Modern(ModernArgs), + Legacy(LegacyArgs), } impl From for ModernArgs { fn from(value: ArgsContainer) -> Self { match value { ArgsContainer::Modern(it) => it, - ArgsContainer::Legacy(it) => it.into() + ArgsContainer::Legacy(it) => it.into(), } } } impl ArgsContainer { - fn merge(self, with: ArgsContainer) -> Self { - use ArgsContainer::*; - - match with { - Modern(ModernArgs { arguments }) => { - let Args { - jvm: jvm_ext, - game: game_ext, - } = arguments; - let mut modern = self.into_modern(); - let Args { - ref mut jvm, - ref mut game, - } = modern.arguments; - - jvm.extend(jvm_ext); - game.extend(game_ext); - - Modern(modern) - } - Legacy(args) => self.merge(Modern(args.into())), - } - } - - fn into_modern(self) -> ModernArgs { - use ArgsContainer::*; - - match self { - Modern(it) => it, - Legacy(it) => it.into(), - } - } + fn merge(self, with: ArgsContainer) -> Self { + use ArgsContainer::*; + + match with { + Modern(ModernArgs { arguments }) => { + let Args { + jvm: jvm_ext, + game: game_ext, + } = arguments; + let mut modern = self.into_modern(); + let Args { + ref mut jvm, + ref mut game, + } = modern.arguments; + + jvm.extend(jvm_ext); + game.extend(game_ext); + + Modern(modern) + } + Legacy(args) => self.merge(Modern(args.into())), + } + } + + fn into_modern(self) -> ModernArgs { + use ArgsContainer::*; + + match self { + Modern(it) => it, + Legacy(it) => it.into(), + } + } } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RootManifest { - #[serde(flatten)] - pub arguments: ArgsContainer, - pub asset_index: AssetIndexResource, - pub assets: String, - pub downloads: PackageDownloads, - pub id: String, - pub java_version: JavaVersion, - pub libraries: Vec, - pub logging: Logging, - pub main_class: String, - pub release_time: String, - pub time: String, - #[serde(rename = "type")] - pub version_type: VersionType, + #[serde(flatten)] + pub arguments: ArgsContainer, + pub asset_index: AssetIndexResource, + pub assets: String, + pub downloads: PackageDownloads, + pub id: String, + pub java_version: JavaVersion, + pub libraries: Vec, + pub logging: Logging, + pub main_class: String, + pub release_time: String, + pub time: String, + #[serde(rename = "type")] + pub version_type: VersionType, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InheritedManifest { - pub inherits_from: Option, - #[serde(flatten)] - pub arguments: ArgsContainer, - pub libraries: Vec, - pub main_class: String, - pub release_time: String, - pub time: String, - #[serde(rename = "type")] - pub version_type: VersionType, - pub id: String, + pub inherits_from: Option, + #[serde(flatten)] + pub arguments: ArgsContainer, + pub libraries: Vec, + pub main_class: String, + pub release_time: String, + pub time: String, + #[serde(rename = "type")] + pub version_type: VersionType, + pub id: String, } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(untagged)] pub enum Manifest { - Root(Box), - Inherited(Box), + Root(Box), + Inherited(Box), } impl InheritedManifest { - pub fn into_root(self, mut root: RootManifest) -> RootManifest { - macro_rules! copy { + pub fn into_root(self, mut root: RootManifest) -> RootManifest { + macro_rules! copy { ($($field:ident),+) => { $( root.$field = self.$field; @@ -377,27 +377,27 @@ impl InheritedManifest { }; } - copy! { - id, - time, - release_time, - main_class - }; + copy! { + id, + time, + release_time, + main_class + }; - root.arguments = root.arguments.merge(self.arguments); - root.libraries.extend(self.libraries); + root.arguments = root.arguments.merge(self.arguments); + root.libraries.extend(self.libraries); - root - } + root + } } #[derive(Debug, Deserialize)] pub struct AssetObject { - pub hash: String, - pub size: u64, + pub hash: String, + pub size: u64, } #[derive(Debug, Deserialize)] pub struct AssetIndex { - pub objects: HashMap, + pub objects: HashMap, } diff --git a/crates/download/src/lib.rs b/crates/download/src/lib.rs index 1ef4e6c..b851351 100644 --- a/crates/download/src/lib.rs +++ b/crates/download/src/lib.rs @@ -3,74 +3,76 @@ use { stream::iter as fut_iter, StreamExt, TryStreamExt, - }, + }, reqwest::Client, serde::Serialize, sha1::{ Digest, Sha1, - }, + }, std::{ io::ErrorKind, path::PathBuf, sync::{ - Arc, atomic::{ AtomicUsize, Ordering, - }, - }, + }, + Arc, + }, }, thiserror::Error, tokio::{ - fs::{self}, - fs::File, + fs::{ + File, + {self,}, + }, io::{ AsyncReadExt, AsyncWriteExt, - }, + }, sync::mpsc::Sender, - }, + }, tokio_util::sync::CancellationToken, url::Url, }; #[derive(Debug, Serialize, Clone)] pub struct Item { - pub url: Url, - pub path: PathBuf, - pub known_size: Option, - pub known_sha: Option, + pub url: Url, + pub path: PathBuf, + pub known_size: Option, + pub known_sha: Option, } #[derive(Debug, Error, Serialize, Clone)] pub enum DownloadError { - #[error("known: Unknown kind value")] - UnknownKind, + #[error("known: Unknown kind value")] + UnknownKind, - #[error("io: {0}")] - Io(String), + #[error("io: {0}")] + Io(String), - #[error("reqwest: {0}")] - Reqwest(String), + #[error("reqwest: {0}")] + Reqwest(String), - #[error("sync: {0}")] - SendError(String), + #[error("sync: {0}")] + SendError(String), - #[error("join: {0}")] - JoinError(String), + #[error("join: {0}")] + JoinError(String), - #[error("unexpected: {0}")] - Unexpected(String), + #[error("unexpected: {0}")] + Unexpected(String), - #[error("sha: {0}")] - InvalidSha(String), + #[error("sha: {0}")] + InvalidSha(String), - #[error("shutdown")] - Shutdown, + #[error("shutdown")] + Shutdown, - #[error("cancelled")] - Cancelled, + #[error("cancelled")] + Cancelled, } macro_rules! from_err { @@ -86,174 +88,174 @@ macro_rules! from_err { } from_err! { - std::io::Error => Io, - reqwest::Error => Reqwest, - tokio::sync::mpsc::error::SendError => SendError, - tokio::task::JoinError => JoinError + std::io::Error => Io, + reqwest::Error => Reqwest, + tokio::sync::mpsc::error::SendError => SendError, + tokio::task::JoinError => JoinError } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub enum DownloadEvent { - Start { - item: Item, - }, - Chunk { - path: String, - size: usize, - total: Option, - progress: usize, - }, - Error { - item: Item, - error: DownloadError, - }, - Finish { - item: Item, - total: usize, - progress: usize, - }, + Start { + item: Item, + }, + Chunk { + path: String, + size: usize, + total: Option, + progress: usize, + }, + Error { + item: Item, + error: DownloadError, + }, + Finish { + item: Item, + total: usize, + progress: usize, + }, } pub async fn download( - client: &Client, - item: Item, - sender: &Sender, - token: &CancellationToken, + client: &Client, + item: Item, + sender: &Sender, + token: &CancellationToken, ) -> Result<(), DownloadError> { - if token.is_cancelled() { - return Err(DownloadError::Cancelled); - } - - sender - .send(DownloadEvent::Start { item: item.clone() }) - .await?; - - if let Some(sha) = item.known_sha { - match File::open(&item.path).await { - Ok(mut file) => { - let mut hasher = Sha1::new(); - - let mut buffer: Vec = vec![0; 2097152]; - - loop { - let bytes_read = file.read(&mut buffer).await?; - - if bytes_read == 0 { - break; - } - hasher.update(&buffer[..bytes_read]); - } - - let hex = &hasher.finalize()[..]; - - let bytes = (0..sha.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&sha[i..i + 2], 16)) - .collect::, _>>() - .map_err(|err| DownloadError::InvalidSha(err.to_string()))?; - - let matched = hex.iter().zip(&bytes).all(|(a, b)| a == b); - - if matched { - return Ok(()); - } - } - Err(err) => match err.kind() { - ErrorKind::NotFound => {} - _ => Err(err)?, - }, - }; - } - - let response = client.get(item.url.to_owned()).send().await?; - - let content_length = response.content_length().or(item.known_size); - let mut stream = response.bytes_stream().map_err(DownloadError::from); + if token.is_cancelled() { + return Err(DownloadError::Cancelled); + } + + sender + .send(DownloadEvent::Start { item: item.clone() }) + .await?; + + if let Some(sha) = item.known_sha { + match File::open(&item.path).await { + Ok(mut file) => { + let mut hasher = Sha1::new(); + + let mut buffer: Vec = vec![0; 2097152]; + + loop { + let bytes_read = file.read(&mut buffer).await?; + + if bytes_read == 0 { + break; + } + hasher.update(&buffer[..bytes_read]); + } + + let hex = &hasher.finalize()[..]; + + let bytes = (0..sha.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&sha[i..i + 2], 16)) + .collect::, _>>() + .map_err(|err| DownloadError::InvalidSha(err.to_string()))?; + + let matched = hex.iter().zip(&bytes).all(|(a, b)| a == b); + + if matched { + return Ok(()); + } + } + Err(err) => match err.kind() { + ErrorKind::NotFound => {} + _ => Err(err)?, + }, + }; + } + + let response = client.get(item.url.to_owned()).send().await?; + + let content_length = response.content_length().or(item.known_size); + let mut stream = response.bytes_stream().map_err(DownloadError::from); fs::create_dir_all(&item.path.parent().unwrap()).await?; let mut target_file = File::create(&item.path).await?; - let mut progress: usize = 0; - - let path_key = item - .path - .to_str() - .ok_or(DownloadError::Unexpected( - "Failed to convert path to string".into(), - ))? - .to_owned(); - - while let Some(bytes) = stream.next().await { - if token.is_cancelled() { - return Err(DownloadError::Cancelled); - } - - let bytes = bytes?; - target_file.write_all(&bytes).await?; - - progress += bytes.len(); - - sender - .send(DownloadEvent::Chunk { - path: path_key.clone(), - progress, - size: bytes.len(), - total: content_length, - }) - .await?; - } - - Ok(()) + let mut progress: usize = 0; + + let path_key = item + .path + .to_str() + .ok_or(DownloadError::Unexpected( + "Failed to convert path to string".into(), + ))? + .to_owned(); + + while let Some(bytes) = stream.next().await { + if token.is_cancelled() { + return Err(DownloadError::Cancelled); + } + + let bytes = bytes?; + target_file.write_all(&bytes).await?; + + progress += bytes.len(); + + sender + .send(DownloadEvent::Chunk { + path: path_key.clone(), + progress, + size: bytes.len(), + total: content_length, + }) + .await?; + } + + Ok(()) } pub async fn download_all( - client: &Client, - items: Vec, - sender: Arc>, - token: Arc, - workers: usize, + client: &Client, + items: Vec, + sender: Arc>, + token: Arc, + workers: usize, ) -> Result<(), DownloadError> { - let len = items.len(); - let counter = Arc::new(AtomicUsize::new(0)); - - let mut futures = fut_iter(items.into_iter().map(|it| { - let counter = counter.clone(); - let client = client.clone(); - let sender = sender.clone(); - let token = token.clone(); - - tokio::spawn(async move { - match download(&client, it.clone(), &sender, &token).await { - Ok(result) => { - counter.fetch_add(1, Ordering::Relaxed); - - sender - .send(DownloadEvent::Finish { - item: it.clone(), - total: len, - progress: counter.load(Ordering::Relaxed), - }) - .await?; - - Ok(result) - } - Err(err) => { - sender - .send(DownloadEvent::Error { - item: it.clone(), - error: err.clone(), - }) - .await?; - Err(err) - } - } - }) - })) - .buffer_unordered(workers); - - while let Some(result) = futures.next().await { - result?? - } - - Ok(()) + let len = items.len(); + let counter = Arc::new(AtomicUsize::new(0)); + + let mut futures = fut_iter(items.into_iter().map(|it| { + let counter = counter.clone(); + let client = client.clone(); + let sender = sender.clone(); + let token = token.clone(); + + tokio::spawn(async move { + match download(&client, it.clone(), &sender, &token).await { + Ok(result) => { + counter.fetch_add(1, Ordering::Relaxed); + + sender + .send(DownloadEvent::Finish { + item: it.clone(), + total: len, + progress: counter.load(Ordering::Relaxed), + }) + .await?; + + Ok(result) + } + Err(err) => { + sender + .send(DownloadEvent::Error { + item: it.clone(), + error: err.clone(), + }) + .await?; + Err(err) + } + } + }) + })) + .buffer_unordered(workers); + + while let Some(result) = futures.next().await { + result?? + } + + Ok(()) } diff --git a/crates/download/src/main.rs b/crates/download/src/main.rs index 23dc3fc..b66b55b 100644 --- a/crates/download/src/main.rs +++ b/crates/download/src/main.rs @@ -3,7 +3,7 @@ use { download_all, DownloadEvent, Item, - }, + }, reqwest::Client, std::sync::Arc, tokio::sync::mpsc::channel, @@ -13,7 +13,7 @@ use { #[tokio::main] async fn main() { - let items = vec![ + let items = vec![ Item { known_size: None, // known_sha: None, @@ -30,15 +30,14 @@ async fn main() { } ]; - let client = Client::new(); + let client = Client::new(); - let (tx, mut rx) = channel::(1024); - let token = Arc::new(CancellationToken::new()); + let (tx, mut rx) = channel::(1024); + let token = Arc::new(CancellationToken::new()); let task_token = token.clone(); - let task = tokio::spawn(async move { - download_all(&client, items, Arc::new(tx), task_token, 2).await - }); + let task = + tokio::spawn(async move { download_all(&client, items, Arc::new(tx), task_token, 2).await }); // tokio::spawn(async move { // tokio::time::sleep(Duration::from_secs(3)).await; diff --git a/crates/ipc/src/lib.rs b/crates/ipc/src/lib.rs index b1571c9..11b795f 100644 --- a/crates/ipc/src/lib.rs +++ b/crates/ipc/src/lib.rs @@ -1,6 +1,6 @@ #![feature(specialization)] -mod lookup; pub(crate) mod error; +mod lookup; pub use lookup::*; diff --git a/crates/ipc/src/lookup/mod.rs b/crates/ipc/src/lookup/mod.rs index 289764c..b1123a5 100644 --- a/crates/ipc/src/lookup/mod.rs +++ b/crates/ipc/src/lookup/mod.rs @@ -1,9 +1,9 @@ use { - crate::error::IpcError, - std::path::Path, + crate::error::IpcError, + std::path::Path, }; #[tauri::command] pub async fn lookup_versions(path: &Path) -> Result, IpcError> { - Ok(lookup::lookup_versions(path).await?) + Ok(lookup::lookup_versions(path).await?) } diff --git a/crates/launcher/src/from.rs b/crates/launcher/src/from.rs index 3310774..54fe8c5 100644 --- a/crates/launcher/src/from.rs +++ b/crates/launcher/src/from.rs @@ -1,97 +1,95 @@ use { - crate::launcher::Launcher, - common::{ - libutils::libname_to_path, - manifest::{ - Argument, - ConditionalArgument, - Library::{ - self, - }, - ModernArgs, - RootManifest, - Rule, - VersionType, - }, - }, - once_cell::sync::Lazy, - std::collections::HashSet, - thiserror::Error, + crate::launcher::Launcher, + common::{ + libutils::libname_to_path, + manifest::{ + Argument, + ConditionalArgument, + Library::{self,}, + ModernArgs, + Os, + RootManifest, + Rule, + VersionType, + }, + }, + once_cell::sync::Lazy, + std::collections::HashSet, + thiserror::Error, }; -use common::manifest::Os; static DEFAULT_FEATURES: Lazy> = - Lazy::new(|| HashSet::from(["has_custom_resolution"])); + Lazy::new(|| HashSet::from(["has_custom_resolution"])); fn process_args(args: Vec, to: &mut Vec) { - for arg in args { - match arg { - Argument::Constant(it) => to.push(it), - Argument::Conditional { rules, value } => { - if !rules.iter().all(|it| it.unwrap_featured(&DEFAULT_FEATURES)) { - continue; - } + for arg in args { + match arg { + Argument::Constant(it) => to.push(it), + Argument::Conditional { rules, value } => { + if !rules.iter().all(|it| it.unwrap_featured(&DEFAULT_FEATURES)) { + continue; + } - match value { - ConditionalArgument::Single(it) => to.push(it), - ConditionalArgument::List(it) => to.extend(it), - } - } - } - } + match value { + ConditionalArgument::Single(it) => to.push(it), + ConditionalArgument::List(it) => to.extend(it), + } + } + } + } } #[derive(Debug, Error)] pub enum FromError { - #[error("Failed to convert manifest to launcher instance: {0}")] - InvalidManifest(String), + #[error("Failed to convert manifest to launcher instance: {0}")] + InvalidManifest(String), } impl TryFrom for Launcher { - type Error = FromError; + type Error = FromError; - fn try_from(value: RootManifest) -> Result { - let mut launcher = Launcher { - id: value.id, - asset_index_name: value.assets, - main_class: value.main_class, + fn try_from(value: RootManifest) -> Result { + let mut launcher = Launcher { + id: value.id, + asset_index_name: value.assets, + main_class: value.main_class, jre_component: value.java_version.component, - version_type: match value.version_type { - VersionType::Release => "release", - VersionType::Snapshot => "snapshot", - VersionType::OldBeta => "old_beta", - VersionType::OldAlpha => "old_alpha", - } - .into(), + version_type: match value.version_type { + VersionType::Release => "release", + VersionType::Snapshot => "snapshot", + VersionType::OldBeta => "old_beta", + VersionType::OldAlpha => "old_alpha", + } + .into(), ..Default::default() - }; + }; - for lib in value.libraries { - match lib { - Library::Custom { name, .. } => launcher.classpath.push( - libname_to_path(&name).ok_or(FromError::InvalidManifest("Invalid lib name".into()))?, - ), - Library::Seminative { - rules, downloads, .. - } => { - if rules.iter().all(Rule::unwrap) { - launcher.classpath.push(downloads.artifact.path); - } - } - Library::Default { downloads, .. } => launcher.classpath.push(downloads.artifact.path), - Library::Native { downloads, .. } => launcher.classpath.push(downloads.artifact.path), - } - } + for lib in value.libraries { + match lib { + Library::Custom { name, .. } => launcher.classpath.push( + libname_to_path(&name).ok_or(FromError::InvalidManifest("Invalid lib name".into()))?, + ), + Library::Seminative { + rules, downloads, .. + } => { + if rules.iter().all(Rule::unwrap) { + launcher.classpath.push(downloads.artifact.path); + } + } + Library::Default { downloads, .. } => launcher.classpath.push(downloads.artifact.path), + Library::Native { downloads, .. } => launcher.classpath.push(downloads.artifact.path), + } + } - launcher - .classpath - .push(format!("../versions/{0}/{0}.jar", &launcher.id).into()); + launcher + .classpath + .push(format!("../versions/{0}/{0}.jar", &launcher.id).into()); - let ModernArgs { arguments }: ModernArgs = value.arguments.into(); + let ModernArgs { arguments }: ModernArgs = value.arguments.into(); - process_args(arguments.jvm, &mut launcher.jvm_args); - process_args(arguments.game, &mut launcher.game_args); + process_args(arguments.jvm, &mut launcher.jvm_args); + process_args(arguments.game, &mut launcher.game_args); - Ok(launcher) - } + Ok(launcher) + } } diff --git a/crates/launcher/src/launcher.rs b/crates/launcher/src/launcher.rs index a5d51ff..73fe135 100644 --- a/crates/launcher/src/launcher.rs +++ b/crates/launcher/src/launcher.rs @@ -1,163 +1,163 @@ use { - std::{ - collections::HashMap, - fs::{ - set_permissions, - Permissions, - }, - iter, - os::unix::fs::PermissionsExt, - path::PathBuf, - process::Command, - }, - thiserror::Error, + std::{ + collections::HashMap, + fs::{ + set_permissions, + Permissions, + }, + iter, + os::unix::fs::PermissionsExt, + path::PathBuf, + process::Command, + }, + thiserror::Error, }; #[derive(Debug)] pub struct Launcher { - pub id: String, - pub jre_component: String, - pub classpath: Vec, - pub asset_index_name: String, - pub version_type: String, - pub jvm_args: Vec, - pub game_args: Vec, - pub main_class: String, - pub root_dir: PathBuf, - pub game_dir: PathBuf, - pub username: String, - pub uuid: String, - pub access_token: String, - pub width: u32, - pub height: u32, - pub fullscreen: bool, + pub id: String, + pub jre_component: String, + pub classpath: Vec, + pub asset_index_name: String, + pub version_type: String, + pub jvm_args: Vec, + pub game_args: Vec, + pub main_class: String, + pub root_dir: PathBuf, + pub game_dir: PathBuf, + pub username: String, + pub uuid: String, + pub access_token: String, + pub width: u32, + pub height: u32, + pub fullscreen: bool, } impl Default for Launcher { - fn default() -> Self { - let home_dir = directories::BaseDirs::new() - .unwrap() - .home_dir() - .join(".minecraft"); - - Self { - id: String::new(), - jre_component: "java-runtime-gamma".into(), - width: 1280, - height: 720, - root_dir: home_dir.clone(), - game_dir: home_dir.clone(), - username: "Player".into(), - uuid: "00000000-0000-0000-0000-000000000000".into(), - fullscreen: false, - access_token: "local".into(), - classpath: Vec::new(), - asset_index_name: String::new(), - version_type: "release".into(), - jvm_args: Vec::new(), - game_args: Vec::new(), - main_class: String::new(), - } - } + fn default() -> Self { + let home_dir = directories::BaseDirs::new() + .unwrap() + .home_dir() + .join(".minecraft"); + + Self { + id: String::new(), + jre_component: "java-runtime-gamma".into(), + width: 1280, + height: 720, + root_dir: home_dir.clone(), + game_dir: home_dir.clone(), + username: "Player".into(), + uuid: "00000000-0000-0000-0000-000000000000".into(), + fullscreen: false, + access_token: "local".into(), + classpath: Vec::new(), + asset_index_name: String::new(), + version_type: "release".into(), + jvm_args: Vec::new(), + game_args: Vec::new(), + main_class: String::new(), + } + } } #[derive(Debug, Error)] pub enum LaunchError { - #[error("Invalid path")] - InvalidPath, + #[error("Invalid path")] + InvalidPath, - #[error(transparent)] - Io(#[from] std::io::Error), + #[error(transparent)] + Io(#[from] std::io::Error), } impl Launcher { - pub fn launch(&self) -> Result { - let lib_dir = self.root_dir.join("libraries"); - let nat_dir = self - .root_dir - .join("versions") - .join(&self.id) - .join("natives"); - let assets_dir = self.root_dir.join("assets"); - - let classpath = self - .classpath - .iter() - .map(|it| { - Ok( - lib_dir - .join(it) - .canonicalize()? - .to_str() - .ok_or(LaunchError::InvalidPath)? - .to_owned(), - ) - }) - .collect::, LaunchError>>()?; - - let mut vars = HashMap::<&str, &str>::new(); - - let var_game_dir = &self.game_dir.canonicalize()?; - let var_assets_dir = assets_dir.canonicalize()?; - let var_nat_dir = nat_dir.canonicalize()?; - - vars.insert("${auth_player_name}", &self.username); - vars.insert("${version_name}", &self.id); - vars.insert( - "${game_directory}", - var_game_dir.to_str().ok_or(LaunchError::InvalidPath)?, - ); - vars.insert( - "${assets_root}", - var_assets_dir.to_str().ok_or(LaunchError::InvalidPath)?, - ); - vars.insert( - "${natives_directory}", - var_nat_dir.to_str().ok_or(LaunchError::InvalidPath)?, - ); - - let width = self.width.to_string(); - let height = self.height.to_string(); - - vars.insert("${assets_index_name}", &self.asset_index_name); - vars.insert("${auth_uuid}", &self.uuid); - vars.insert("${auth_access_token}", &self.access_token); - vars.insert("${version_type}", &self.version_type); - vars.insert("${resolution_width}", &width); - vars.insert("${resolution_height}", &height); - vars.insert("${launcher_name}", "coppertiles"); - vars.insert("${launcher_version}", "unknown"); - - #[cfg(target_family = "windows")] - let cp_str = classpath.join(":"); - #[cfg(not(target_family = "windows"))] - let cp_str = classpath.join(":"); - - vars.insert("${classpath}", &cp_str); - - let args: Vec<_> = self - .jvm_args - .iter() - .chain(iter::once(&self.main_class)) - .chain(&self.game_args) - .map(|arg| { - let mut arg = arg.to_owned(); - for (key, value) in &vars { - arg = arg.replace(key, value); - } - arg - }) - .collect(); - - let bin = PathBuf::from("jre") - .join(&self.jre_component) - .join("bin/java"); - - set_permissions(self.root_dir.join(&bin), Permissions::from_mode(0o744))?; - - let mut command = Command::new(bin); - command.current_dir(&self.root_dir); - command.args(args); - Ok(command) - } + pub fn launch(&self) -> Result { + let lib_dir = self.root_dir.join("libraries"); + let nat_dir = self + .root_dir + .join("versions") + .join(&self.id) + .join("natives"); + let assets_dir = self.root_dir.join("assets"); + + let classpath = self + .classpath + .iter() + .map(|it| { + Ok( + lib_dir + .join(it) + .canonicalize()? + .to_str() + .ok_or(LaunchError::InvalidPath)? + .to_owned(), + ) + }) + .collect::, LaunchError>>()?; + + let mut vars = HashMap::<&str, &str>::new(); + + let var_game_dir = &self.game_dir.canonicalize()?; + let var_assets_dir = assets_dir.canonicalize()?; + let var_nat_dir = nat_dir.canonicalize()?; + + vars.insert("${auth_player_name}", &self.username); + vars.insert("${version_name}", &self.id); + vars.insert( + "${game_directory}", + var_game_dir.to_str().ok_or(LaunchError::InvalidPath)?, + ); + vars.insert( + "${assets_root}", + var_assets_dir.to_str().ok_or(LaunchError::InvalidPath)?, + ); + vars.insert( + "${natives_directory}", + var_nat_dir.to_str().ok_or(LaunchError::InvalidPath)?, + ); + + let width = self.width.to_string(); + let height = self.height.to_string(); + + vars.insert("${assets_index_name}", &self.asset_index_name); + vars.insert("${auth_uuid}", &self.uuid); + vars.insert("${auth_access_token}", &self.access_token); + vars.insert("${version_type}", &self.version_type); + vars.insert("${resolution_width}", &width); + vars.insert("${resolution_height}", &height); + vars.insert("${launcher_name}", "coppertiles"); + vars.insert("${launcher_version}", "unknown"); + + #[cfg(target_family = "windows")] + let cp_str = classpath.join(":"); + #[cfg(not(target_family = "windows"))] + let cp_str = classpath.join(":"); + + vars.insert("${classpath}", &cp_str); + + let args: Vec<_> = self + .jvm_args + .iter() + .chain(iter::once(&self.main_class)) + .chain(&self.game_args) + .map(|arg| { + let mut arg = arg.to_owned(); + for (key, value) in &vars { + arg = arg.replace(key, value); + } + arg + }) + .collect(); + + let bin = PathBuf::from("jre") + .join(&self.jre_component) + .join("bin/java"); + + set_permissions(self.root_dir.join(&bin), Permissions::from_mode(0o744))?; + + let mut command = Command::new(bin); + command.current_dir(&self.root_dir); + command.args(args); + Ok(command) + } } diff --git a/crates/launcher/src/lib.rs b/crates/launcher/src/lib.rs index 189c3fc..6decabc 100644 --- a/crates/launcher/src/lib.rs +++ b/crates/launcher/src/lib.rs @@ -1,4 +1,4 @@ -mod launcher; mod from; +mod launcher; pub use launcher::*; diff --git a/crates/launcher/src/main.rs b/crates/launcher/src/main.rs index 82af880..a5e2b07 100644 --- a/crates/launcher/src/main.rs +++ b/crates/launcher/src/main.rs @@ -8,22 +8,22 @@ use { }; fn main() -> Result<(), Box> { - let mut json = String::new(); - File::open("./minecraft/versions/1.16.4/1.16.4.json") - .unwrap() - .read_to_string(&mut json) - .unwrap(); + let mut json = String::new(); + File::open("./minecraft/versions/1.16.4/1.16.4.json") + .unwrap() + .read_to_string(&mut json) + .unwrap(); - let manifest: RootManifest = serde_json::from_str(&json).unwrap(); + let manifest: RootManifest = serde_json::from_str(&json).unwrap(); - let mut launcher: Launcher = manifest.try_into()?; + let mut launcher: Launcher = manifest.try_into()?; - launcher.game_dir = "./minecraft/game".into(); - launcher.root_dir = "./minecraft".into(); + launcher.game_dir = "./minecraft/game".into(); + launcher.root_dir = "./minecraft".into(); - let mut command = launcher.launch()?; + let mut command = launcher.launch()?; - command.spawn()?.wait()?; + command.spawn()?.wait()?; - Ok(()) + Ok(()) } diff --git a/crates/lookup/src/main.rs b/crates/lookup/src/main.rs index a48caff..108b833 100644 --- a/crates/lookup/src/main.rs +++ b/crates/lookup/src/main.rs @@ -4,7 +4,9 @@ mod versions; #[tokio::main] async fn main() { - let result = versions::lookup_versions(Path::new("./minecraft")).await.unwrap(); + let result = versions::lookup_versions(Path::new("./minecraft")) + .await + .unwrap(); println!("{:#?}", &result); } diff --git a/crates/lookup/src/versions/mod.rs b/crates/lookup/src/versions/mod.rs index 9eaebd8..87d7210 100644 --- a/crates/lookup/src/versions/mod.rs +++ b/crates/lookup/src/versions/mod.rs @@ -1,39 +1,39 @@ pub use overview::VersionOverview; use { - profile::{ - read_profile, - ProfileEntry, - }, - std::path::Path, - tokio::fs, + profile::{ + read_profile, + ProfileEntry, + }, + std::path::Path, + tokio::fs, }; mod overview; pub async fn lookup_versions(root: &Path) -> Result, std::io::Error> { - let versions_dir = root.join("versions"); - let mut versions_entries = fs::read_dir(&versions_dir).await?; + let versions_dir = root.join("versions"); + let mut versions_entries = fs::read_dir(&versions_dir).await?; - let mut versions = Vec::::new(); + let mut versions = Vec::::new(); - while let Some(entry) = versions_entries.next_entry().await? { - let id = entry.file_name(); - let id_as_str = id.to_str().unwrap(); + while let Some(entry) = versions_entries.next_entry().await? { + let id = entry.file_name(); + let id_as_str = id.to_str().unwrap(); - let profiles = read_profile(root).await?.profiles; + let profiles = read_profile(root).await?.profiles; - if let Some(ProfileEntry { icon, .. }) = profiles.get(id_as_str) { - versions.push(VersionOverview { - id: id_as_str.to_owned(), - icon: icon.to_owned(), - }) - } else { - versions.push(VersionOverview { - id: id_as_str.to_owned(), - icon: None, - }) - } - } + if let Some(ProfileEntry { icon, .. }) = profiles.get(id_as_str) { + versions.push(VersionOverview { + id: id_as_str.to_owned(), + icon: icon.to_owned(), + }) + } else { + versions.push(VersionOverview { + id: id_as_str.to_owned(), + icon: None, + }) + } + } - Ok(versions) + Ok(versions) } diff --git a/crates/lookup/src/versions/overview.rs b/crates/lookup/src/versions/overview.rs index 749af85..50c1fa4 100644 --- a/crates/lookup/src/versions/overview.rs +++ b/crates/lookup/src/versions/overview.rs @@ -1,12 +1,15 @@ -use serde::{Deserialize, Serialize}; +use serde::{ + Deserialize, + Serialize, +}; #[derive(Debug, Deserialize)] pub struct ManifestPart { - pub id: String + pub id: String, } #[derive(Debug, Serialize)] pub struct VersionOverview { pub id: String, - pub icon: Option + pub icon: Option, } diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index 8c560ad..3b79181 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs @@ -1,41 +1,41 @@ -use std::io::ErrorKind; use { - serde::{ - Deserialize, - Serialize, - }, - std::{ - collections::HashMap, - path::Path, - }, - tokio::{ - fs, - io::AsyncWriteExt, - }, + serde::{ + Deserialize, + Serialize, + }, + std::{ + collections::HashMap, + io::ErrorKind, + path::Path, + }, + tokio::{ + fs, + io::AsyncWriteExt, + }, }; #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct ProfileEntry { - pub last_version_id: String, - pub icon: Option, + pub last_version_id: String, + pub icon: Option, } #[derive(Debug, Deserialize, Serialize, Default)] pub struct Profile { - pub profiles: HashMap, + pub profiles: HashMap, } pub async fn read_profile(root: &Path) -> Result { - let contents = fs::read_to_string(root.join("profile.json")).await?; + let contents = fs::read_to_string(root.join("profile.json")).await?; - Ok(serde_json::from_str(contents.as_str())?) + Ok(serde_json::from_str(contents.as_str())?) } pub async fn create_empty_profile(root: &Path) -> Result<(), std::io::Error> { - let mut file = fs::File::create(root).await?; + let mut file = fs::File::create(root).await?; - file.write_all(br#"{"profiles":{}}"#).await?; + file.write_all(br#"{"profiles":{}}"#).await?; - Ok(()) + Ok(()) } diff --git a/crates/providers/mojang/src/api.rs b/crates/providers/mojang/src/api.rs index 5a953e2..d771704 100644 --- a/crates/providers/mojang/src/api.rs +++ b/crates/providers/mojang/src/api.rs @@ -3,77 +3,77 @@ use { jre::{ all::JavaRuntime, manifest::Manifest as JreManifest, - }, + }, manifest::RootManifest, - }, + }, once_cell::sync::Lazy, serde::Deserialize, url::Url, }; pub static PISTON_META_BASE_URL: Lazy = - Lazy::new(|| Url::parse("https://piston-meta.mojang.com/").unwrap()); + Lazy::new(|| Url::parse("https://piston-meta.mojang.com/").unwrap()); pub static MINECRAFT_RESOURCES_BASE_URL: Lazy = - Lazy::new(|| Url::parse("https://resources.download.minecraft.net").unwrap()); + Lazy::new(|| Url::parse("https://resources.download.minecraft.net").unwrap()); #[derive(Debug, Deserialize)] pub struct Version { - pub id: String, - #[serde(rename = "type")] - pub version_type: String, - pub url: Url, - pub sha1: String, + pub id: String, + #[serde(rename = "type")] + pub version_type: String, + pub url: Url, + pub sha1: String, } #[derive(Debug, Deserialize)] pub struct Latest { - pub release: String, - pub snapshot: String, + pub release: String, + pub snapshot: String, } #[derive(Debug, Deserialize)] pub struct Versions { - pub latest: Latest, - pub versions: Vec, + pub latest: Latest, + pub versions: Vec, } pub async fn get_versions_manifest() -> Result, reqwest::Error> { - reqwest::get( - PISTON_META_BASE_URL - .join("mc/game/version_manifest_v2.json") - .unwrap(), - ) - .await? - .json() - .await + reqwest::get( + PISTON_META_BASE_URL + .join("mc/game/version_manifest_v2.json") + .unwrap(), + ) + .await? + .json() + .await } pub async fn get_manifest(sha: &str, id: &str) -> Result { - reqwest::get( - PISTON_META_BASE_URL - .join(&format!("v1/packages/{sha}/{id}.json")) - .unwrap(), - ) - .await? - .json() - .await + reqwest::get( + PISTON_META_BASE_URL + .join(&format!("v1/packages/{sha}/{id}.json")) + .unwrap(), + ) + .await? + .json() + .await } pub async fn get_jre_components() -> Result { - reqwest::get("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json") + reqwest::get("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json") .await? .json() .await } pub async fn get_jre_manifest(sha: &str) -> Result { - reqwest::get( - PISTON_META_BASE_URL - .join(&format!("v1/packages/{sha}/manifest.json")) - .unwrap(), - ) - .await? - .json() - .await + reqwest::get( + PISTON_META_BASE_URL + .join(&format!("v1/packages/{sha}/manifest.json")) + .unwrap(), + ) + .await? + .json() + .await } diff --git a/crates/providers/mojang/src/install.rs b/crates/providers/mojang/src/install.rs index 495e51b..583ecfa 100644 --- a/crates/providers/mojang/src/install.rs +++ b/crates/providers/mojang/src/install.rs @@ -1,15 +1,21 @@ use { + crate::api::{ + get_jre_components, + get_jre_manifest, + get_versions_manifest, + MINECRAFT_RESOURCES_BASE_URL, + }, common::{ jre::{ all::{ ComponentType, Target, - }, + }, manifest::{ Entry, Manifest as JreManifest, - }, - }, + }, + }, libutils::libname_to_path, manifest::{ Artifact, @@ -18,14 +24,8 @@ use { Os, RootManifest, Rule, - }, - }, - crate::api::{ - get_jre_components, - get_jre_manifest, - get_versions_manifest, - MINECRAFT_RESOURCES_BASE_URL, - }, + }, + }, download::Item as DownloadItem, once_cell::sync::Lazy, reqwest::Error, @@ -35,221 +35,222 @@ use { path::{ Path, PathBuf, - }, - }, + }, + }, thiserror::Error, url::Url, }; #[derive(Debug, PartialEq, Eq, Hash)] pub enum Kind { - Lib, - Asset, - Version, - Jre, + Lib, + Asset, + Version, + Jre, } #[derive(Debug)] pub struct Item { - pub kind: Kind, - pub url: Url, - pub path: PathBuf, - pub known_size: Option, - pub known_sha: Option, + pub kind: Kind, + pub url: Url, + pub path: PathBuf, + pub known_size: Option, + pub known_sha: Option, } static PATHS: Lazy> = Lazy::new(|| { - HashMap::from([ - (Kind::Version, PathBuf::from("versions")), - (Kind::Lib, PathBuf::from("libraries")), - (Kind::Asset, PathBuf::from("assets")), - (Kind::Jre, PathBuf::from("jre")), - ]) + HashMap::from([ + (Kind::Version, PathBuf::from("versions")), + (Kind::Lib, PathBuf::from("libraries")), + (Kind::Asset, PathBuf::from("assets")), + (Kind::Jre, PathBuf::from("jre")), + ]) }); impl Item { - pub fn place(self, root: &Path) -> DownloadItem { - DownloadItem { - path: root.join( - PATHS - .get(&self.kind) - .expect("Unknown item kind") - .join(self.path), - ), - url: self.url, - known_sha: self.known_sha, - known_size: self.known_size, - } - } + pub fn place(self, root: &Path) -> DownloadItem { + DownloadItem { + path: root.join( + PATHS + .get(&self.kind) + .expect("Unknown item kind") + .join(self.path), + ), + url: self.url, + known_sha: self.known_sha, + known_size: self.known_size, + } + } } impl From for Item { - fn from( - Artifact { - url, - path, - size, - sha1, - }: Artifact, - ) -> Self { - Self { - kind: Kind::Lib, - url, - path, - known_size: Some(size), - known_sha: Some(sha1), - } - } + fn from( + Artifact { + url, + path, + size, + sha1, + }: Artifact, + ) -> Self { + Self { + kind: Kind::Lib, + url, + path, + known_size: Some(size), + known_sha: Some(sha1), + } + } } #[derive(Debug, Error, Deserialize)] pub enum InstallError { - #[error("Malformed or unsupported manifest: {0}")] - InvalidManifest(String), + #[error("Malformed or unsupported manifest: {0}")] + InvalidManifest(String), - #[error("network: {0}")] - Network(String), + #[error("network: {0}")] + Network(String), - #[error("unsupported: {0}")] - Unsupported(String), + #[error("unsupported: {0}")] + Unsupported(String), - #[error("unexpected")] - Unexpected(String), + #[error("unexpected")] + Unexpected(String), } impl From for InstallError { - fn from(value: Error) -> Self { - Self::Network(value.to_string()) - } + fn from(value: Error) -> Self { + Self::Network(value.to_string()) + } } pub trait Install { - fn into_items(self) -> Result, InstallError>; + fn into_items(self) -> Result, InstallError>; } impl Install for RootManifest { - fn into_items(self) -> Result, InstallError> { - let mut items: Vec = Vec::new(); + fn into_items(self) -> Result, InstallError> { + let mut items: Vec = Vec::new(); let version_path = PathBuf::from(&self.id); - items.push(Item { - kind: Kind::Version, - url: self.downloads.client.url, - path: version_path.join(format!("{}.jar", &self.id)), - known_size: Some(self.downloads.client.size), - known_sha: Some(self.downloads.client.sha1), - }); - - let native_path = version_path.join("natives"); - - for lib in self.libraries { - match lib { - Library::Custom { name, url } => { - let path = libname_to_path(&name) - .ok_or(InstallError::InvalidManifest("Invalid lib name".into()))?; - - let url = url - .join(path.to_str().expect("Failed to parse artifact path")) - .map_err(|_| InstallError::InvalidManifest("Failed to parse lib url".into()))?; - - items.push(Item { - kind: Kind::Lib, - url, - path, - known_size: None, - known_sha: None, - }); - } - Library::Native { - rules, - mut downloads, - natives, - .. - } => { - items.push(Item { - kind: Kind::Lib, - url: downloads.artifact.url, - path: downloads.artifact.path, - known_size: Some(downloads.artifact.size), - known_sha: Some(downloads.artifact.sha1), - }); - - if !rules.iter().all(Rule::unwrap) { - continue; - } - - #[cfg(target_os = "windows")] - let classifier = natives.get(&Os::Windows); - #[cfg(target_os = "macos")] - let classifier = natives.get(&Os::Osx); - #[cfg(target_os = "linux")] - let classifier = natives.get(&Os::Linux); - #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] - panic!("Unsupported os"); - - let classifier = classifier.ok_or(InstallError::InvalidManifest( - "Inappropriate native classifier".into(), - ))?; - - let Artifact { - url, - path, - size, - sha1, - } = downloads.classifiers - .remove(classifier) - .ok_or(InstallError::InvalidManifest( - "Inappropriate native classifier".into(), - ))?; + items.push(Item { + kind: Kind::Version, + url: self.downloads.client.url, + path: version_path.join(format!("{}.jar", &self.id)), + known_size: Some(self.downloads.client.size), + known_sha: Some(self.downloads.client.sha1), + }); + + let native_path = version_path.join("natives"); + + for lib in self.libraries { + match lib { + Library::Custom { name, url } => { + let path = libname_to_path(&name) + .ok_or(InstallError::InvalidManifest("Invalid lib name".into()))?; + + let url = url + .join(path.to_str().expect("Failed to parse artifact path")) + .map_err(|_| InstallError::InvalidManifest("Failed to parse lib url".into()))?; + + items.push(Item { + kind: Kind::Lib, + url, + path, + known_size: None, + known_sha: None, + }); + } + Library::Native { + rules, + mut downloads, + natives, + .. + } => { + items.push(Item { + kind: Kind::Lib, + url: downloads.artifact.url, + path: downloads.artifact.path, + known_size: Some(downloads.artifact.size), + known_sha: Some(downloads.artifact.sha1), + }); + + if !rules.iter().all(Rule::unwrap) { + continue; + } + + #[cfg(target_os = "windows")] + let classifier = natives.get(&Os::Windows); + #[cfg(target_os = "macos")] + let classifier = natives.get(&Os::Osx); + #[cfg(target_os = "linux")] + let classifier = natives.get(&Os::Linux); + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + panic!("Unsupported os"); + + let classifier = classifier.ok_or(InstallError::InvalidManifest( + "Inappropriate native classifier".into(), + ))?; + + let Artifact { + url, + path, + size, + sha1, + } = downloads + .classifiers + .remove(classifier) + .ok_or(InstallError::InvalidManifest( + "Inappropriate native classifier".into(), + ))?; println!("{:?}", path); - items.push(Item { - kind: Kind::Version, - url, - path: native_path.join(path.iter().last().unwrap()), - known_size: Some(size), - known_sha: Some(sha1), - }); - } - Library::Seminative { - rules, downloads, .. - } => { - if !rules.iter().all(Rule::unwrap) { - continue; - } - - items.push(downloads.artifact.into()); - } - Library::Default { downloads, .. } => { - items.push(downloads.artifact.into()); - } - } - } - - Ok(items) - } + items.push(Item { + kind: Kind::Version, + url, + path: native_path.join(path.iter().last().unwrap()), + known_size: Some(size), + known_sha: Some(sha1), + }); + } + Library::Seminative { + rules, downloads, .. + } => { + if !rules.iter().all(Rule::unwrap) { + continue; + } + + items.push(downloads.artifact.into()); + } + Library::Default { downloads, .. } => { + items.push(downloads.artifact.into()); + } + } + } + + Ok(items) + } } impl Install for AssetIndex { - fn into_items(self) -> Result, InstallError> { - let base = PathBuf::from("objects"); - let mut items: Vec = Vec::with_capacity(2048); - - for it in self.objects.into_values() { - let path = format!("{}/{}", &it.hash[..2], it.hash); - - items.push(Item { - kind: Kind::Asset, - url: MINECRAFT_RESOURCES_BASE_URL.join(&path).unwrap(), - path: base.join(path), - known_size: Some(it.size), - known_sha: Some(it.hash), - }); - } - - Ok(items) - } + fn into_items(self) -> Result, InstallError> { + let base = PathBuf::from("objects"); + let mut items: Vec = Vec::with_capacity(2048); + + for it in self.objects.into_values() { + let path = format!("{}/{}", &it.hash[..2], it.hash); + + items.push(Item { + kind: Kind::Asset, + url: MINECRAFT_RESOURCES_BASE_URL.join(&path).unwrap(), + path: base.join(path), + known_size: Some(it.size), + known_sha: Some(it.hash), + }); + } + + Ok(items) + } } trait JreInstall { @@ -257,13 +258,13 @@ trait JreInstall { } impl JreInstall for JreManifest { - fn into_items(self, jre_component: &Path) -> Result, InstallError> { - Ok( - self - .files - .into_iter() - .filter_map(|(path, entry)| match entry { - Entry::File { downloads, .. } => { + fn into_items(self, jre_component: &Path) -> Result, InstallError> { + Ok( + self + .files + .into_iter() + .filter_map(|(path, entry)| match entry { + Entry::File { downloads, .. } => { let file = downloads.raw; Some(Item { @@ -273,49 +274,49 @@ impl JreInstall for JreManifest { known_size: Some(file.size), known_sha: Some(file.sha1), }) - }, - _ => None, - }) - .collect(), - ) - } + } + _ => None, + }) + .collect(), + ) + } } pub async fn get_items(id: &str) -> Result, InstallError> { - let versions = get_versions_manifest().await?; - let version = versions - .versions - .into_iter() - .find(|it| it.id == id) - .expect("Unknown id"); - - let manifest: RootManifest = reqwest::get(version.url.clone()).await?.json().await?; - let asset_index = reqwest::get(manifest.asset_index.url.clone()) - .await? - .json::() - .await?; - - let jre_manifest = get_jre_components().await?; - let jre_target = jre_manifest - .get( - #[cfg(all(target_os = "linux", target_arch = "x86_64"))] - &Target::Linux, - #[cfg(all(target_os = "linux", target_arch = "x86"))] - &Target::LinuxI386, - #[cfg(all(target_os = "windows", target_arch = "x86_64"))] - &Target::WindowsX64, - #[cfg(all(target_os = "windows", target_arch = "x86"))] - &Target::WindowsX86, - #[cfg(all(target_os = "windows", target_arch = "aarch64"))] - &Target::WindowsArm64, - #[cfg(all(target_os = "macos", target_arch = "x86_64"))] - &Target::Macos, - #[cfg(all(target_os = "macos", target_arch = "aarch64"))] - &Target::Macos, - ) - .ok_or(InstallError::Unsupported("Unsupported platform".into()))?; - - let jre_component = jre_target + let versions = get_versions_manifest().await?; + let version = versions + .versions + .into_iter() + .find(|it| it.id == id) + .expect("Unknown id"); + + let manifest: RootManifest = reqwest::get(version.url.clone()).await?.json().await?; + let asset_index = reqwest::get(manifest.asset_index.url.clone()) + .await? + .json::() + .await?; + + let jre_manifest = get_jre_components().await?; + let jre_target = jre_manifest + .get( + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + &Target::Linux, + #[cfg(all(target_os = "linux", target_arch = "x86"))] + &Target::LinuxI386, + #[cfg(all(target_os = "windows", target_arch = "x86_64"))] + &Target::WindowsX64, + #[cfg(all(target_os = "windows", target_arch = "x86"))] + &Target::WindowsX86, + #[cfg(all(target_os = "windows", target_arch = "aarch64"))] + &Target::WindowsArm64, + #[cfg(all(target_os = "macos", target_arch = "x86_64"))] + &Target::Macos, + #[cfg(all(target_os = "macos", target_arch = "aarch64"))] + &Target::Macos, + ) + .ok_or(InstallError::Unsupported("Unsupported platform".into()))?; + + let jre_component = jre_target .get( &ComponentType::from_str(&manifest.java_version.component) .ok_or(InstallError::Unexpected("Unknown jre component".into()))?, @@ -326,31 +327,31 @@ pub async fn get_items(id: &str) -> Result, InstallError> { "Jre is not supported for current platform".into(), ))?; - let jre_manifest = get_jre_manifest(&jre_component.manifest.sha1).await?; + let jre_manifest = get_jre_manifest(&jre_component.manifest.sha1).await?; - let mut items = Vec::with_capacity(8192); + let mut items = Vec::with_capacity(8192); - items.push(Item { - kind: Kind::Version, - url: version.url, - path: PathBuf::from(id).join(format!("{id}.json")), - known_size: None, - known_sha: Some(version.sha1), - }); + items.push(Item { + kind: Kind::Version, + url: version.url, + path: PathBuf::from(id).join(format!("{id}.json")), + known_size: None, + known_sha: Some(version.sha1), + }); - items.push(Item { - kind: Kind::Asset, - url: manifest.asset_index.url.clone(), - path: PathBuf::from("indexes").join(format!("{}.json", &manifest.assets)), - known_size: None, - known_sha: Some(manifest.asset_index.sha1.clone()), - }); + items.push(Item { + kind: Kind::Asset, + url: manifest.asset_index.url.clone(), + path: PathBuf::from("indexes").join(format!("{}.json", &manifest.assets)), + known_size: None, + known_sha: Some(manifest.asset_index.sha1.clone()), + }); let jre_component = manifest.java_version.component.clone(); - items.extend(manifest.into_items()?); - items.extend(asset_index.into_items()?); - items.extend(jre_manifest.into_items(Path::new(&jre_component))?); + items.extend(manifest.into_items()?); + items.extend(asset_index.into_items()?); + items.extend(jre_manifest.into_items(Path::new(&jre_component))?); - Ok(items) + Ok(items) } diff --git a/crates/providers/mojang/src/main.rs b/crates/providers/mojang/src/main.rs index 3db90d6..1d4f701 100644 --- a/crates/providers/mojang/src/main.rs +++ b/crates/providers/mojang/src/main.rs @@ -2,13 +2,13 @@ use { download::{ download_all, DownloadEvent, - }, + }, reqwest::Client, std::{ path::Path, sync::Arc, time::Duration, - }, + }, tokio::sync::mpsc::channel, tokio_util::sync::CancellationToken, }; @@ -18,31 +18,33 @@ mod install; #[tokio::main] async fn main() -> Result<(), Box> { - let items = install::get_items("1.16.4").await?; + let items = install::get_items("1.16.4").await?; - let root = Path::new("./minecraft"); + let root = Path::new("./minecraft"); - let items: Vec<_> = items.into_iter().map(|it| it.place(root)).collect(); + let items: Vec<_> = items.into_iter().map(|it| it.place(root)).collect(); - let client = Client::builder().connect_timeout(Duration::from_secs(30)).build()?; + let client = Client::builder() + .connect_timeout(Duration::from_secs(30)) + .build()?; - let (tx, mut rx) = channel::(1024); - let token = Arc::new(CancellationToken::new()); + let (tx, mut rx) = channel::(1024); + let token = Arc::new(CancellationToken::new()); - let task_token = token.clone(); - let task = - tokio::spawn(async move { download_all(&client, items, Arc::new(tx), task_token, 4).await }); + let task_token = token.clone(); + let task = + tokio::spawn(async move { download_all(&client, items, Arc::new(tx), task_token, 4).await }); - while let Some(msg) = rx.recv().await { - if let DownloadEvent::Finish { - total, progress, .. - } = msg - { - println!("{progress} / {total}") - }; - } + while let Some(msg) = rx.recv().await { + if let DownloadEvent::Finish { + total, progress, .. + } = msg + { + println!("{progress} / {total}") + }; + } - task.await??; + task.await??; - Ok(()) + Ok(()) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 8127019..7a581ea 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,31 +7,29 @@ use window_vibrancy::apply_blur; #[cfg(target_os = "macos")] use window_vibrancy::{ - apply_vibrancy, - NSVisualEffectMaterial, + apply_vibrancy, + NSVisualEffectMaterial, }; fn main() { - tauri::Builder::default() + tauri::Builder::default() .plugin(tauri_plugin_window::init()) .plugin(tauri_plugin_shell::init()) - .setup(|app| { + .setup(|app| { #[allow(unused)] - let window = app.get_window("main").unwrap(); + let window = app.get_window("main").unwrap(); - #[cfg(target_os = "macos")] - apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None) - .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); + #[cfg(target_os = "macos")] + apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None) + .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); - #[cfg(target_os = "windows")] - apply_blur(&window, None) - .expect("Unsupported platform! 'apply_blur' is only supported on Windows"); + #[cfg(target_os = "windows")] + apply_blur(&window, None) + .expect("Unsupported platform! 'apply_blur' is only supported on Windows"); - Ok(()) - }) - .invoke_handler(tauri::generate_handler![ - ipc::lookup_versions - ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ipc::lookup_versions]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); }