visualizer_cli/
debug_visualizer_app.rs1use std::{borrow::BorrowMut, cell::RefCell, io::Read, sync::{Arc, Mutex}};
2
3use futures::{
4 channel::oneshot::{self},
5 future::Shared,
6 FutureExt,
7};
8use serde::{Deserialize, Serialize};
9use serde_json::from_value;
10use url::Url;
11use wry::{Application, ApplicationProxy, Attributes, CustomProtocol, RpcRequest, WindowProxy};
12
13pub struct DebugVisualizerApp {
14 app: Application,
15}
16
17const BUNDLE_ZIP: &'static [u8] = include_bytes!("../web/dist/bundle.zip");
18
19
20impl DebugVisualizerApp {
21 pub fn new() -> wry::Result<DebugVisualizerApp> {
22 let app = Application::new()?;
23
24 Ok(DebugVisualizerApp { app })
25 }
26
27 pub fn proxy(&self) -> DebugVisualizerAppProxy {
28 DebugVisualizerAppProxy {
29 app_proxy: self.app.application_proxy(),
30 }
31 }
32
33 pub fn run(self) {
34 self.app.run();
35 }
36}
37
38pub struct DebugVisualizerAppProxy {
39 app_proxy: ApplicationProxy,
40}
41
42#[derive(Deserialize, Serialize)]
43struct InitializedEvent {}
44
45#[derive(Deserialize, Serialize)]
46#[serde(tag = "kind", rename_all = "camelCase")]
47enum Event {
48 Initialized(InitializedEvent),
49}
50
51#[derive(Default)]
52pub struct WindowOptions {
53 pub title: Option<String>,
54}
55
56impl DebugVisualizerAppProxy {
57 pub fn new_window(&self, options: WindowOptions) -> wry::Result<DebugVisualizerWindow> {
58
59 let c = std::io::Cursor::new(BUNDLE_ZIP);
60 let zip = Arc::new(Mutex::new(zip::ZipArchive::new(c).unwrap()));
61
62 let title = options.title.unwrap_or(String::from("Visualization"));
63
64 let attributes = Attributes {
65 url:
66 Some("app://host/index.html".to_string()),
68 title,
69 initialization_scripts: vec![
71 String::from("window.sendMessage = function(message) { window.rpc.call('handleMessage', message) };"),
72 ],
73 ..Default::default()
74 };
75
76 let (set_initialized, initialized) = oneshot::channel::<()>();
77
78 let set_initialized = Arc::new(Mutex::new(RefCell::new(Some(set_initialized))));
79
80 let handler = Box::new(move |proxy: WindowProxy, req: RpcRequest| {
81 if req.method == "handleMessage" {
82 let params = req.params.unwrap();
83 let params = params.as_array().unwrap();
84
85 let e: Event = from_value(params.into_iter().next().unwrap().clone()).unwrap();
86 match e {
87 Event::Initialized(_) => {
88 set_initialized
89 .lock()
90 .unwrap()
91 .borrow_mut()
92 .take()
93 .unwrap()
94 .send(())
95 .unwrap();
96 proxy.show().unwrap();
97 }
98 }
99 }
100 None
101 });
102
103 let window = self.app_proxy.add_window_with_configs(
104 attributes,
105 Some(handler),
106 Some(CustomProtocol {
107 name: String::from("app"),
108 handler: Box::new(move |url: &str| {
109 let url = Url::parse(url)?;
110 let path = &url.path()[1..];
111
112 let mut archive = zip.lock().unwrap();
113 let file_result = archive.by_name(path);
114 if let Ok(mut file) = file_result {
115 let mut buf = Vec::new();
116 file.read_to_end(&mut buf).unwrap();
117 Ok(buf)
118 } else {
119 Err(wry::Error::MessageSender)
120 }
121 }),
122 }),
123 None,
124 )?;
125
126 Ok(DebugVisualizerWindow {
127 window,
128 initialized: initialized.shared(),
129 })
130 }
131}
132
133pub struct DebugVisualizerWindow {
134 window: WindowProxy,
135 initialized: Shared<oneshot::Receiver<()>>,
136}
137
138#[derive(Deserialize, Serialize)]
139struct ShowVisualization {
140 data: serde_json::Value,
141}
142
143#[derive(Deserialize, Serialize)]
144#[serde(tag = "kind", rename_all = "camelCase")]
145enum Message {
146 ShowVisualization(ShowVisualization),
147}
148
149impl DebugVisualizerWindow {
150 async fn send_message(&self, message: Message) {
151 let message_str = serde_json::to_string(&message).unwrap();
152
153 self.initialized.clone().await.unwrap();
154
155 self.window
156 .evaluate_script(format!("window.processEvent({})", message_str))
157 .unwrap();
158 }
159
160 pub async fn show_visualization_data(&self, visualization_data: &str) -> wry::Result<()> {
161 self.send_message(Message::ShowVisualization(ShowVisualization {
162 data: serde_json::from_str(visualization_data).unwrap(),
163 }))
164 .await;
165 Ok(())
166 }
167}