mod better_path;
mod download;
pub mod maven_coord;
use std::ffi::{OsStr, OsString};
use anyhow::{anyhow, Result};
use maven_coord::ArtifactCoordinate;
#[cfg(feature="mod_loaders")]
use serde::de::Visitor;
#[cfg(feature="mod_loaders")]
use serde::Deserializer;
#[cfg(feature="mod_loaders")]
use versions::{Requirement, Versioning};
#[cfg(feature="mod_loaders")]
use crate::components::mods::VersionBound;
use crate::minecraft::schemas::{Arguments, EnvRule, OSType, VersionJSON};
pub use self::better_path::BetterPath;
pub use self::download::{download, download_res, download_txt, download_all, check_hash, download_to_writer, DownloadAllMessage};
#[cfg(not(target_os="windows"))]
pub const PATH_DELIMITER: &str = ":";
#[cfg(target_os="windows")]
pub const PATH_DELIMITER: &str = ";";
#[cfg(target_os="windows")]
pub fn get_os() -> OSType {
OSType::Windows
}
#[cfg(target_os="linux")]
pub fn get_os() -> OSType {
OSType::Linux
}
#[cfg(target_os="macos")]
pub fn get_os() -> OSType {
OSType::OSX
}
#[cfg(target_pointer_width = "64")]
pub fn get_bits() -> String {
"64".to_string()
}
#[cfg(target_pointer_width = "32")]
pub fn get_bits() -> String {
"32".to_string()
}
pub fn check_rule(rule: &EnvRule) -> bool {
if let Some(os) = &rule.os {
if let Some(os) = &os.name {
return get_os() == *os;
}
}
rule.features.is_none() }
pub fn check_rules_no_option(rules: &Vec<EnvRule>) -> bool {
for i in rules {
if !check_rule(i) {
return false;
}
}
true
}
pub fn check_rules(rules: &Option<Vec<EnvRule>>) -> bool {
if rules.is_none() {
return true;
}
for i in rules.as_ref().unwrap() {
if check_rule(i) {
return true;
}
}
false
}
pub fn osstr_concat<A: Clone, B: Clone + AsRef<OsStr>>(a: &A, b: &B) -> OsString
where OsString: From<A> {
let mut str = OsString::from(a.clone());
str.push(b.clone());
str
}
pub fn merge_version_json(a: &VersionJSON, b: &VersionJSON) -> Result<VersionJSON> {
let mut c = a.get_base().clone();
c.libraries = b.get_base().libraries.clone().into_iter().chain(c.libraries.into_iter()).collect();
c.main_class = b.get_base().main_class.clone();
match a {
VersionJSON::Old { base: _, minecraft_arguments: _ } => {
if let VersionJSON::Old {base: _, minecraft_arguments} = b {
return Ok(VersionJSON::Old { base: c, minecraft_arguments: minecraft_arguments.to_string() });
}
return Result::Err(anyhow!("try to merge different kinds of version json!").into()); },
VersionJSON::New { base: _, arguments: arguments_a } => {
if let VersionJSON::New {base: _, arguments: arguments_b} = b {
let mut new_game = arguments_a.game.clone();
if new_game.is_none() {
new_game = arguments_b.game.clone();
} else if let Some(b_game) = arguments_b.game.clone() {
new_game.as_mut().unwrap().extend(b_game);
}
let mut new_jvm = arguments_a.jvm.clone();
if new_jvm.is_none() {
new_jvm = arguments_b.jvm.clone();
} else if let Some(b_jvm) = arguments_b.jvm.clone() {
new_jvm.as_mut().unwrap().extend(b_jvm);
}
return Ok(VersionJSON::New { base: c, arguments: Arguments { game: new_game, jvm: new_jvm } });
}
return Result::Err(anyhow!("try to merge different kinds of version json!").into()); },
}
}
pub fn expand_maven_id(id: &str) -> String {
ArtifactCoordinate::from(id).to_path()
}
#[cfg(feature="mod_loaders")]
pub fn parse_maven_version_range(v: &str) -> anyhow::Result<Vec<VersionBound>> {
let error = anyhow!(format!("{v} isn't a vaild Maven version range!")); if !v.contains(",") {
if v.starts_with("[") && v.ends_with("]") {
let version = &v[1..v.len() - 1];
return Ok(vec![VersionBound::new_one(Requirement {
op: versions::Op::Exact,
version: Some(Versioning::parse(version).map_err(|_|error)?.1)
})]);
}
return Ok(vec![VersionBound::new_one(Requirement {
op: versions::Op::GreaterEq,
version: Some(Versioning::parse(v).map_err(|_|error)?.1)
})]);
}
let splited = v.split(",");
let mut ret = vec![];
for [lower, upper] in splited.array_chunks() {
let mut bounds = vec![];
if lower != "(" && lower.starts_with("(") {
bounds.push(Requirement {
op: versions::Op::Greater,
version: Versioning::new(&lower[1..])
});
} else if lower.starts_with("[") {
bounds.push(Requirement {
op: versions::Op::GreaterEq,
version: Versioning::new(&lower[1..])
});
} else if lower != "(" {
return Err(error);
}
if upper != ")" && lower.ends_with(")") {
bounds.push(Requirement {
op: versions::Op::Greater,
version: Versioning::new(&upper[..upper.len() - 1])
});
}
if lower.ends_with("]") {
bounds.push(Requirement {
op: versions::Op::GreaterEq,
version: Versioning::new(&upper[..upper.len() - 1])
});
} else if upper != ")" {
return Err(error.into());
}
ret.push(VersionBound(bounds))
}
Ok(ret)
}
#[cfg(feature="mod_loaders")]
pub fn deserialize_maven_version_range<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<VersionBound>, D::Error> {
struct StructVisitor;
impl <'de> Visitor<'de> for StructVisitor {
type Value = Vec<VersionBound>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("A Maven Version Range")
}
fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error {
parse_maven_version_range(v).map_err(|_|serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &"A Maven Version Range"))
}
}
deserializer.deserialize_str(StructVisitor)
}
pub fn json_newline_transform(source: &String) -> String {
let mut result = vec![];
let mut is_str = 0;
for i in source.chars() {
match i {
'"' => {
if is_str == 1 {
is_str = 0;
}
else if is_str == 0 {
is_str = 1;
}
result.push(i);
},
'\'' => {
if is_str == 2 {
is_str = 0;
}
else if is_str == 0 {
is_str = 2;
}
result.push(i);
}
'\n' => {
if is_str != 0 {
result.push('\\');
result.push('n');
}
}
_ => {
result.push(i)
}
}
}
return result.into_iter().collect();
}