Skip to main content

zoi_resolver/
mini_resolve.rs

1use anyhow::{Result, anyhow};
2use colored::*;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5pub use zoi_core::types::MiniVulnerability;
6
7fn default_revision() -> String {
8    "1".to_string()
9}
10
11/// A simplified version of package metadata used in Zoi Mini's remote index.
12///
13/// This index allows Zoi Mini to perform fast lookups and vulnerability checks
14/// without downloading individual `.pkg.lua` files or cloning entire registries.
15#[derive(Debug, Serialize, Deserialize, Clone)]
16pub struct MiniPackageIndex {
17    pub repo: String,
18    pub repo_type: String,
19    pub version: String,
20    #[serde(default = "default_revision")]
21    pub revision: String,
22    pub description: String,
23    #[serde(default, deserialize_with = "deserialize_sub_packages")]
24    pub sub_packages: Option<Vec<String>>,
25    pub vuln: Option<Vec<MiniVulnerability>>,
26}
27
28fn deserialize_sub_packages<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
29where
30    D: serde::Deserializer<'de>,
31{
32    let v: serde_json::Value = serde::Deserialize::deserialize(deserializer)?;
33    if v.is_array() {
34        Ok(serde_json::from_value(v).ok())
35    } else {
36        Ok(None)
37    }
38}
39
40#[derive(Debug, Serialize, Deserialize, Clone)]
41pub struct MiniRegistryIndex {
42    pub packages: HashMap<String, MiniPackageIndex>,
43}
44
45/// Fetches the optimized JSON index from the official Zoidberg registry.
46///
47/// This index is the backbone of Zoi Mini, providing a pre-resolved mapping
48/// of package names to their current versions and metadata.
49pub fn fetch_registry_index() -> Result<MiniRegistryIndex> {
50    let url = "https://gitlab.com/zillowe/zillwen/zusty/zoidberg/-/raw/main/packages.json";
51    let client = zoi_core::utils::get_http_client()?;
52    let response = client.get(url).send()?;
53    if !response.status().is_success() {
54        return Err(anyhow!(
55            "Failed to fetch packages.json from Zoidberg registry: {}",
56            response.status()
57        ));
58    }
59    let index: MiniRegistryIndex = response.json()?;
60    Ok(index)
61}
62
63pub fn fetch_registry_config() -> Result<zoi_core::types::RepoConfig> {
64    let url = "https://gitlab.com/zillowe/zillwen/zusty/zoidberg/-/raw/main/repo.yaml";
65    let client = zoi_core::utils::get_http_client()?;
66    let response = client.get(url).send()?;
67    if !response.status().is_success() {
68        return Err(anyhow!(
69            "Failed to fetch repo.yaml from Zoidberg registry: {}",
70            response.status()
71        ));
72    }
73    let content = response.text()?;
74    let config: zoi_core::types::RepoConfig = serde_yaml::from_str(&content)?;
75    Ok(config)
76}
77
78pub fn get_package_lua_url(repo: &str, name: &str) -> String {
79    format!(
80        "https://gitlab.com/zillowe/zillwen/zusty/zoidberg/-/raw/main/{}/{}/{}.pkg.lua",
81        repo, name, name
82    )
83}
84
85/// Scans the package metadata for known security advisories.
86///
87/// Returns `true` if the package is safe to install, or if the user
88/// explicitly chooses to bypass a security warning.
89pub fn check_vulnerabilities(
90    pkg_name: &str,
91    pkg_index: &MiniPackageIndex,
92    version: &str,
93) -> Result<bool> {
94    let Some(vulns) = &pkg_index.vuln else {
95        return Ok(true);
96    };
97
98    let target_version = semver::Version::parse(version.trim_start_matches('v'))
99        .map_err(|e| anyhow!("Failed to parse version {}: {}", version, e))?;
100
101    let mut affected = Vec::new();
102
103    for vuln in vulns {
104        if let Ok(req) = semver::VersionReq::parse(&vuln.affected_range)
105            && req.matches(&target_version)
106        {
107            affected.push(vuln);
108        }
109    }
110
111    if affected.is_empty() {
112        return Ok(true);
113    }
114
115    println!("\n{}", "SECURITY WARNING".red().bold());
116    for vuln in affected {
117        println!(
118            "Package {} v{} is known to be vulnerable:",
119            pkg_name.cyan().bold(),
120            version.red()
121        );
122        println!(
123            "[{}] {} (Severity: {})",
124            vuln.id.dimmed(),
125            vuln.summary,
126            vuln.severity.to_uppercase()
127        );
128        if let Some(fixed) = &vuln.fixed_in {
129            println!("Fixed in version: {}", fixed.green());
130        }
131        println!();
132    }
133
134    Ok(zoi_core::utils::ask_for_confirmation(
135        "Do you want to continue with the installation anyway?",
136        false,
137    ))
138}