1use crate::CGIErrorKind;
2use json::{object::Object, JsonValue};
3use std::fmt::{Debug, Display};
4
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#[derive(Debug)]
31pub struct CGIExtensions {
32 pub file_name: String,
33 pub alias: String,
34 pub md5: String,
35 pub description: String,
36}
37
38impl Display for CGIExtensions {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 write!(f, "fileName: {},", self.file_name)?;
41 write!(f, "alias: {},", self.alias)?;
42 write!(f, "md5: {},", self.md5)?;
43 write!(f, "description: {},", self.description)
44 }
45}
46
47pub struct CGIEnv {
48 pub name: String,
49 pub value: String,
50}
51
52pub struct CGICommand {
53 command: String,
54 args: Vec<String>,
55 envs: Vec<CGIEnv>,
56 handle: Option<u32>,
57}
58
59type ReadFn = unsafe extern "C" fn(u32, *mut u8, u32, *mut u32) -> u32;
60
61impl CGICommand {
62 fn new(command: String, args: Vec<String>, envs: Vec<CGIEnv>) -> Self {
63 Self {
64 command,
65 args,
66 envs,
67 handle: None,
68 }
69 }
70
71 pub fn exec(&mut self) -> Result<(), CGIErrorKind> {
72 let mut handle = 0u32;
73 let parmas = self.json_params();
74 unsafe {
75 let rs = cgi_open(parmas.as_ptr(), parmas.len() as _, &mut handle);
76 if rs != 0 {
77 return Err(CGIErrorKind::ExecError);
78 }
79 };
80 self.handle = Some(handle);
81 Ok(())
82 }
83
84 fn read_all(&mut self, read_call: ReadFn) -> Result<Vec<u8>, CGIErrorKind> {
85 let mut readn = 0u32;
86 let mut data: Vec<u8> = Vec::new();
87 if self.handle.is_none() {
88 return Ok(data);
89 }
90 let handle = self.handle.unwrap();
91 let mut bs = [0u8; 1024];
92 loop {
93 unsafe {
94 let rs = read_call(handle, &mut bs as _, bs.len() as _, &mut readn);
95 if rs != 0 {
96 return Err(CGIErrorKind::ReadError);
97 }
98 if readn == 0 {
99 break;
100 }
101 data.extend_from_slice(&bs[..readn as _]);
102 }
103 }
104 Ok(data)
105 }
106
107 pub fn read_all_stdin(&mut self) -> Result<Vec<u8>, CGIErrorKind> {
108 self.read_all(cgi_stdout_read)
109 }
110
111 pub fn read_all_stderr(&mut self) -> Result<Vec<u8>, CGIErrorKind> {
112 self.read_all(cgi_stderr_read)
113 }
114
115 pub fn exec_command(&mut self) -> Result<String, CGIErrorKind> {
116 self.exec()?;
117 let bs = self.read_all_stdin()?;
118 String::from_utf8(bs).map_err(|_| CGIErrorKind::EncodingError)
119 }
120
121 fn json_params(&self) -> String {
122 let mut obj = Object::new();
123 let command = JsonValue::String(self.command.clone());
124 obj.insert("command", command);
125 let args = self
126 .args
127 .iter()
128 .map(|arg| JsonValue::String(arg.to_string()))
129 .collect::<Vec<_>>();
130 obj.insert("args", JsonValue::Array(args));
131 let envs = self
132 .envs
133 .iter()
134 .map(|env| {
135 let mut obj = Object::new();
136 let name = JsonValue::String(env.name.clone());
137 obj.insert("name", name);
138 let value = JsonValue::String(env.value.clone());
139 obj.insert("value", value);
140 JsonValue::Object(obj)
141 })
142 .collect::<Vec<_>>();
143 obj.insert("envs", JsonValue::Array(envs));
144 obj.dump()
145 }
146}
147
148pub struct CGIListExtensions {
149 handle: u32,
150}
151
152impl Drop for CGIListExtensions {
153 fn drop(&mut self) {
154 unsafe {
155 cgi_close(self.handle);
156 }
157 }
158}
159
160impl CGIListExtensions {
161 pub fn new() -> Result<Self, CGIErrorKind> {
162 let mut cgi_handle: u32 = 0;
163 unsafe {
164 let rs = cgi_list_exec(&mut cgi_handle as *mut u32);
165 if rs != 0 {
166 return Err(CGIErrorKind::ListError);
167 }
168 };
169 Ok(CGIListExtensions { handle: cgi_handle })
170 }
171
172 fn list_read_all(&self) -> Result<Vec<u8>, CGIErrorKind> {
173 let mut data: Vec<u8> = Vec::new();
174 let mut bs = [0u8; 1024];
175 let mut readn = 0u32;
176 loop {
177 unsafe {
178 let rs = cgi_list_read(self.handle, &mut bs as _, bs.len() as _, &mut readn);
179 if rs != 0 {
180 return Err(CGIErrorKind::ListError);
181 }
182 if readn == 0 {
183 break;
184 }
185 data.extend_from_slice(&bs[..readn as _]);
186 }
187 }
188 Ok(data)
189 }
190
191 pub fn command(
192 &self,
193 command: &str,
194 args: Vec<String>,
195 envs: Vec<CGIEnv>,
196 ) -> Result<CGICommand, CGIErrorKind> {
197 let extensions = self.list()?;
198 extensions
199 .iter()
200 .find(|ext| if &ext.alias == command { true } else { false })
201 .map(|_| CGICommand::new(command.to_string(), args, envs))
202 .ok_or(CGIErrorKind::NoCommandError)
203 }
204
205 pub fn list(&self) -> Result<Vec<CGIExtensions>, CGIErrorKind> {
206 let data = self.list_read_all()?;
207 let s = std::str::from_utf8(&data).map_err(|_| CGIErrorKind::EncodingError)?;
208 let json = json::parse(s).map_err(|_| CGIErrorKind::JsonDecodingError)?;
209 let externs = json
210 .members()
211 .map(|json| {
212 let file_name = json["fileName"].as_str().unwrap_or("").to_string();
213 let alias = json["alias"].as_str().unwrap_or("").to_string();
214 let md5 = json["md5"].as_str().unwrap_or("").to_string();
215 let description = json["description"].as_str().unwrap_or("").to_string();
216 CGIExtensions {
217 description,
218 file_name,
219 alias,
220 md5,
221 }
222 })
223 .collect::<Vec<_>>();
224 Ok(externs)
225 }
226}