blockless_sdk/
cgi.rs

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}