328 lines
12 KiB
Rust
328 lines
12 KiB
Rust
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
|
|
|
// Standard library imports
|
|
use std::path::Path;
|
|
use std::env;
|
|
|
|
// Serde for serialization/deserialization
|
|
|
|
// Tauri related imports
|
|
|
|
// Internal modules
|
|
pub mod models;
|
|
pub mod services;
|
|
pub mod commands;
|
|
pub mod crawlers;
|
|
pub mod platform_matcher;
|
|
|
|
// Import our models
|
|
pub use models::server::{ServerType, ServerInfo, ScanResult, ScanProgress};
|
|
pub use models::plugin::{Plugin, PluginMeta};
|
|
pub use models::repository::{RepositorySource, RepositoryPlugin, PotentialPluginMatch};
|
|
|
|
// Import our services
|
|
pub use services::http::HttpClient;
|
|
pub use services::plugin_scanner::{scan_server_directory, perform_scan, extract_plugin_metadata, calculate_file_hash, is_file_locked};
|
|
pub use services::update_manager::{check_for_plugin_updates, check_single_plugin_update, backup_plugin, replace_plugin, normalize_version, compare_plugin_versions};
|
|
|
|
// Import our commands
|
|
pub use commands::plugin_commands::*;
|
|
pub use commands::scan_commands::*;
|
|
|
|
// Import our crawlers
|
|
pub use crawlers::HangarCrawler;
|
|
pub use crawlers::SpigotMCCrawler;
|
|
pub use crawlers::ModrinthCrawler;
|
|
pub use crawlers::GitHubCrawler;
|
|
pub use crawlers::Repository;
|
|
|
|
// Import platform matchers
|
|
pub use platform_matcher::{get_compatible_modrinth_loaders, is_version_compatible_with_server};
|
|
|
|
use futures::future::{BoxFuture, FutureExt}; // Import necessary future types
|
|
|
|
/// Search for plugins in repositories
|
|
pub async fn lib_search_plugins_in_repositories(
|
|
query: &str,
|
|
repositories: Vec<RepositorySource>
|
|
) -> Result<Vec<RepositoryPlugin>, String> {
|
|
// Check for empty query
|
|
if query.is_empty() {
|
|
return Err("Search query cannot be empty".to_string());
|
|
}
|
|
|
|
// Check for empty repository list
|
|
if repositories.is_empty() {
|
|
return Err("No repositories specified for search".to_string());
|
|
}
|
|
|
|
// Create a list to store tasks (boxed futures)
|
|
let mut tasks: Vec<BoxFuture<Result<(String, Vec<RepositoryPlugin>), (String, String)>>> = Vec::new();
|
|
|
|
// Add tasks for each repository
|
|
for repo in repositories {
|
|
match repo {
|
|
RepositorySource::HangarMC => {
|
|
let crawler = crawlers::HangarCrawler::new();
|
|
let query_owned = query.to_string();
|
|
let task = async move {
|
|
match crawler.search(&query_owned).await {
|
|
Ok(repo_results) => Ok((crawler.get_repository_name(), repo_results)),
|
|
Err(e) => Err((crawler.get_repository_name(), e.to_string()))
|
|
}
|
|
}.boxed(); // Box the future
|
|
tasks.push(task);
|
|
},
|
|
RepositorySource::SpigotMC => {
|
|
let crawler = crawlers::SpigotMCCrawler::new();
|
|
let query_owned = query.to_string();
|
|
let task = async move {
|
|
match crawler.search(&query_owned).await {
|
|
Ok(repo_results) => Ok((crawler.get_repository_name(), repo_results)),
|
|
Err(e) => Err((crawler.get_repository_name(), e.to_string()))
|
|
}
|
|
}.boxed(); // Box the future
|
|
tasks.push(task);
|
|
},
|
|
RepositorySource::Modrinth => {
|
|
let crawler = crawlers::ModrinthCrawler::new();
|
|
let query_owned = query.to_string();
|
|
let task = async move {
|
|
match crawler.search(&query_owned).await {
|
|
Ok(repo_results) => Ok((crawler.get_repository_name(), repo_results)),
|
|
Err(e) => Err((crawler.get_repository_name(), e.to_string()))
|
|
}
|
|
}.boxed(); // Box the future
|
|
tasks.push(task);
|
|
},
|
|
RepositorySource::GitHub => {
|
|
let crawler = crawlers::GitHubCrawler::new();
|
|
let query_owned = query.to_string();
|
|
let task = async move {
|
|
match crawler.search(&query_owned).await {
|
|
Ok(repo_results) => Ok((crawler.get_repository_name(), repo_results)),
|
|
Err(e) => Err((crawler.get_repository_name(), e.to_string()))
|
|
}
|
|
}.boxed(); // Box the future
|
|
tasks.push(task);
|
|
},
|
|
_ => {
|
|
eprintln!("Repository {:?} not supported for search", repo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Execute all tasks concurrently
|
|
let results = futures::future::join_all(tasks).await;
|
|
|
|
// Collect successful results
|
|
let mut plugins = Vec::new();
|
|
let mut errors = Vec::new();
|
|
|
|
for result in results {
|
|
match result {
|
|
Ok((_, repo_plugins)) => {
|
|
plugins.extend(repo_plugins);
|
|
},
|
|
Err((repo, error)) => {
|
|
errors.push(format!("[{}] {}", repo, error));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if we found any results
|
|
if plugins.is_empty() {
|
|
if !errors.is_empty() {
|
|
return Err(errors.join("; "));
|
|
} else {
|
|
return Ok(Vec::new());
|
|
}
|
|
}
|
|
|
|
Ok(plugins)
|
|
}
|
|
|
|
/// Generate search variations for a plugin name
|
|
fn generate_search_variations(plugin_name: &str) -> Vec<String> {
|
|
let mut variations = Vec::new();
|
|
|
|
// Add original name
|
|
variations.push(plugin_name.to_string());
|
|
|
|
// Convert to lowercase
|
|
let name_lower = plugin_name.to_lowercase();
|
|
if name_lower != plugin_name {
|
|
variations.push(name_lower.clone());
|
|
}
|
|
|
|
// Add variations with common prefixes/suffixes removed
|
|
let prefixes = ["plugin", "mc", "minecraft"];
|
|
let suffixes = ["plugin", "spigot", "bukkit", "paper", "mc"];
|
|
|
|
for prefix in prefixes.iter() {
|
|
let prefix_str = format!("{} ", prefix);
|
|
if name_lower.starts_with(&prefix_str) {
|
|
variations.push(name_lower[prefix_str.len()..].to_string());
|
|
}
|
|
}
|
|
|
|
for suffix in suffixes.iter() {
|
|
let suffix_str = format!(" {}", suffix);
|
|
if name_lower.ends_with(&suffix_str) {
|
|
variations.push(name_lower[0..name_lower.len() - suffix_str.len()].to_string());
|
|
}
|
|
}
|
|
|
|
// Remove duplicates
|
|
variations.sort();
|
|
variations.dedup();
|
|
|
|
variations
|
|
}
|
|
|
|
/// Search for plugin variations
|
|
pub async fn search_with_variations(plugin_name: &str, repositories: &[RepositorySource]) -> Result<Vec<RepositoryPlugin>, String> {
|
|
let variations = generate_search_variations(plugin_name);
|
|
let mut all_results = Vec::new();
|
|
|
|
for variation in variations {
|
|
match lib_search_plugins_in_repositories(&variation, repositories.to_vec()).await {
|
|
Ok(results) => {
|
|
all_results.extend(results);
|
|
},
|
|
Err(e) => {
|
|
println!("Error searching for variation '{}': {}", variation, e);
|
|
// Continue with other variations even if this one fails
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove duplicates by plugin ID and repository
|
|
all_results.sort_by(|a, b| {
|
|
let a_key = format!("{:?}:{}", a.repository, a.id);
|
|
let b_key = format!("{:?}:{}", b.repository, b.id);
|
|
a_key.cmp(&b_key)
|
|
});
|
|
|
|
all_results.dedup_by(|a, b| {
|
|
a.id == b.id && a.repository == b.repository
|
|
});
|
|
|
|
Ok(all_results)
|
|
}
|
|
|
|
/// Get plugin details from a repository
|
|
pub async fn lib_get_plugin_details_from_repository(
|
|
plugin_id: &str,
|
|
repository: RepositorySource,
|
|
server_type: Option<&ServerType>
|
|
) -> Result<RepositoryPlugin, String> {
|
|
match repository {
|
|
RepositorySource::HangarMC => {
|
|
let crawler = crawlers::HangarCrawler::new();
|
|
crawler.get_plugin_details(plugin_id).await
|
|
.map_err(|e| format!("Failed to get plugin details from HangarMC: {}", e))
|
|
},
|
|
RepositorySource::SpigotMC => {
|
|
let crawler = crawlers::SpigotMCCrawler::new();
|
|
crawler.get_plugin_details(plugin_id).await
|
|
.map_err(|e| format!("Failed to get plugin details from SpigotMC: {}", e))
|
|
},
|
|
RepositorySource::Modrinth => {
|
|
let crawler = crawlers::ModrinthCrawler::new();
|
|
|
|
// Use server type aware version if provided
|
|
if let Some(server_type) = server_type {
|
|
crawler.get_plugin_details_with_server_type(plugin_id, Some(server_type)).await
|
|
.map_err(|e| format!("Failed to get plugin details from Modrinth: {}", e))
|
|
} else {
|
|
crawler.get_plugin_details(plugin_id).await
|
|
.map_err(|e| format!("Failed to get plugin details from Modrinth: {}", e))
|
|
}
|
|
},
|
|
RepositorySource::GitHub => {
|
|
let crawler = crawlers::GitHubCrawler::new();
|
|
crawler.get_plugin_details(plugin_id).await
|
|
.map_err(|e| format!("Failed to get plugin details from GitHub: {}", e))
|
|
},
|
|
_ => Err(format!("Repository source {:?} not supported for plugin details", repository))
|
|
}
|
|
}
|
|
|
|
/// Download a plugin from a repository
|
|
pub async fn lib_download_plugin_from_repository(
|
|
plugin_id: &str,
|
|
version: &str,
|
|
repository: RepositorySource,
|
|
destination: &str,
|
|
server_type: Option<&ServerType>
|
|
) -> Result<String, String> {
|
|
match repository {
|
|
RepositorySource::HangarMC => {
|
|
let crawler = crawlers::HangarCrawler::new();
|
|
crawler.download_plugin(plugin_id, version, Path::new(destination)).await
|
|
.map_err(|e| format!("Failed to download plugin from HangarMC: {}", e))
|
|
},
|
|
RepositorySource::SpigotMC => {
|
|
let crawler = crawlers::SpigotMCCrawler::new();
|
|
crawler.download_plugin(plugin_id, version, Path::new(destination)).await
|
|
.map_err(|e| format!("Failed to download plugin from SpigotMC: {}", e))
|
|
},
|
|
RepositorySource::Modrinth => {
|
|
let crawler = crawlers::ModrinthCrawler::new();
|
|
|
|
// Use server type aware version if provided
|
|
if let Some(server_type) = server_type {
|
|
crawler.download_plugin_with_server_type(plugin_id, version, Path::new(destination), Some(server_type)).await
|
|
.map_err(|e| format!("Failed to download plugin from Modrinth: {}", e))
|
|
} else {
|
|
crawler.download_plugin(plugin_id, version, Path::new(destination)).await
|
|
.map_err(|e| format!("Failed to download plugin from Modrinth: {}", e))
|
|
}
|
|
},
|
|
RepositorySource::GitHub => {
|
|
let crawler = crawlers::GitHubCrawler::new();
|
|
crawler.download_plugin(plugin_id, version, Path::new(destination)).await
|
|
.map_err(|e| format!("Failed to download plugin from GitHub: {}", e))
|
|
},
|
|
_ => Err(format!("Repository source {:?} not supported for downloads", repository))
|
|
}
|
|
}
|
|
|
|
/// Configure and run the Tauri application
|
|
pub fn run() {
|
|
// Build the Tauri application
|
|
tauri::Builder::default()
|
|
.plugin(tauri_plugin_dialog::init())
|
|
.invoke_handler(tauri::generate_handler![
|
|
// Plugin discovery commands
|
|
scan_server_dir,
|
|
scan_server_dir_sync,
|
|
|
|
// Plugin repository commands
|
|
search_plugins,
|
|
get_plugin_details,
|
|
|
|
// Update commands
|
|
update_plugin,
|
|
check_plugin_updates,
|
|
check_single_plugin_update_command,
|
|
backup_plugin_command,
|
|
|
|
// Plugin management commands
|
|
download_plugin,
|
|
set_plugin_repository,
|
|
get_plugin_versions,
|
|
load_plugin_data,
|
|
save_plugin_data,
|
|
|
|
// Utility commands
|
|
get_potential_plugin_matches,
|
|
compare_versions,
|
|
is_plugin_compatible,
|
|
greet
|
|
])
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running tauri application");
|
|
}
|