zoi-lua 1.22.1

Advanced Package Manager & Environment Orchestrator
Documentation
use mlua::{self, Lua, LuaSerdeExt, Table, Value};
use zoi_core::utils;

use serde::Deserialize;
pub fn add_fetch_util(lua: &Lua) -> Result<(), mlua::Error> {
    let fetch_table = lua.create_table()?;

    let fetch_fn = lua.create_function(|_, url: String| -> Result<String, mlua::Error> {
        let client =
            utils::get_http_client().map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
        let response = client
            .get(url)
            .send()
            .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
        let text = response
            .text()
            .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
        Ok(text)
    })?;
    fetch_table.set("url", fetch_fn)?;

    let utils_table: Table = lua.globals().get("UTILS")?;
    utils_table.set("FETCH", fetch_table)?;

    Ok(())
}

#[derive(Deserialize)]
struct GitArgs {
    repo: String,
    domain: Option<String>,
    branch: Option<String>,
}

fn fetch_json(url: &str) -> Result<serde_json::Value, mlua::Error> {
    let client = utils::get_http_client().map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;

    let response = client
        .get(url)
        .send()
        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;

    if !response.status().is_success() {
        return Err(mlua::Error::RuntimeError(format!(
            "Request to {} failed with status: {} and body: {}",
            url,
            response.status(),
            response.text().unwrap_or_else(|_| "N/A".to_string())
        )));
    }

    let text = response
        .text()
        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
    serde_json::from_str(&text).map_err(|e| mlua::Error::RuntimeError(e.to_string()))
}

pub fn add_git_fetch_util(lua: &Lua) -> Result<(), mlua::Error> {
    let utils_table: Table = lua.globals().get("UTILS")?;
    let fetch_table: Table = utils_table.get("FETCH")?;

    for provider in ["GITHUB", "GITLAB", "GITEA", "FORGEJO"] {
        let provider_table = lua.create_table()?;
        let latest_table = lua.create_table()?;

        for what in ["tag", "release", "commit"] {
            let get_latest_fn = lua.create_function(move |lua, args: Table| {
                let git_args: GitArgs = lua
                    .from_value(Value::Table(args))
                    .map_err(|e| mlua::Error::RuntimeError(format!("Invalid arguments: {}", e)))?;

                let base_url = match provider {
                    "GITHUB" => git_args
                        .domain
                        .unwrap_or_else(|| "https://api.github.com".to_string()),
                    "GITLAB" => git_args
                        .domain
                        .unwrap_or_else(|| "https://gitlab.com".to_string()),
                    "GITEA" => git_args
                        .domain
                        .unwrap_or_else(|| "https://gitea.com".to_string()),
                    "FORGEJO" => git_args
                        .domain
                        .unwrap_or_else(|| "https://codeberg.org".to_string()),
                    _ => unreachable!(),
                };

                let url = match (provider, what) {
                    ("GITHUB", "tag") => format!("{}/repos/{}/tags", base_url, git_args.repo),
                    ("GITHUB", "release") => {
                        format!("{}/repos/{}/releases/latest", base_url, git_args.repo)
                    }
                    ("GITHUB", "commit") => format!(
                        "{}/repos/{}/commits?sha={}",
                        base_url,
                        git_args.repo,
                        git_args.branch.as_deref().unwrap_or("HEAD")
                    ),

                    ("GITLAB", "tag") => format!(
                        "{}/api/v4/projects/{}/repository/tags",
                        base_url,
                        urlencoding::encode(&git_args.repo)
                    ),
                    ("GITLAB", "release") => format!(
                        "{}/api/v4/projects/{}/releases",
                        base_url,
                        urlencoding::encode(&git_args.repo)
                    ),
                    ("GITLAB", "commit") => format!(
                        "{}/api/v4/projects/{}/repository/commits?ref_name={}",
                        base_url,
                        urlencoding::encode(&git_args.repo),
                        git_args.branch.as_deref().unwrap_or("HEAD")
                    ),

                    ("GITEA" | "FORGEJO", "tag") => {
                        format!("{}/api/v1/repos/{}/tags", base_url, git_args.repo)
                    }
                    ("GITEA" | "FORGEJO", "release") => {
                        format!(
                            "{}/api/v1/repos/{}/releases/latest",
                            base_url, git_args.repo
                        )
                    }
                    ("GITEA" | "FORGEJO", "commit") => format!(
                        "{}/api/v1/repos/{}/commits?sha={}",
                        base_url,
                        git_args.repo,
                        git_args.branch.as_deref().unwrap_or("HEAD")
                    ),
                    _ => unreachable!(),
                };

                let json = fetch_json(&url)?;

                let result = match (provider, what) {
                    ("GITHUB", "tag") | ("GITEA", "tag") | ("FORGEJO", "tag") => json
                        .as_array()
                        .and_then(|a| a.first())
                        .and_then(|t| t["name"].as_str()),
                    ("GITHUB", "release") | ("GITEA", "release") | ("FORGEJO", "release") => {
                        json["tag_name"].as_str()
                    }
                    ("GITHUB", "commit") | ("GITEA", "commit") | ("FORGEJO", "commit") => json
                        .as_array()
                        .and_then(|a| a.first())
                        .and_then(|c| c["sha"].as_str()),

                    ("GITLAB", "tag") => json
                        .as_array()
                        .and_then(|a| a.first())
                        .and_then(|t| t["name"].as_str()),
                    ("GITLAB", "release") => json
                        .as_array()
                        .and_then(|a| a.first())
                        .and_then(|r| r["tag_name"].as_str()),
                    ("GITLAB", "commit") => json
                        .as_array()
                        .and_then(|a| a.first())
                        .and_then(|c| c["id"].as_str()),
                    _ => unreachable!(),
                };

                result.map(|s| s.to_string()).ok_or_else(|| {
                    mlua::Error::RuntimeError(
                        "Could not extract value from API response".to_string(),
                    )
                })
            })?;
            latest_table.set(what, get_latest_fn)?;
        }

        provider_table.set("LATEST", latest_table)?;
        fetch_table.set(provider, provider_table)?;
    }

    Ok(())
}