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