ff64021c20
将 Tauri 文件导入导出执行编排收口到 files 模块 让桌面 dispatch 只委托文件能力请求 同步桌面壳文件边界门禁、架构文档和共享记忆
208 lines
8.1 KiB
Rust
208 lines
8.1 KiB
Rust
use crate::host_bridge::appearance::desktop_appearance_color_scheme;
|
|
use crate::host_bridge::badge::set_desktop_app_badge_count;
|
|
use crate::host_bridge::capabilities::capabilities;
|
|
use crate::host_bridge::clipboard::{
|
|
read_desktop_host_bridge_clipboard_text, write_desktop_host_bridge_clipboard_text,
|
|
};
|
|
use crate::host_bridge::files::{
|
|
export_desktop_host_bridge_audio_file, export_desktop_host_bridge_image_file,
|
|
export_desktop_host_bridge_text_file, import_desktop_host_bridge_audio_file,
|
|
import_desktop_host_bridge_document_file, import_desktop_host_bridge_image_file,
|
|
import_desktop_host_bridge_text_file,
|
|
};
|
|
use crate::host_bridge::navigation::{
|
|
open_desktop_host_bridge_external_url, open_desktop_host_bridge_native_page,
|
|
reload_desktop_host_bridge_webview,
|
|
};
|
|
use crate::host_bridge::network::resolve_desktop_host_bridge_network_status;
|
|
use crate::host_bridge::notifications::show_desktop_local_notification;
|
|
use crate::host_bridge::protocol::{
|
|
failed, ok, validate_request, HostBridgeRequest, HostBridgeResponse, HostBridgeRuntime,
|
|
HOST_BRIDGE_VERSION,
|
|
};
|
|
use crate::host_bridge::share::{
|
|
open_desktop_host_bridge_share, set_desktop_host_bridge_share_target,
|
|
};
|
|
use crate::host_bridge::title::set_desktop_host_bridge_window_title;
|
|
use crate::shell::webview::desktop_platform;
|
|
use serde_json::json;
|
|
|
|
fn desktop_runtime() -> HostBridgeRuntime {
|
|
HostBridgeRuntime {
|
|
shell: "tauri_desktop",
|
|
platform: desktop_platform(),
|
|
host_version: env!("CARGO_PKG_VERSION"),
|
|
bridge_version: HOST_BRIDGE_VERSION,
|
|
capabilities: capabilities(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resolve_host_bridge_request(request: HostBridgeRequest) -> HostBridgeResponse {
|
|
if let Some(response) = validate_request(&request) {
|
|
return response;
|
|
}
|
|
|
|
match request.method.as_str() {
|
|
"host.getRuntime" => ok(request.id, json!(desktop_runtime())),
|
|
_ => failed(
|
|
request.id,
|
|
"unsupported_method",
|
|
format!("{} unsupported in desktop shell", request.method),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub(super) async fn execute_host_bridge_request(
|
|
app: tauri::AppHandle,
|
|
request: HostBridgeRequest,
|
|
) -> HostBridgeResponse {
|
|
if let Some(response) = validate_request(&request) {
|
|
return response;
|
|
}
|
|
|
|
match request.method.as_str() {
|
|
"app.openExternalUrl" => open_desktop_host_bridge_external_url(&app, &request),
|
|
"appearance.getColorScheme" => desktop_appearance_color_scheme(&app, &request),
|
|
"navigation.openNativePage" => open_desktop_host_bridge_native_page(&app, &request),
|
|
"app.reloadWebView" => reload_desktop_host_bridge_webview(&app, &request),
|
|
"clipboard.writeText" => write_desktop_host_bridge_clipboard_text(&app, &request),
|
|
"clipboard.readText" => read_desktop_host_bridge_clipboard_text(&app, &request),
|
|
"file.exportText" => export_desktop_host_bridge_text_file(&app, &request).await,
|
|
"file.importText" => import_desktop_host_bridge_text_file(&app, &request).await,
|
|
"file.importDocument" => import_desktop_host_bridge_document_file(&app, &request).await,
|
|
"file.exportImage" => export_desktop_host_bridge_image_file(&app, &request).await,
|
|
"file.importImage" => import_desktop_host_bridge_image_file(&app, &request).await,
|
|
"file.importAudio" => import_desktop_host_bridge_audio_file(&app, &request).await,
|
|
"file.exportAudio" => export_desktop_host_bridge_audio_file(&app, &request).await,
|
|
"app.setTitle" => set_desktop_host_bridge_window_title(&app, &request),
|
|
"app.setBadgeCount" => match set_desktop_app_badge_count(&app, &request) {
|
|
Ok(()) => ok(request.id, json!(true)),
|
|
Err(response) => response,
|
|
},
|
|
"network.status" => match resolve_desktop_host_bridge_network_status().await {
|
|
Ok(status) => ok(request.id, status),
|
|
Err(error) => failed(request.id, "host_error", error.to_string()),
|
|
},
|
|
"notification.showLocal" => match show_desktop_local_notification(&app, &request) {
|
|
Ok(()) => ok(request.id, json!(true)),
|
|
Err(response) => response,
|
|
},
|
|
"share.setTarget" => set_desktop_host_bridge_share_target(&app, &request),
|
|
"share.open" => open_desktop_host_bridge_share(&app, &request),
|
|
_ => resolve_host_bridge_request(request),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::host_bridge::protocol::request;
|
|
|
|
#[test]
|
|
fn runtime_response_reports_tauri_shell() {
|
|
let response = resolve_host_bridge_request(request("host.getRuntime"));
|
|
|
|
assert!(response.ok);
|
|
let result = response.result.expect("runtime result");
|
|
assert_eq!(result["shell"], "tauri_desktop");
|
|
assert_eq!(result["bridgeVersion"], HOST_BRIDGE_VERSION);
|
|
assert_eq!(result["capabilities"], json!(capabilities()));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("appearance.getColorScheme")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("host.events")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("app.lifecycle")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("network.status")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("network.statusChanged")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("share.open")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("share.setTarget")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("navigation.openNativePage")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("app.reloadWebView")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("app.setTitle")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("app.setBadgeCount")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("clipboard.readText")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.exportText")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.importText")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.importDocument")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.exportImage")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.importImage")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.importAudio")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.exportAudio")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("file.imageDropped")));
|
|
assert!(result["capabilities"]
|
|
.as_array()
|
|
.unwrap()
|
|
.contains(&json!("notification.showLocal")));
|
|
}
|
|
|
|
#[test]
|
|
fn unsupported_method_is_explicit() {
|
|
for method in ["auth.requestLogin", "payment.request", "scanner.scanQrCode"] {
|
|
let response = resolve_host_bridge_request(request(method));
|
|
|
|
assert!(!response.ok);
|
|
let error = response.error.expect("error");
|
|
assert_eq!(error.code, "unsupported_method");
|
|
assert!(error.message.contains(method));
|
|
}
|
|
}
|
|
}
|