Skip to main content

zoi_lua/api/
http.rs

1use mlua::{self, Lua, LuaSerdeExt, Table, Value};
2use zoi_core::utils;
3
4use serde::Deserialize;
5pub fn add_fetch_util(lua: &Lua) -> Result<(), mlua::Error> {
6    let fetch_table = lua.create_table()?;
7
8    let fetch_fn = lua.create_function(|_, url: String| -> Result<String, mlua::Error> {
9        let client =
10            utils::get_http_client().map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
11        let response = client
12            .get(url)
13            .send()
14            .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
15        let text = response
16            .text()
17            .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
18        Ok(text)
19    })?;
20    fetch_table.set("url", fetch_fn)?;
21
22    let utils_table: Table = lua.globals().get("UTILS")?;
23    utils_table.set("FETCH", fetch_table)?;
24
25    Ok(())
26}
27
28#[derive(Deserialize)]
29struct GitArgs {
30    repo: String,
31    domain: Option<String>,
32    branch: Option<String>,
33}
34
35fn fetch_json(url: &str) -> Result<serde_json::Value, mlua::Error> {
36    let client = utils::get_http_client().map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
37
38    let response = client
39        .get(url)
40        .send()
41        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
42
43    if !response.status().is_success() {
44        return Err(mlua::Error::RuntimeError(format!(
45            "Request to {} failed with status: {} and body: {}",
46            url,
47            response.status(),
48            response.text().unwrap_or_else(|_| "N/A".to_string())
49        )));
50    }
51
52    let text = response
53        .text()
54        .map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
55    serde_json::from_str(&text).map_err(|e| mlua::Error::RuntimeError(e.to_string()))
56}
57
58pub fn add_git_fetch_util(lua: &Lua) -> Result<(), mlua::Error> {
59    let utils_table: Table = lua.globals().get("UTILS")?;
60    let fetch_table: Table = utils_table.get("FETCH")?;
61
62    for provider in ["GITHUB", "GITLAB", "GITEA", "FORGEJO"] {
63        let provider_table = lua.create_table()?;
64        let latest_table = lua.create_table()?;
65
66        for what in ["tag", "release", "commit"] {
67            let get_latest_fn = lua.create_function(move |lua, args: Table| {
68                let git_args: GitArgs = lua
69                    .from_value(Value::Table(args))
70                    .map_err(|e| mlua::Error::RuntimeError(format!("Invalid arguments: {}", e)))?;
71
72                let base_url = match provider {
73                    "GITHUB" => git_args
74                        .domain
75                        .unwrap_or_else(|| "https://api.github.com".to_string()),
76                    "GITLAB" => git_args
77                        .domain
78                        .unwrap_or_else(|| "https://gitlab.com".to_string()),
79                    "GITEA" => git_args
80                        .domain
81                        .unwrap_or_else(|| "https://gitea.com".to_string()),
82                    "FORGEJO" => git_args
83                        .domain
84                        .unwrap_or_else(|| "https://codeberg.org".to_string()),
85                    _ => unreachable!(),
86                };
87
88                let url = match (provider, what) {
89                    ("GITHUB", "tag") => format!("{}/repos/{}/tags", base_url, git_args.repo),
90                    ("GITHUB", "release") => {
91                        format!("{}/repos/{}/releases/latest", base_url, git_args.repo)
92                    }
93                    ("GITHUB", "commit") => format!(
94                        "{}/repos/{}/commits?sha={}",
95                        base_url,
96                        git_args.repo,
97                        git_args.branch.as_deref().unwrap_or("HEAD")
98                    ),
99
100                    ("GITLAB", "tag") => format!(
101                        "{}/api/v4/projects/{}/repository/tags",
102                        base_url,
103                        urlencoding::encode(&git_args.repo)
104                    ),
105                    ("GITLAB", "release") => format!(
106                        "{}/api/v4/projects/{}/releases",
107                        base_url,
108                        urlencoding::encode(&git_args.repo)
109                    ),
110                    ("GITLAB", "commit") => format!(
111                        "{}/api/v4/projects/{}/repository/commits?ref_name={}",
112                        base_url,
113                        urlencoding::encode(&git_args.repo),
114                        git_args.branch.as_deref().unwrap_or("HEAD")
115                    ),
116
117                    ("GITEA" | "FORGEJO", "tag") => {
118                        format!("{}/api/v1/repos/{}/tags", base_url, git_args.repo)
119                    }
120                    ("GITEA" | "FORGEJO", "release") => {
121                        format!(
122                            "{}/api/v1/repos/{}/releases/latest",
123                            base_url, git_args.repo
124                        )
125                    }
126                    ("GITEA" | "FORGEJO", "commit") => format!(
127                        "{}/api/v1/repos/{}/commits?sha={}",
128                        base_url,
129                        git_args.repo,
130                        git_args.branch.as_deref().unwrap_or("HEAD")
131                    ),
132                    _ => unreachable!(),
133                };
134
135                let json = fetch_json(&url)?;
136
137                let result = match (provider, what) {
138                    ("GITHUB", "tag") | ("GITEA", "tag") | ("FORGEJO", "tag") => json
139                        .as_array()
140                        .and_then(|a| a.first())
141                        .and_then(|t| t["name"].as_str()),
142                    ("GITHUB", "release") | ("GITEA", "release") | ("FORGEJO", "release") => {
143                        json["tag_name"].as_str()
144                    }
145                    ("GITHUB", "commit") | ("GITEA", "commit") | ("FORGEJO", "commit") => json
146                        .as_array()
147                        .and_then(|a| a.first())
148                        .and_then(|c| c["sha"].as_str()),
149
150                    ("GITLAB", "tag") => json
151                        .as_array()
152                        .and_then(|a| a.first())
153                        .and_then(|t| t["name"].as_str()),
154                    ("GITLAB", "release") => json
155                        .as_array()
156                        .and_then(|a| a.first())
157                        .and_then(|r| r["tag_name"].as_str()),
158                    ("GITLAB", "commit") => json
159                        .as_array()
160                        .and_then(|a| a.first())
161                        .and_then(|c| c["id"].as_str()),
162                    _ => unreachable!(),
163                };
164
165                result.map(|s| s.to_string()).ok_or_else(|| {
166                    mlua::Error::RuntimeError(
167                        "Could not extract value from API response".to_string(),
168                    )
169                })
170            })?;
171            latest_table.set(what, get_latest_fn)?;
172        }
173
174        provider_table.set("LATEST", latest_table)?;
175        fetch_table.set(provider, provider_table)?;
176    }
177
178    Ok(())
179}