1use crate::CGIErrorKind;
2use json::{object::Object, JsonValue};
3use std::fmt::{Debug, Display};
4
5#[cfg(not(feature = "mock-ffi"))]
6#[link(wasm_import_module = "blockless_cgi")]
7extern "C" {
8 #[link_name = "cgi_open"]
9 pub(crate) fn cgi_open(opts: *const u8, opts_len: u32, cgi_handle: *mut u32) -> u32;
10
11 #[link_name = "cgi_stdout_read"]
12 pub(crate) fn cgi_stdout_read(handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32;
13
14 #[link_name = "cgi_stderr_read"]
15 pub(crate) fn cgi_stderr_read(handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32;
16
17 #[link_name = "cgi_stdin_write"]
18 #[allow(dead_code)]
19 pub(crate) fn cgi_stdin_write(handle: u32, buf: *const u8, buf_len: u32, num: *mut u32) -> u32;
20
21 #[link_name = "cgi_close"]
22 pub(crate) fn cgi_close(handle: u32) -> u32;
23
24 #[link_name = "cgi_list_exec"]
25 pub(crate) fn cgi_list_exec(cgi_handle: *mut u32) -> u32;
26
27 #[link_name = "cgi_list_read"]
28 pub(crate) fn cgi_list_read(handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32;
29}
30
31#[cfg(feature = "mock-ffi")]
32#[allow(unused_variables)]
33mod mock_ffi {
34 pub unsafe extern "C" fn cgi_open(
35 _opts: *const u8,
36 _opts_len: u32,
37 cgi_handle: *mut u32,
38 ) -> u32 {
39 unimplemented!()
40 }
41
42 pub unsafe extern "C" fn cgi_stdout_read(
43 _handle: u32,
44 buf: *mut u8,
45 buf_len: u32,
46 num: *mut u32,
47 ) -> u32 {
48 unimplemented!()
49 }
50
51 pub unsafe extern "C" fn cgi_stderr_read(
52 _handle: u32,
53 buf: *mut u8,
54 buf_len: u32,
55 num: *mut u32,
56 ) -> u32 {
57 unimplemented!()
58 }
59
60 #[allow(dead_code)]
61 pub unsafe extern "C" fn cgi_stdin_write(
62 _handle: u32,
63 _buf: *const u8,
64 buf_len: u32,
65 num: *mut u32,
66 ) -> u32 {
67 unimplemented!()
68 }
69
70 pub unsafe fn cgi_close(_handle: u32) -> u32 {
71 unimplemented!()
72 }
73
74 pub unsafe fn cgi_list_exec(cgi_handle: *mut u32) -> u32 {
75 unimplemented!()
76 }
77
78 pub unsafe fn cgi_list_read(_handle: u32, buf: *mut u8, buf_len: u32, num: *mut u32) -> u32 {
79 unimplemented!()
80 }
81}
82
83#[cfg(feature = "mock-ffi")]
84use mock_ffi::*;
85
86#[derive(Debug)]
87pub struct CGIExtensions {
88 pub file_name: String,
89 pub alias: String,
90 pub md5: String,
91 pub description: String,
92}
93
94impl Display for CGIExtensions {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 write!(f, "fileName: {},", self.file_name)?;
97 write!(f, "alias: {},", self.alias)?;
98 write!(f, "md5: {},", self.md5)?;
99 write!(f, "description: {},", self.description)
100 }
101}
102
103pub struct CGIEnv {
104 pub name: String,
105 pub value: String,
106}
107
108pub struct CGICommand {
109 command: String,
110 args: Vec<String>,
111 envs: Vec<CGIEnv>,
112 handle: Option<u32>,
113}
114
115type ReadFn = unsafe extern "C" fn(u32, *mut u8, u32, *mut u32) -> u32;
116
117impl CGICommand {
118 fn new(command: String, args: Vec<String>, envs: Vec<CGIEnv>) -> Self {
119 Self {
120 command,
121 args,
122 envs,
123 handle: None,
124 }
125 }
126
127 pub fn exec(&mut self) -> Result<(), CGIErrorKind> {
128 let mut handle = 0u32;
129 let parmas = self.json_params();
130 unsafe {
131 let rs = cgi_open(parmas.as_ptr(), parmas.len() as _, &mut handle);
132 if rs != 0 {
133 return Err(CGIErrorKind::ExecError);
134 }
135 };
136 self.handle = Some(handle);
137 Ok(())
138 }
139
140 fn read_all(&mut self, read_call: ReadFn) -> Result<Vec<u8>, CGIErrorKind> {
141 let mut readn = 0u32;
142 let mut data: Vec<u8> = Vec::new();
143 if self.handle.is_none() {
144 return Ok(data);
145 }
146 let handle = self.handle.unwrap();
147 let mut bs = [0u8; 1024];
148 loop {
149 unsafe {
150 let rs = read_call(handle, &mut bs as _, bs.len() as _, &mut readn);
151 if rs != 0 {
152 return Err(CGIErrorKind::ReadError);
153 }
154 if readn == 0 {
155 break;
156 }
157 data.extend_from_slice(&bs[..readn as _]);
158 }
159 }
160 Ok(data)
161 }
162
163 pub fn read_all_stdin(&mut self) -> Result<Vec<u8>, CGIErrorKind> {
164 self.read_all(cgi_stdout_read)
165 }
166
167 pub fn read_all_stderr(&mut self) -> Result<Vec<u8>, CGIErrorKind> {
168 self.read_all(cgi_stderr_read)
169 }
170
171 pub fn exec_command(&mut self) -> Result<String, CGIErrorKind> {
172 self.exec()?;
173 let bs = self.read_all_stdin()?;
174 String::from_utf8(bs).map_err(|_| CGIErrorKind::EncodingError)
175 }
176
177 fn json_params(&self) -> String {
178 let mut obj = Object::new();
179 let command = JsonValue::String(self.command.clone());
180 obj.insert("command", command);
181 let args = self
182 .args
183 .iter()
184 .map(|arg| JsonValue::String(arg.to_string()))
185 .collect::<Vec<_>>();
186 obj.insert("args", JsonValue::Array(args));
187 let envs = self
188 .envs
189 .iter()
190 .map(|env| {
191 let mut obj = Object::new();
192 let name = JsonValue::String(env.name.clone());
193 obj.insert("name", name);
194 let value = JsonValue::String(env.value.clone());
195 obj.insert("value", value);
196 JsonValue::Object(obj)
197 })
198 .collect::<Vec<_>>();
199 obj.insert("envs", JsonValue::Array(envs));
200 obj.dump()
201 }
202}
203
204pub struct CGIListExtensions {
205 handle: u32,
206}
207
208impl Drop for CGIListExtensions {
209 fn drop(&mut self) {
210 unsafe {
211 cgi_close(self.handle);
212 }
213 }
214}
215
216impl CGIListExtensions {
217 pub fn new() -> Result<Self, CGIErrorKind> {
218 let mut cgi_handle: u32 = 0;
219 unsafe {
220 let rs = cgi_list_exec(&mut cgi_handle as *mut u32);
221 if rs != 0 {
222 return Err(CGIErrorKind::ListError);
223 }
224 };
225 Ok(CGIListExtensions { handle: cgi_handle })
226 }
227
228 fn list_read_all(&self) -> Result<Vec<u8>, CGIErrorKind> {
229 let mut data: Vec<u8> = Vec::new();
230 let mut bs = [0u8; 1024];
231 let mut readn = 0u32;
232 loop {
233 unsafe {
234 let rs = cgi_list_read(self.handle, &mut bs as _, bs.len() as _, &mut readn);
235 if rs != 0 {
236 return Err(CGIErrorKind::ListError);
237 }
238 if readn == 0 {
239 break;
240 }
241 data.extend_from_slice(&bs[..readn as _]);
242 }
243 }
244 Ok(data)
245 }
246
247 pub fn command(
248 &self,
249 command: &str,
250 args: Vec<String>,
251 envs: Vec<CGIEnv>,
252 ) -> Result<CGICommand, CGIErrorKind> {
253 let extensions = self.list()?;
254 extensions
255 .iter()
256 .find(|ext| ext.alias == command)
257 .map(|_| CGICommand::new(command.to_string(), args, envs))
258 .ok_or(CGIErrorKind::NoCommandError)
259 }
260
261 pub fn list(&self) -> Result<Vec<CGIExtensions>, CGIErrorKind> {
262 let data = self.list_read_all()?;
263 let s = std::str::from_utf8(&data).map_err(|_| CGIErrorKind::EncodingError)?;
264 let json = json::parse(s).map_err(|_| CGIErrorKind::JsonDecodingError)?;
265 let externs = json
266 .members()
267 .map(|json| {
268 let file_name = json["fileName"].as_str().unwrap_or("").to_string();
269 let alias = json["alias"].as_str().unwrap_or("").to_string();
270 let md5 = json["md5"].as_str().unwrap_or("").to_string();
271 let description = json["description"].as_str().unwrap_or("").to_string();
272 CGIExtensions {
273 description,
274 file_name,
275 alias,
276 md5,
277 }
278 })
279 .collect::<Vec<_>>();
280 Ok(externs)
281 }
282}