diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 390 | ||||
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/version.rs | 374 | ||||
| -rw-r--r-- | src/version/manifest.rs | 119 |
7 files changed, 899 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40eb61d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/test_stuff diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..45be887 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,390 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "o3launcher" +version = "0.1.0" +dependencies = [ + "chrono", + "regex", + "serde", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e25f08b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "o3launcher" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.39", default-features = false, features = ["std", "alloc", "clock", "now", "serde"] } +regex = "1.11.1" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.133" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..81c2f21 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +mod version; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000..d120093 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,374 @@ +use core::fmt; +use std::{collections::BTreeMap, convert::Infallible, marker::PhantomData, ops::Deref, str::FromStr}; + +use chrono::{DateTime, Utc}; +use regex::Regex; +use serde::{de::{self, Visitor}, Deserialize, Deserializer}; + +pub mod manifest; +use manifest::*; + +#[derive(Deserialize, Debug, Clone, Copy)] +#[serde(rename_all = "lowercase")] +pub enum RuleAction { + Allow, + Disallow +} + +// must derive an order on this because it's used as a key for a btreemap +#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[serde(rename_all = "lowercase")] +pub enum OperatingSystem { + Linux, // "linux" + Windows, // "windows" + + #[serde(alias = "osx")] // not technically correct but it works + MacOS, // "osx" + + #[serde(other)] + Unknown // (not used in official jsons) +} + +#[derive(Debug)] +struct WrappedRegex(Regex); + +impl Deref for WrappedRegex { + type Target = Regex; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +struct RegexVisitor; +impl<'de> Visitor<'de> for RegexVisitor { + type Value = WrappedRegex; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid regular expression") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: de::Error, { + Regex::new(v).map_err(de::Error::custom).map(|r| WrappedRegex(r)) + } +} + +impl<'de> Deserialize<'de> for WrappedRegex { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de> { + deserializer.deserialize_any(RegexVisitor) + } +} + +#[derive(Deserialize, Debug)] +pub struct OSRestriction { + name: Option<OperatingSystem>, + + version: Option<WrappedRegex>, + arch: Option<WrappedRegex> +} + +impl OSRestriction { + pub fn get_name(&self) -> Option<OperatingSystem> { + self.name + } + + pub fn get_version(&self) -> Option<&Regex> { + self.version.as_deref() + } + + pub fn get_arch(&self) -> Option<&Regex> { + self.arch.as_deref() + } +} + +#[derive(Deserialize, Debug)] +pub struct CompatibilityRule { + action: RuleAction, + features: Option<BTreeMap<String, bool>>, + os: Option<OSRestriction> +} + +impl CompatibilityRule { + pub fn get_action(&self) -> RuleAction { + self.action + } + + pub fn get_os(&self) -> Option<&OSRestriction> { + self.os.as_ref() + } + + pub fn get_features(&self) -> Option<&BTreeMap<String, bool>> { + self.features.as_ref() + } + + pub fn features_match(&self, checker: fn(&str) -> bool) -> bool { + if let Some(m) = self.features.as_ref() { + for (feat, expect) in m { + if checker(feat) != *expect { + return false; + } + } + } + + true + } +} + +#[derive(Deserialize, Debug)] +pub struct Argument { + #[serde(default)] + rules: Option<Vec<CompatibilityRule>>, + + #[serde(default)] + #[serde(deserialize_with = "string_or_array")] + value: Vec<String> +} + +impl Argument { + fn get_rules(&self) -> Option<&Vec<CompatibilityRule>> { + self.rules.as_ref() + } + + fn get_value(&self) -> &Vec<String> { + &self.value + } +} + +#[derive(Debug)] +pub struct WrappedArgument(Argument); + +impl FromStr for Argument { + type Err = Infallible; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(Argument { + value: vec![s.to_owned()], + rules: None + }) + } +} + +impl Deref for WrappedArgument { + type Target = Argument; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'de> Deserialize<'de> for WrappedArgument { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de> { + Ok(WrappedArgument(string_or_struct(deserializer)?)) + } +} + +#[derive(Deserialize, Debug)] +pub struct Arguments { + game: Option<Vec<WrappedArgument>>, + jvm: Option<Vec<WrappedArgument>> +} + +impl Arguments { + fn get_game(&self) -> Option<&Vec<WrappedArgument>> { + self.game.as_ref() + } + + fn get_jvm(&self) -> Option<&Vec<WrappedArgument>> { + self.jvm.as_ref() + } +} + +#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "snake_case")] +pub enum DownloadType { + Client, + ClientMappings, + Server, + ServerMappings, + WindowsServer +} + +#[derive(Deserialize, Debug)] +pub struct DownloadInfo { + sha1: Option<String>, + size: Option<usize>, + total_size: Option<usize>, // available for asset index + url: Option<String>, // may not be present for libraries + id: Option<String>, + path: Option<String> +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct JavaVersionInfo { + component: String, + major_version: u32 +} + +#[derive(Deserialize, Debug)] +pub struct LibraryDownloads { + artifact: Option<DownloadInfo>, + classifiers: Option<BTreeMap<String, DownloadInfo>> +} + +#[derive(Deserialize, Debug)] +pub struct LibraryExtractRule { + #[serde(default)] + exclude: Vec<String> +} + +#[derive(Deserialize, Debug)] +pub struct Library { + downloads: Option<LibraryDownloads>, + name: String, + extract: Option<LibraryExtractRule>, + natives: Option<BTreeMap<OperatingSystem, String>>, + rules: Option<Vec<CompatibilityRule>>, + url: Option<String> // old format +} + +#[derive(Deserialize, Debug)] +pub struct ClientLogging { + argument: String, + r#type: String, + file: DownloadInfo +} + +#[derive(Deserialize, Debug)] +pub struct Logging { + client: ClientLogging // other fields unknown +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct CompleteVersion { + arguments: Option<Arguments>, + minecraft_arguments: Option<String>, + + asset_index: Option<DownloadInfo>, + assets: Option<String>, + + compliance_level: Option<u32>, + + java_version: Option<JavaVersionInfo>, + + #[serde(default)] + downloads: BTreeMap<DownloadType, DownloadInfo>, + + #[serde(default)] + libraries: Vec<Library>, + + id: String, + jar: Option<String>, // used as the jar filename if specified? (no longer used officially) + + logging: Option<Logging>, + + main_class: Option<String>, + minimum_launcher_version: Option<u32>, + release_time: Option<DateTime<Utc>>, + time: Option<DateTime<Utc>>, + + #[serde(rename = "type")] + version_type: Option<VersionType>, + + compatibility_rules: Option<Vec<CompatibilityRule>>, // + incompatibility_reason: Option<String>, // message shown when compatibility rules fail for this version + + inherits_from: Option<String> + + /* omitting field `savableVersion' because it seems like a vestigial part from old launcher versions + * (also it isn't even a string that is present in modern liblauncher.so, so I assume it will never be used.) + */ +} + +// https://serde.rs/string-or-struct.html +fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error> +where + T: Deserialize<'de> + FromStr<Err = Infallible>, + D: Deserializer<'de>, +{ + struct StringOrStruct<T>(PhantomData<fn() -> T>); + + impl<'de, T> Visitor<'de> for StringOrStruct<T> + where + T: Deserialize<'de> + FromStr<Err = Infallible>, + { + type Value = T; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> fmt::Result { + formatter.write_str("string or map") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, { + Ok(FromStr::from_str(v).unwrap()) + } + + fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error> + where + A: serde::de::MapAccess<'de>, { + // wizardry (check comment in link) + Deserialize::deserialize(de::value::MapAccessDeserializer::new(map)) + } + } + + deserializer.deserialize_any(StringOrStruct(PhantomData)) +} + +// adapted from above +fn string_or_array<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error> +where + T: Deserialize<'de> + FromStr<Err = Infallible>, + D: Deserializer<'de>, +{ + struct StringOrVec<T>(PhantomData<fn() -> T>); + + impl<'de, T> Visitor<'de> for StringOrVec<T> + where + T: Deserialize<'de> + FromStr<Err = Infallible>, + { + type Value = Vec<T>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("string or array") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: de::Error, { + Ok(vec![FromStr::from_str(v).unwrap()]) + } + + fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error> + where + A: de::SeqAccess<'de>, { + Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq)) + } + } + + deserializer.deserialize_any(StringOrVec(PhantomData)) +} + +#[cfg(test)] +mod tests { + use std::fs; + + use serde_json::json; + + use super::*; + + #[test] + fn test_it() { + let s = fs::read_to_string("./test_stuff/versions/1.7.10.json"); + + let arg: CompleteVersion = serde_json::from_str(s.unwrap().as_str()).unwrap(); + dbg!(arg); + } +} diff --git a/src/version/manifest.rs b/src/version/manifest.rs new file mode 100644 index 0000000..a44e435 --- /dev/null +++ b/src/version/manifest.rs @@ -0,0 +1,119 @@ +use core::fmt; + +use chrono::{DateTime, Utc}; +use serde::{de::Visitor, Deserialize}; + +#[derive(Deserialize)] +pub struct LatestVersions { + release: String, + snapshot: String +} + +impl LatestVersions { + pub fn get_release(&self) -> &String { + &self.release + } + + pub fn get_snapshot(&self) -> &String { + &self.snapshot + } +} + +#[derive(Debug)] +pub enum VersionType { + Snapshot, + Release, + OldBeta, + OldAlpha, + Other(String) +} + +struct VersionTypeVisitor; + +impl<'de> Visitor<'de> for VersionTypeVisitor { + type Value = VersionType; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> fmt::Result { + formatter.write_str("a Minecraft release type") + } + + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, { + match v { + "snapshot" => Ok(VersionType::Snapshot), + "release" => Ok(VersionType::Release), + "old_beta" => Ok(VersionType::OldBeta), + "old_alpha" => Ok(VersionType::OldAlpha), + _ => Ok(VersionType::Other(v.to_owned())) + } + } +} + +impl<'de> Deserialize<'de> for VersionType { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de> { + deserializer.deserialize_string(VersionTypeVisitor) + } +} + +// https://piston-meta.mojang.com/mc/game/version_manifest_v2.json +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VersionManifestVersion { + id: String, + #[serde(rename = "type")] + version_type: VersionType, + url: String, + time: DateTime<Utc>, + release_time: DateTime<Utc>, + sha1: String, + compliance_level: u32 +} + +impl VersionManifestVersion { + pub fn get_id(&self) -> &String { + &self.id + } + + pub fn get_version_type(&self) -> &VersionType { + &self.version_type + } + + pub fn get_url(&self) -> &String { + &self.url + } + + pub fn get_time(&self) -> &DateTime<Utc> { + &self.time + } + + pub fn get_release_time(&self) -> &DateTime<Utc> { + &self.release_time + } + + pub fn get_sha1(&self) -> &String { + &self.sha1 + } + + pub fn get_compliance_level(&self) -> u32 { + self.compliance_level + } +} + +#[derive(Deserialize)] +pub struct VersionManifest { + latest: LatestVersions, + versions: Vec<VersionManifestVersion> +} + +impl VersionManifest { + pub fn get_latest(&self) -> &LatestVersions { + &self.latest + } + + pub fn get_versions(&self) -> &Vec<VersionManifestVersion> { + &self.versions + } +} |
