pub mod apis; pub mod cache; pub mod commands; pub mod config; pub mod db; pub mod error; pub mod files; use std::{fmt::Display, fs::{File, remove_file, self}, io::{Write, Read}, time::Duration}; pub use apis::*; use apis::modrinth::{get_game_versions, GameVersion, GameVersionType}; pub use commands::*; use error::{ErrorType, MLError, MLE}; use serde::{Deserialize, Serialize, de::Visitor}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Modloader { Fabric, Forge, } impl Modloader { pub fn from(string: &str) -> MLE { match string { "forge" => Ok(Modloader::Forge), "fabric" => Ok(Modloader::Fabric), _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")), } } } impl Display for Modloader { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Modloader::Fabric => write!(f, "fabric"), Modloader::Forge => write!(f, "forge"), } } } impl Serialize for Modloader { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { match self { Modloader::Fabric => serializer.serialize_str("fabric"), Modloader::Forge => serializer.serialize_str("forge"), } } } impl<'de> Deserialize<'de> for Modloader { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Modloader; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("`fabric`, `forge` or `quilt`") } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { match v { "fabric" => Ok(Modloader::Fabric), "forge" => Ok(Modloader::Forge), _ => Err(serde::de::Error::unknown_field(v, &["fabric", "forge", "quilt"])) } } } deserializer.deserialize_identifier(FieldVisitor) } } #[derive(Debug, Clone)] pub enum VersionLevel { Release, Snapshot, Version(String) } /// Checks if update needed (time) /// if yes: get versions, update pub async fn check_game_versions(path: &str, force: bool) -> MLE<()> { let creation_time = fs::metadata(path)?.created()?; if !force && creation_time.elapsed().unwrap() < Duration::from_secs(60 * 60 * 24) { return Ok(()); } print!("Update minecraft versions"); std::io::stdout().flush()?; let versions = get_game_versions().await; remove_file(path)?; let mut file = File::create(path)?; file.write_all(&serde_json::to_string_pretty(&versions)?.as_bytes())?; println!(" ✓"); Ok(()) } /// Loads game versions pub fn load_game_versions(path: &str) -> MLE> { let mut file = File::open(path)?; let mut data = String::new(); file.read_to_string(&mut data)?; let versions: Vec = serde_json::from_str(&data)?; Ok(versions) } impl VersionLevel { pub fn from(str: &str) -> Self { match str { "release" => VersionLevel::Release, "snapshot" => VersionLevel::Snapshot, _ => VersionLevel::Version(String::from(str)), } } pub fn get(self, versions_path: &str) -> MLE { let path = format!("{}/versions.json", versions_path); let mut versions = load_game_versions(&path)?.into_iter(); match self { VersionLevel::Release => { let release = versions.find(|ver| ver.version_type == GameVersionType::release).unwrap(); println!("{:?}", release); Ok(release.version) }, VersionLevel::Snapshot => { let snapshot = versions.find(|ver| ver.version_type == GameVersionType::snapshot).unwrap(); println!("{:?}", snapshot); Ok(snapshot.version) }, VersionLevel::Version(v) => { if versions.find(|ver| ver.version == v).is_some() { Ok(v) } else { Err(MLError::new(ErrorType::ConfigError, "unknown minecraft version")) } }, } } } impl Serialize for VersionLevel { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { match self { VersionLevel::Release => serializer.serialize_str("release"), VersionLevel::Snapshot => serializer.serialize_str("snapshot"), VersionLevel::Version(v) => serializer.serialize_str(v), } } } impl<'de> Deserialize<'de> for VersionLevel { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = VersionLevel; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("`fabric`, `forge` or `quilt`") } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { match v { "release" => Ok(VersionLevel::Release), "snapshot" => Ok(VersionLevel::Snapshot), _ => Ok(VersionLevel::Version(String::from(v))) } } } deserializer.deserialize_identifier(FieldVisitor) } }