use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use vx_core::{Platform, VxResult};
use vx_installer::InstallConfig;
pub trait StandardToolConfig {
fn tool_name() -> &'static str;
fn create_install_config(version: &str, install_dir: PathBuf) -> InstallConfig;
fn get_install_methods() -> Vec<String>;
fn supports_auto_install() -> bool;
fn get_manual_instructions() -> String;
fn get_dependencies() -> Vec<ToolDependency>;
fn get_default_version() -> &'static str;
}
pub trait StandardUrlBuilder {
fn download_url(version: &str) -> Option<String>;
fn get_filename(version: &str) -> String;
fn get_platform_string() -> String;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDependency {
pub tool_name: String,
pub description: String,
pub required: bool,
pub version_requirement: Option<String>,
pub platforms: Vec<Platform>,
}
impl ToolDependency {
pub fn required(tool_name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
tool_name: tool_name.into(),
description: description.into(),
required: true,
version_requirement: None,
platforms: vec![],
}
}
pub fn optional(tool_name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
tool_name: tool_name.into(),
description: description.into(),
required: false,
version_requirement: None,
platforms: vec![],
}
}
pub fn with_version(mut self, requirement: impl Into<String>) -> Self {
self.version_requirement = Some(requirement.into());
self
}
pub fn for_platforms(mut self, platforms: Vec<Platform>) -> Self {
self.platforms = platforms;
self
}
}
pub trait ToolRuntime {
fn is_available(&self) -> impl std::future::Future<Output = VxResult<bool>> + Send;
fn get_version(&self) -> impl std::future::Future<Output = VxResult<Option<String>>> + Send;
fn get_path(&self) -> impl std::future::Future<Output = VxResult<Option<PathBuf>>> + Send;
fn execute(&self, args: &[String]) -> impl std::future::Future<Output = VxResult<i32>> + Send;
}
pub trait VersionParser {
fn parse_version(output: &str) -> Option<String>;
fn is_valid_version(version: &str) -> bool;
fn compare_versions(a: &str, b: &str) -> std::cmp::Ordering;
}
pub struct PlatformUrlBuilder;
impl PlatformUrlBuilder {
pub fn get_platform_string() -> String {
let platform = Platform::current();
platform.to_string()
}
pub fn get_archive_extension() -> &'static str {
if cfg!(windows) {
"zip"
} else {
"tar.gz"
}
}
pub fn get_exe_extension() -> &'static str {
if cfg!(windows) {
".exe"
} else {
""
}
}
}
pub struct UrlUtils;
impl UrlUtils {
pub fn github_release_url(owner: &str, repo: &str, version: &str, filename: &str) -> String {
format!(
"https://github.com/{}/{}/releases/download/{}/{}",
owner, repo, version, filename
)
}
pub fn official_download_url(base_url: &str, version: &str, filename: &str) -> String {
format!("{}/v{}/{}", base_url, version, filename)
}
}
pub struct VersionUtils;
impl VersionUtils {
pub fn is_latest(version: &str) -> bool {
version == "latest" || version == "stable"
}
pub fn normalize_version(version: &str) -> String {
version.strip_prefix('v').unwrap_or(version).to_string()
}
pub fn is_prerelease(version: &str) -> bool {
version.contains('-')
&& (version.contains("alpha")
|| version.contains("beta")
|| version.contains("rc")
|| version.contains("pre"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tool_dependency_creation() {
let dep = ToolDependency::required("node", "Node.js runtime").with_version(">=16.0.0");
assert_eq!(dep.tool_name, "node");
assert!(dep.required);
assert_eq!(dep.version_requirement, Some(">=16.0.0".to_string()));
}
#[test]
fn test_platform_url_builder() {
let platform = PlatformUrlBuilder::get_platform_string();
assert!(!platform.is_empty());
let ext = PlatformUrlBuilder::get_archive_extension();
assert!(ext == "zip" || ext == "tar.gz");
}
#[test]
fn test_url_utils() {
let url = UrlUtils::github_release_url("owner", "repo", "v1.0.0", "file.zip");
assert_eq!(
url,
"https://github.com/owner/repo/releases/download/v1.0.0/file.zip"
);
}
#[test]
fn test_version_utils() {
assert!(VersionUtils::is_latest("latest"));
assert!(VersionUtils::is_latest("stable"));
assert!(!VersionUtils::is_latest("1.0.0"));
assert_eq!(VersionUtils::normalize_version("v1.0.0"), "1.0.0");
assert_eq!(VersionUtils::normalize_version("1.0.0"), "1.0.0");
assert!(VersionUtils::is_prerelease("1.0.0-beta.1"));
assert!(!VersionUtils::is_prerelease("1.0.0"));
}
}