blockless_sdk/
cgi.rs

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 {}