Skip to main content

xript_runtime/
handle.rs

1use std::collections::{HashMap, HashSet};
2use std::sync::mpsc;
3use std::thread;
4
5use crate::error::{Result, XriptError};
6use crate::fragment::ModInstance;
7use crate::sandbox::{ExecutionResult, RuntimeOptions};
8
9enum Command {
10    Execute {
11        code: String,
12        tx: mpsc::Sender<Result<ExecutionResult>>,
13    },
14    LoadMod {
15        mod_manifest_json: String,
16        fragment_sources: HashMap<String, String>,
17        granted_capabilities: HashSet<String>,
18        entry_source: Option<String>,
19        tx: mpsc::Sender<Result<ModInstance>>,
20    },
21    ManifestName {
22        tx: mpsc::Sender<String>,
23    },
24    Shutdown,
25}
26
27pub struct XriptHandle {
28    cmd_tx: mpsc::Sender<Command>,
29    thread: Option<thread::JoinHandle<()>>,
30}
31
32impl XriptHandle {
33    pub fn new(manifest_json: String, options: RuntimeOptions) -> Result<Self> {
34        let (cmd_tx, cmd_rx) = mpsc::channel::<Command>();
35        let (init_tx, init_rx) = mpsc::channel::<Result<()>>();
36
37        let thread = thread::spawn(move || {
38            let rt = match crate::create_runtime(&manifest_json, options) {
39                Ok(rt) => {
40                    let _ = init_tx.send(Ok(()));
41                    rt
42                }
43                Err(e) => {
44                    let _ = init_tx.send(Err(e));
45                    return;
46                }
47            };
48
49            while let Ok(cmd) = cmd_rx.recv() {
50                match cmd {
51                    Command::Execute { code, tx } => {
52                        let _ = tx.send(rt.execute(&code));
53                    }
54                    Command::LoadMod {
55                        mod_manifest_json,
56                        fragment_sources,
57                        granted_capabilities,
58                        entry_source,
59                        tx,
60                    } => {
61                        let _ = tx.send(rt.load_mod(
62                            &mod_manifest_json,
63                            fragment_sources,
64                            &granted_capabilities,
65                            entry_source.as_deref(),
66                        ));
67                    }
68                    Command::ManifestName { tx } => {
69                        let _ = tx.send(rt.manifest().name.clone());
70                    }
71                    Command::Shutdown => break,
72                }
73            }
74        });
75
76        init_rx
77            .recv()
78            .map_err(|_| XriptError::Engine("runtime thread panicked during init".into()))??;
79
80        Ok(Self {
81            cmd_tx,
82            thread: Some(thread),
83        })
84    }
85
86    pub fn execute(&self, code: &str) -> Result<ExecutionResult> {
87        let (tx, rx) = mpsc::channel();
88        self.cmd_tx
89            .send(Command::Execute {
90                code: code.to_string(),
91                tx,
92            })
93            .map_err(|_| XriptError::Engine("runtime thread is gone".into()))?;
94        rx.recv()
95            .map_err(|_| XriptError::Engine("runtime thread dropped response".into()))?
96    }
97
98    pub fn load_mod(
99        &self,
100        mod_manifest_json: &str,
101        fragment_sources: HashMap<String, String>,
102        granted_capabilities: &HashSet<String>,
103        entry_source: Option<&str>,
104    ) -> Result<ModInstance> {
105        let (tx, rx) = mpsc::channel();
106        self.cmd_tx
107            .send(Command::LoadMod {
108                mod_manifest_json: mod_manifest_json.to_string(),
109                fragment_sources,
110                granted_capabilities: granted_capabilities.clone(),
111                entry_source: entry_source.map(|s| s.to_string()),
112                tx,
113            })
114            .map_err(|_| XriptError::Engine("runtime thread is gone".into()))?;
115        rx.recv()
116            .map_err(|_| XriptError::Engine("runtime thread dropped response".into()))?
117    }
118
119    pub fn manifest_name(&self) -> Result<String> {
120        let (tx, rx) = mpsc::channel();
121        self.cmd_tx
122            .send(Command::ManifestName { tx })
123            .map_err(|_| XriptError::Engine("runtime thread is gone".into()))?;
124        rx.recv()
125            .map_err(|_| XriptError::Engine("runtime thread dropped response".into()))
126    }
127}
128
129impl Drop for XriptHandle {
130    fn drop(&mut self) {
131        let _ = self.cmd_tx.send(Command::Shutdown);
132        if let Some(thread) = self.thread.take() {
133            let _ = thread.join();
134        }
135    }
136}