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