blockless_sdk/
cgi.rs

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}