Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ic1fda520473e53d1a584a3dda63ffda86a6a6964
771 lines
25 KiB
Rust
771 lines
25 KiB
Rust
use std::collections::{HashMap, HashSet};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use super::enums::{ProjectSide, ProjectType, ReleaseType, UpdateStrategy};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Project {
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub pakku_id: Option<String>,
|
|
#[serde(skip_serializing_if = "HashSet::is_empty", default)]
|
|
pub pakku_links: HashSet<String>,
|
|
#[serde(rename = "type")]
|
|
pub r#type: ProjectType,
|
|
#[serde(default = "default_side")]
|
|
pub side: ProjectSide,
|
|
pub slug: HashMap<String, String>,
|
|
pub name: HashMap<String, String>,
|
|
pub id: HashMap<String, String>,
|
|
#[serde(
|
|
default = "default_update_strategy",
|
|
skip_serializing_if = "is_default_update_strategy"
|
|
)]
|
|
pub update_strategy: UpdateStrategy,
|
|
#[serde(
|
|
default = "default_redistributable",
|
|
skip_serializing_if = "is_default_redistributable"
|
|
)]
|
|
pub redistributable: bool,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub subpath: Option<String>,
|
|
#[serde(default, skip_serializing_if = "HashSet::is_empty")]
|
|
pub aliases: HashSet<String>,
|
|
#[serde(
|
|
default = "default_export",
|
|
skip_serializing_if = "is_default_export"
|
|
)]
|
|
pub export: bool,
|
|
pub files: Vec<ProjectFile>,
|
|
}
|
|
|
|
const fn default_export() -> bool {
|
|
true
|
|
}
|
|
|
|
const fn default_side() -> ProjectSide {
|
|
ProjectSide::Both
|
|
}
|
|
|
|
const fn default_update_strategy() -> UpdateStrategy {
|
|
UpdateStrategy::Latest
|
|
}
|
|
|
|
const fn default_redistributable() -> bool {
|
|
true
|
|
}
|
|
|
|
const fn is_default_update_strategy(strategy: &UpdateStrategy) -> bool {
|
|
matches!(strategy, UpdateStrategy::Latest)
|
|
}
|
|
|
|
const fn is_default_redistributable(redistributable: &bool) -> bool {
|
|
*redistributable
|
|
}
|
|
|
|
const fn is_default_export(export: &bool) -> bool {
|
|
*export
|
|
}
|
|
|
|
impl Project {
|
|
pub fn new(pakku_id: String, typ: ProjectType, side: ProjectSide) -> Self {
|
|
Self {
|
|
pakku_id: Some(pakku_id),
|
|
pakku_links: HashSet::new(),
|
|
r#type: typ,
|
|
side,
|
|
slug: HashMap::new(),
|
|
name: HashMap::new(),
|
|
id: HashMap::new(),
|
|
update_strategy: UpdateStrategy::Latest,
|
|
redistributable: true,
|
|
subpath: None,
|
|
aliases: HashSet::new(),
|
|
export: true,
|
|
files: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn get_platform_id(&self, platform: &str) -> Option<&String> {
|
|
self.id.get(platform)
|
|
}
|
|
|
|
pub fn get_name(&self) -> String {
|
|
self.name.values().next().cloned().unwrap_or_else(|| {
|
|
self
|
|
.pakku_id
|
|
.clone()
|
|
.unwrap_or_else(|| "unknown".to_string())
|
|
})
|
|
}
|
|
|
|
pub fn matches_input(&self, input: &str) -> bool {
|
|
// Check pakku_id
|
|
if let Some(ref pakku_id) = self.pakku_id
|
|
&& pakku_id == input
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check slugs
|
|
if self.slug.values().any(|s| s == input) {
|
|
return true;
|
|
}
|
|
|
|
// Check names (case-insensitive)
|
|
if self.name.values().any(|n| n.eq_ignore_ascii_case(input)) {
|
|
return true;
|
|
}
|
|
|
|
// Check IDs
|
|
if self.id.values().any(|i| i == input) {
|
|
return true;
|
|
}
|
|
|
|
// Check aliases
|
|
if self.aliases.contains(input) {
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn add_platform(
|
|
&mut self,
|
|
platform: String,
|
|
id: String,
|
|
slug: String,
|
|
name: String,
|
|
) {
|
|
self.id.insert(platform.clone(), id);
|
|
self.slug.insert(platform.clone(), slug);
|
|
self.name.insert(platform, name);
|
|
}
|
|
|
|
pub fn merge(&mut self, other: Self) {
|
|
// Merge platform identifiers
|
|
for (platform, id) in other.id {
|
|
self.id.entry(platform.clone()).or_insert(id);
|
|
}
|
|
for (platform, slug) in other.slug {
|
|
self.slug.entry(platform.clone()).or_insert(slug);
|
|
}
|
|
for (platform, name) in other.name {
|
|
self.name.entry(platform).or_insert(name);
|
|
}
|
|
|
|
// Merge pakku links
|
|
self.pakku_links.extend(other.pakku_links);
|
|
|
|
// Merge files
|
|
for file in other.files {
|
|
if !self.files.iter().any(|f| f.id == file.id) {
|
|
self.files.push(file);
|
|
}
|
|
}
|
|
|
|
// Merge aliases
|
|
self.aliases.extend(other.aliases);
|
|
}
|
|
|
|
/// Check if versions match across all providers.
|
|
/// Returns true if all provider files have the same version/file,
|
|
/// or if there's only one provider.
|
|
pub fn versions_match_across_providers(&self) -> bool {
|
|
if self.files.len() <= 1 {
|
|
return true;
|
|
}
|
|
|
|
// Group files by provider (using parent_id as proxy)
|
|
let mut versions_by_provider: HashMap<String, Vec<&str>> = HashMap::new();
|
|
for file in &self.files {
|
|
// Extract provider from file type or use parent_id
|
|
let provider = &file.file_type;
|
|
versions_by_provider
|
|
.entry(provider.clone())
|
|
.or_default()
|
|
.push(&file.file_name);
|
|
}
|
|
|
|
// If only one provider, versions match
|
|
if versions_by_provider.len() <= 1 {
|
|
return true;
|
|
}
|
|
|
|
// Compare semantic versions extracted from file names
|
|
let parse_version = |name: &str| {
|
|
// Try to extract version from patterns like "mod-1.0.0.jar" or
|
|
// "mod_v1.0.0"
|
|
let version_str = name
|
|
.rsplit_once('-')
|
|
.and_then(|(_, v)| v.strip_suffix("jar"))
|
|
.or_else(|| {
|
|
name
|
|
.rsplit_once('_')
|
|
.and_then(|(_, v)| v.strip_suffix("jar"))
|
|
})
|
|
.unwrap_or(name);
|
|
semver::Version::parse(version_str).ok()
|
|
};
|
|
|
|
let versions: Vec<_> = versions_by_provider
|
|
.values()
|
|
.filter_map(|files| files.first().copied().and_then(parse_version))
|
|
.collect();
|
|
|
|
// All versions should be the same
|
|
versions.windows(2).all(|w| w[0] == w[1])
|
|
}
|
|
|
|
/// Check if versions do NOT match across providers.
|
|
/// Returns Some with details if there's a mismatch, None if versions match.
|
|
pub fn check_version_mismatch(&self) -> Option<String> {
|
|
if self.versions_match_across_providers() {
|
|
return None;
|
|
}
|
|
|
|
// Collect version info by provider
|
|
let mut provider_versions: Vec<(String, String)> = Vec::new();
|
|
for file in &self.files {
|
|
provider_versions.push((file.file_type.clone(), file.file_name.clone()));
|
|
}
|
|
|
|
Some(format!(
|
|
"Version mismatch for {}: {}",
|
|
self.get_name(),
|
|
provider_versions
|
|
.iter()
|
|
.map(|(p, v)| format!("{p}={v}"))
|
|
.collect::<Vec<_>>()
|
|
.join(", ")
|
|
))
|
|
}
|
|
|
|
pub fn select_file(
|
|
&mut self,
|
|
mc_versions: &[String],
|
|
loaders: &[String],
|
|
) -> crate::error::Result<()> {
|
|
// Filter compatible files
|
|
let compatible_files: Vec<_> = self
|
|
.files
|
|
.iter()
|
|
.filter(|f| f.is_compatible(mc_versions, loaders))
|
|
.collect();
|
|
|
|
if compatible_files.is_empty() {
|
|
return Err(crate::error::PakkerError::FileSelectionError(format!(
|
|
"No compatible files found for {}",
|
|
self.get_name()
|
|
)));
|
|
}
|
|
|
|
// Sort by release type (release > beta > alpha) and date
|
|
let mut sorted_files = compatible_files.clone();
|
|
sorted_files.sort_by(|a, b| {
|
|
use super::enums::ReleaseType;
|
|
let type_order = |rt: &ReleaseType| {
|
|
match rt {
|
|
ReleaseType::Release => 0,
|
|
ReleaseType::Beta => 1,
|
|
ReleaseType::Alpha => 2,
|
|
}
|
|
};
|
|
|
|
type_order(&a.release_type)
|
|
.cmp(&type_order(&b.release_type))
|
|
.then_with(|| b.date_published.cmp(&a.date_published))
|
|
});
|
|
|
|
// Keep only the best file
|
|
if let Some(best_file) = sorted_files.first() {
|
|
self.files = vec![(*best_file).clone()];
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ProjectFile {
|
|
#[serde(rename = "type")]
|
|
pub file_type: String,
|
|
pub file_name: String,
|
|
pub mc_versions: Vec<String>,
|
|
#[serde(default)]
|
|
pub loaders: Vec<String>,
|
|
pub release_type: ReleaseType,
|
|
pub url: String,
|
|
pub id: String,
|
|
pub parent_id: String,
|
|
pub hashes: HashMap<String, String>,
|
|
pub required_dependencies: Vec<String>,
|
|
pub size: u64,
|
|
pub date_published: String,
|
|
}
|
|
|
|
impl ProjectFile {
|
|
pub fn is_compatible(
|
|
&self,
|
|
mc_versions: &[String],
|
|
loaders: &[String],
|
|
) -> bool {
|
|
const VALID_LOADERS: &[&str] =
|
|
&["minecraft", "iris", "optifine", "datapack"];
|
|
|
|
let mc_compatible =
|
|
self.mc_versions.iter().any(|v| mc_versions.contains(v));
|
|
|
|
// Accept files with empty loaders, OR loaders matching request, OR valid
|
|
// special loaders
|
|
let loader_compatible = self.loaders.is_empty()
|
|
|| self.loaders.iter().any(|l| loaders.contains(l))
|
|
|| self
|
|
.loaders
|
|
.iter()
|
|
.any(|l| VALID_LOADERS.contains(&l.as_str()));
|
|
|
|
mc_compatible && loader_compatible
|
|
}
|
|
|
|
/// Generate a viewable URL for this file based on its provider.
|
|
/// Returns None if the URL cannot be determined.
|
|
pub fn get_site_url(&self, project: &Project) -> Option<String> {
|
|
// Determine provider from file type
|
|
match self.file_type.as_str() {
|
|
"modrinth" => {
|
|
// Format: https://modrinth.com/mod/{slug}/version/{file_id}
|
|
let slug = project.slug.get("modrinth")?;
|
|
Some(format!(
|
|
"https://modrinth.com/mod/{}/version/{}",
|
|
slug, self.id
|
|
))
|
|
},
|
|
"curseforge" => {
|
|
// Format: https://www.curseforge.com/minecraft/mc-mods/{slug}/files/{file_id}
|
|
let slug = project.slug.get("curseforge")?;
|
|
Some(format!(
|
|
"https://www.curseforge.com/minecraft/mc-mods/{}/files/{}",
|
|
slug, self.id
|
|
))
|
|
},
|
|
"github" => {
|
|
// Format: https://github.com/{owner}/{repo}/releases/tag/{tag}
|
|
// parent_id contains owner/repo, id contains the tag/version
|
|
Some(format!(
|
|
"https://github.com/{}/releases/tag/{}",
|
|
self.parent_id, self.id
|
|
))
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_project_new() {
|
|
let project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
assert_eq!(project.pakku_id, Some("test-id".to_string()));
|
|
assert_eq!(project.r#type, ProjectType::Mod);
|
|
assert_eq!(project.side, ProjectSide::Both);
|
|
assert!(project.pakku_links.is_empty());
|
|
assert!(project.files.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_serialization() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
project
|
|
.slug
|
|
.insert("modrinth".to_string(), "test-slug".to_string());
|
|
project
|
|
.name
|
|
.insert("modrinth".to_string(), "Test Mod".to_string());
|
|
project
|
|
.id
|
|
.insert("modrinth".to_string(), "abc123".to_string());
|
|
|
|
let json = serde_json::to_string(&project).unwrap();
|
|
let deserialized: Project = serde_json::from_str(&json).unwrap();
|
|
|
|
assert_eq!(deserialized.pakku_id, project.pakku_id);
|
|
assert_eq!(deserialized.r#type, project.r#type);
|
|
assert_eq!(deserialized.side, project.side);
|
|
assert_eq!(
|
|
deserialized.slug.get("modrinth"),
|
|
Some(&"test-slug".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_file_is_compatible_with_empty_loaders() {
|
|
let file = ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "test.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec![], // Empty loaders should be accepted
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/test.jar".to_string(),
|
|
id: "file123".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let lockfile_mc = vec!["1.20.1".to_string()];
|
|
let lockfile_loaders = vec!["fabric".to_string()];
|
|
|
|
assert!(file.is_compatible(&lockfile_mc, &lockfile_loaders));
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_file_is_compatible_with_matching_loaders() {
|
|
let file = ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "test.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/test.jar".to_string(),
|
|
id: "file123".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let lockfile_mc = vec!["1.20.1".to_string()];
|
|
let lockfile_loaders = vec!["fabric".to_string()];
|
|
|
|
assert!(file.is_compatible(&lockfile_mc, &lockfile_loaders));
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_file_is_compatible_with_valid_loaders() {
|
|
for loader in ["minecraft", "iris", "optifine", "datapack"] {
|
|
let file = ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "test.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec![loader.to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/test.jar".to_string(),
|
|
id: "file123".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let lockfile_mc = vec!["1.20.1".to_string()];
|
|
let lockfile_loaders = vec!["fabric".to_string()];
|
|
|
|
assert!(
|
|
file.is_compatible(&lockfile_mc, &lockfile_loaders),
|
|
"Failed for valid loader: {}",
|
|
loader
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_file_incompatible() {
|
|
let file = ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "test.jar".to_string(),
|
|
mc_versions: vec!["1.19.4".to_string()],
|
|
loaders: vec!["forge".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/test.jar".to_string(),
|
|
id: "file123".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let lockfile_mc = vec!["1.20.1".to_string()];
|
|
let lockfile_loaders = vec!["fabric".to_string()];
|
|
|
|
assert!(!file.is_compatible(&lockfile_mc, &lockfile_loaders));
|
|
}
|
|
|
|
#[test]
|
|
fn test_project_select_file() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
project.files.push(ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "alpha.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Alpha,
|
|
url: "https://example.com/alpha.jar".to_string(),
|
|
id: "file1".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-03T00:00:00Z".to_string(),
|
|
});
|
|
|
|
project.files.push(ProjectFile {
|
|
file_type: "mod".to_string(),
|
|
file_name: "release.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/release.jar".to_string(),
|
|
id: "file2".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
let lockfile_mc = vec!["1.20.1".to_string()];
|
|
let lockfile_loaders = vec!["fabric".to_string()];
|
|
|
|
let result = project.select_file(&lockfile_mc, &lockfile_loaders);
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_versions_match_across_providers_single_file() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
project.files.push(ProjectFile {
|
|
file_type: "modrinth".to_string(),
|
|
file_name: "test-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/test.jar".to_string(),
|
|
id: "file1".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
assert!(project.versions_match_across_providers());
|
|
assert!(project.check_version_mismatch().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_versions_match_across_providers_same_file() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
// Same file name from different providers
|
|
project.files.push(ProjectFile {
|
|
file_type: "modrinth".to_string(),
|
|
file_name: "test-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://modrinth.com/test.jar".to_string(),
|
|
id: "mr-file1".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
project.files.push(ProjectFile {
|
|
file_type: "curseforge".to_string(),
|
|
file_name: "test-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://curseforge.com/test.jar".to_string(),
|
|
id: "cf-file1".to_string(),
|
|
parent_id: "mod456".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
assert!(project.versions_match_across_providers());
|
|
}
|
|
|
|
#[test]
|
|
fn test_versions_mismatch_across_providers() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
project
|
|
.name
|
|
.insert("test".to_string(), "Test Mod".to_string());
|
|
|
|
// Different file names from different providers
|
|
project.files.push(ProjectFile {
|
|
file_type: "modrinth".to_string(),
|
|
file_name: "test-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://modrinth.com/test.jar".to_string(),
|
|
id: "mr-file1".to_string(),
|
|
parent_id: "mod123".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
project.files.push(ProjectFile {
|
|
file_type: "curseforge".to_string(),
|
|
file_name: "test-0.9.0.jar".to_string(), // Different version
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://curseforge.com/test.jar".to_string(),
|
|
id: "cf-file1".to_string(),
|
|
parent_id: "mod456".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
});
|
|
|
|
assert!(!project.versions_match_across_providers());
|
|
let mismatch = project.check_version_mismatch();
|
|
assert!(mismatch.is_some());
|
|
let msg = mismatch.unwrap();
|
|
assert!(msg.contains("Version mismatch"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_site_url_modrinth() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
project
|
|
.slug
|
|
.insert("modrinth".to_string(), "sodium".to_string());
|
|
|
|
let file = ProjectFile {
|
|
file_type: "modrinth".to_string(),
|
|
file_name: "sodium-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://modrinth.com/sodium.jar".to_string(),
|
|
id: "abc123".to_string(),
|
|
parent_id: "sodium".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let url = file.get_site_url(&project);
|
|
assert!(url.is_some());
|
|
let url = url.unwrap();
|
|
assert!(url.contains("modrinth.com"));
|
|
assert!(url.contains("sodium"));
|
|
assert!(url.contains("abc123"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_site_url_curseforge() {
|
|
let mut project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
project
|
|
.slug
|
|
.insert("curseforge".to_string(), "jei".to_string());
|
|
|
|
let file = ProjectFile {
|
|
file_type: "curseforge".to_string(),
|
|
file_name: "jei-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["forge".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://curseforge.com/jei.jar".to_string(),
|
|
id: "12345".to_string(),
|
|
parent_id: "jei".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let url = file.get_site_url(&project);
|
|
assert!(url.is_some());
|
|
let url = url.unwrap();
|
|
assert!(url.contains("curseforge.com"));
|
|
assert!(url.contains("jei"));
|
|
assert!(url.contains("12345"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_site_url_github() {
|
|
let project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
let file = ProjectFile {
|
|
file_type: "github".to_string(),
|
|
file_name: "mod-1.0.0.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url:
|
|
"https://github.com/owner/repo/releases/download/v1.0.0/mod.jar"
|
|
.to_string(),
|
|
id: "v1.0.0".to_string(),
|
|
parent_id: "owner/repo".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let url = file.get_site_url(&project);
|
|
assert!(url.is_some());
|
|
let url = url.unwrap();
|
|
assert!(url.contains("github.com"));
|
|
assert!(url.contains("owner/repo"));
|
|
assert!(url.contains("v1.0.0"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_site_url_unknown_type() {
|
|
let project =
|
|
Project::new("test-id".to_string(), ProjectType::Mod, ProjectSide::Both);
|
|
|
|
let file = ProjectFile {
|
|
file_type: "unknown".to_string(),
|
|
file_name: "mod.jar".to_string(),
|
|
mc_versions: vec!["1.20.1".to_string()],
|
|
loaders: vec!["fabric".to_string()],
|
|
release_type: ReleaseType::Release,
|
|
url: "https://example.com/mod.jar".to_string(),
|
|
id: "123".to_string(),
|
|
parent_id: "mod".to_string(),
|
|
hashes: HashMap::new(),
|
|
required_dependencies: vec![],
|
|
size: 1024,
|
|
date_published: "2024-01-01T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let url = file.get_site_url(&project);
|
|
assert!(url.is_none());
|
|
}
|
|
}
|