Skip to main content

jasper_plugin_sdk/
host.rs

1//! 宿主能力的类型化封装(spec §6.5「插件 → host」方法)。
2//! 每个函数对应一个 host_call 方法;所需能力见 spec §7,未授权时返回 code="forbidden"。
3
4use crate::rt::{call_host, PluginError};
5use base64::Engine as _;
6use serde_json::{json, Value};
7use std::collections::BTreeMap;
8
9/// `log`:宿主日志(无需能力)。失败静默忽略——日志不该让业务失败。
10pub fn log(level: &str, message: &str) {
11    let _ = call_host("log", json!({ "level": level, "message": message }));
12}
13
14/// `time.now`:当前时间 Unix 毫秒(无需能力,spec 0.2)。
15/// 沙箱内没有时钟(`SystemTime::now()` 在 wasm32 会 panic)——签名协议(S3 SigV4 等)用这个。
16pub fn now_ms() -> Result<i64, PluginError> {
17    let r = call_host("time.now", json!({}))?;
18    r.get("unix_ms")
19        .and_then(Value::as_i64)
20        .ok_or_else(|| PluginError::internal("time.now 响应缺 unix_ms"))
21}
22
23/// `settings.get`:读插件作用域 KV(能力 `settings`)。键不存在返回 Null。
24pub fn settings_get(key: &str) -> Result<Value, PluginError> {
25    let result = call_host("settings.get", json!({ "key": key }))?;
26    Ok(result.get("value").cloned().unwrap_or(Value::Null))
27}
28
29/// `settings.set`:写插件作用域 KV(能力 `settings`)。
30pub fn settings_set(key: &str, value: Value) -> Result<(), PluginError> {
31    call_host("settings.set", json!({ "key": key, "value": value }))?;
32    Ok(())
33}
34
35/// HTTP 请求(能力 `host:http`,spec 0.2)。二进制体在这里做 base64 编解码。
36#[derive(Debug, Clone, Default)]
37pub struct HttpRequest {
38    /// GET/PUT/PROPFIND/…(原样透传给宿主)
39    pub method: String,
40    pub url: String,
41    pub headers: BTreeMap<String, String>,
42    pub body: Option<Vec<u8>>,
43    pub timeout_ms: Option<u64>,
44}
45
46#[derive(Debug, Clone)]
47pub struct HttpResponse {
48    /// 非 2xx 也照常返回(spec §6.5:错误语义留给插件判断)
49    pub status: u16,
50    pub headers: BTreeMap<String, String>,
51    pub body: Vec<u8>,
52}
53
54impl HttpResponse {
55    pub fn is_success(&self) -> bool {
56        (200..300).contains(&self.status)
57    }
58    pub fn body_text(&self) -> String {
59        String::from_utf8_lossy(&self.body).into_owned()
60    }
61}
62
63/// `http.request`:宿主代理执行 HTTP(S)。网络失败(连接/超时)→ Err(internal);
64/// 拿到响应(含 4xx/5xx)→ Ok(HttpResponse)。
65pub fn http_request(req: &HttpRequest) -> Result<HttpResponse, PluginError> {
66    let b64 = base64::engine::general_purpose::STANDARD;
67    let mut params = json!({
68        "method": req.method,
69        "url": req.url,
70        "headers": req.headers,
71    });
72    if let Some(body) = &req.body {
73        params["body_b64"] = Value::String(b64.encode(body));
74    }
75    if let Some(t) = req.timeout_ms {
76        params["timeout_ms"] = json!(t);
77    }
78    let result = call_host("http.request", params)?;
79    let status = result
80        .get("status")
81        .and_then(Value::as_u64)
82        .ok_or_else(|| PluginError::internal("http.request 响应缺 status"))? as u16;
83    let headers = result
84        .get("headers")
85        .and_then(Value::as_object)
86        .map(|m| {
87            m.iter()
88                .filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
89                .collect()
90        })
91        .unwrap_or_default();
92    let body = match result.get("body_b64").and_then(Value::as_str) {
93        Some(s) => b64
94            .decode(s)
95            .map_err(|e| PluginError::internal(format!("body_b64 解码失败: {e}")))?,
96        None => Vec::new(),
97    };
98    Ok(HttpResponse { status, headers, body })
99}