visualizer_cli/
debug_visualizer_app.rs

1use 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("http://localhost:8080".to_string()), 
67                Some("app://host/index.html".to_string()),
68            title,
69            //visible: false,
70            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}