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