aboutsummaryrefslogtreecommitdiff
path: root/bundle/src-tauri/src/main.rs
blob: 80b28c58448d6ad28eaf561de49ddd8e0281aa1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

mod compiler;
mod runner;
use compiler::Compiler;
use runner::{OutputEvent, Runner};
use std::{
    collections::HashMap,
    sync::{
        atomic::{AtomicUsize, Ordering},
        Mutex,
    },
};
use tauri::{ipc::Channel, AppHandle, Manager, State, WebviewWindow, WebviewWindowBuilder};

// Each window runs its own program, keyed by window label.
#[derive(Default)]
struct Runners(Mutex<HashMap<String, Runner>>);

impl Runners {
    fn get(&self, label: &str) -> Runner {
        self.0
            .lock()
            .unwrap()
            .entry(label.into())
            .or_default()
            .clone()
    }
}

#[tauri::command]
fn read_file(path: String) -> Result<String, String> {
    std::fs::read_to_string(path).map_err(|error| error.to_string())
}

#[tauri::command]
fn write_file(path: String, text: String) -> Result<(), String> {
    std::fs::write(path, text).map_err(|error| error.to_string())
}

#[tauri::command]
async fn run_file(
    path: String,
    input: String,
    output: Channel<OutputEvent>,
    window: WebviewWindow,
    runners: State<'_, Runners>,
    compiler: State<'_, Compiler>,
) -> Result<(), String> {
    let executable = compiler.inner().0.clone()?;
    let result = runners
        .get(window.label())
        .run(executable, path.into(), input, |stream, bytes| {
            let _ = output.send(OutputEvent::Data {
                stream: stream.into(),
                bytes,
            });
        })
        .await?;
    output
        .send(OutputEvent::Finished {
            code: result.code,
            stopped: result.stopped,
        })
        .map_err(|error| error.to_string())
}

#[tauri::command]
async fn stop_run(window: WebviewWindow, runners: State<'_, Runners>) -> Result<(), String> {
    runners.get(window.label()).stop().await;
    Ok(())
}

// Async because creating a window from a synchronous command deadlocks on Windows.
#[tauri::command]
async fn new_window(app: AppHandle) -> Result<(), String> {
    static NEXT: AtomicUsize = AtomicUsize::new(1);
    let mut config = app.config().app.windows[0].clone();
    config.label = format!("window-{}", NEXT.fetch_add(1, Ordering::Relaxed));
    WebviewWindowBuilder::from_config(&app, &config)
        .and_then(|builder| builder.build())
        .map(drop)
        .map_err(|error| error.to_string())
}

fn main() {
    let compiler = Compiler::load();
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .manage(Runners::default())
        .manage(compiler)
        .invoke_handler(tauri::generate_handler![
            read_file, write_file, run_file, stop_run, new_window
        ])
        .build(tauri::generate_context!())
        .expect("Could not start CPSC Bundle")
        .run(|app, event| match event {
            tauri::RunEvent::ExitRequested { .. } => {
                let runners: Vec<Runner> = app
                    .state::<Runners>()
                    .0
                    .lock()
                    .unwrap()
                    .values()
                    .cloned()
                    .collect();
                tauri::async_runtime::block_on(async {
                    for runner in runners {
                        runner.stop().await;
                    }
                });
            }
            tauri::RunEvent::WindowEvent {
                label,
                event: tauri::WindowEvent::Destroyed,
                ..
            } => {
                let runner = app.state::<Runners>().0.lock().unwrap().remove(&label);
                if let Some(runner) = runner {
                    tauri::async_runtime::block_on(runner.stop());
                }
            }
            _ => {}
        });
}