rush_wasm_engine/
wasm_runtime_build.rs

1use crate::{WasmLoaderFile, WasmRuntime, WASM_LOADER_FILE};
2use anyhow::anyhow;
3use std::collections::HashMap;
4use wd_tools::PFErr;
5
6#[async_trait::async_trait]
7pub trait WasmLoader: Send + Sync {
8    fn load(&self, _rule_name: String, file: String) -> anyhow::Result<Vec<u8>>;
9    async fn async_load(&self, rule_name: String, file: String) -> anyhow::Result<Vec<u8>> {
10        self.load(rule_name, file)
11    }
12}
13
14#[derive(Default)]
15pub struct WasmRuntimeFactory {
16    loader: HashMap<&'static str, Box<dyn WasmLoader>>,
17}
18
19impl WasmRuntimeFactory {
20    pub fn new() -> Self {
21        let loader: HashMap<&'static str, Box<dyn WasmLoader>> = HashMap::new();
22        let mut lrf = Self { loader };
23        lrf.add_loader(WASM_LOADER_FILE, WasmLoaderFile);
24        lrf
25    }
26    pub fn add_loader<Load: WasmLoader + 'static>(&mut self, tag: &'static str, loader: Load) {
27        self.loader.insert(tag, Box::new(loader));
28    }
29    pub fn remove_loader<S: AsRef<str>>(&mut self, tag: S) {
30        self.loader.remove(tag.as_ref());
31    }
32    fn check_engine(buf: &str) -> anyhow::Result<(String, String)> {
33        let buf = buf.trim_start_matches(|c| " \n\r\t".contains(c));
34        let (head, body) = if let Some(s) = buf.split_once('\n') {
35            s
36        } else {
37            return anyhow!("first input must is : rule [name] [description] wasm [other]").err();
38        };
39        let list = head.split(' ').collect::<Vec<_>>();
40        if list.len() < 4 {
41            return anyhow!("rule header format: rule [name] [description] wasm [other]").err();
42        }
43        if list[0].to_lowercase() != "rule" {
44            return anyhow!("rule header must have start 'rule'").err();
45        }
46        if list[3].to_lowercase() != "wasm" {
47            return anyhow!("WasmRuntime no support rule[{}]", list[3]).err();
48        }
49        let body = body.trim_start_matches(|c| " \n\r\t".contains(c));
50        Ok((list[2].to_string(), body.into()))
51    }
52    pub fn build<S: AsRef<str>>(&self, rule: S) -> anyhow::Result<WasmRuntime> {
53        let (rule, buf) = Self::check_engine(rule.as_ref())?;
54        for (k, v) in self.loader.iter() {
55            if buf.starts_with(*k) {
56                let bytes = v.load(rule, buf)?;
57                return WasmRuntime::new(bytes);
58            }
59        }
60        anyhow!("not found eligible loader").err()
61    }
62    pub async fn async_build<S: AsRef<str>>(&self, rule: S) -> anyhow::Result<WasmRuntime> {
63        let (rule, buf) = Self::check_engine(rule.as_ref())?;
64        for (k, v) in self.loader.iter() {
65            if buf.starts_with(*k) {
66                let bytes = v.async_load(rule, buf).await?;
67                return WasmRuntime::new(bytes);
68            }
69        }
70        anyhow!("not found eligible loader").err()
71    }
72}
73
74#[cfg(test)]
75mod test {
76    use crate::WasmRuntimeFactory;
77    use serde_json::Value;
78    use std::collections::HashMap;
79
80    const WASM_RULE: &'static str = "
81    rule WASM_RULE _ wasm
82    wasm_file: ../target/wasm32-unknown-unknown/release/wasm_example_one.wasm
83    ";
84
85    #[test]
86    fn test_wasm_build() {
87        let rt = WasmRuntimeFactory::new().build(WASM_RULE).unwrap();
88
89        let result: HashMap<String, String> = rt.call(Value::String("hello".into())).unwrap();
90        assert_eq!(result.get("input").unwrap().as_str(), "hello");
91    }
92}