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}