emu_runner/
lib.rs

1use std::process::{Command, Output};
2use camino::Utf8PathBuf;
3
4pub mod contexts;
5pub mod includes;
6
7#[derive(Debug)]
8pub enum Error {
9    StdIo(std::io::Error),
10    MissingExecutable(Utf8PathBuf),
11    MissingBash(Utf8PathBuf),
12    MissingConfig(Utf8PathBuf),
13    MissingRom(Utf8PathBuf),
14    MissingMovie(Utf8PathBuf),
15    MissingLua(Utf8PathBuf),
16    IncompatibleOSVersion,
17    AbsolutePathFailed,
18}
19impl From<std::io::Error> for Error {
20    fn from(value: std::io::Error) -> Self {
21        Self::StdIo(value)
22    }
23}
24
25/// Behavior used to run an emulator.
26pub trait EmulatorContext: Sized {
27    /// Returns the name of the base command to be executed.
28    /// 
29    /// Typically this is the executable file of the emulator (e.g. `BizHawk.exe` or `fceux.exe`).
30    /// 
31    /// If the executable is a linux binary in the working-directory, be sure to use `./cmd_name` rather than just `cmd_name`.
32    fn cmd_name(&self) -> String;
33    
34    /// Returns a list of arguments to be passed to [`Command::args`].
35    fn args(&self) -> Vec<String>;
36    
37    /// Returns a list of environment variables to be passed to [`Command::envs`].
38    fn env(&self) -> Vec<(String, String)>;
39    
40    /// Returns the path to the working directory intended for the command's child process.
41    /// 
42    /// Refer to [`Command::current_dir`] for more details.
43    fn working_dir(&self) -> Utf8PathBuf;
44    
45    /// Perform any file copying or final checks to ensure context is ready for running.
46    /// 
47    /// Returns an error if preparation failed.
48    fn prepare(&mut self) -> Result<(), Error>;
49    
50    /// Creates and executes a [`Command`] and returns the output result.
51    /// 
52    /// Default trait implementation simply calls [`run`].
53    fn run(self) -> Result<Output, Error> {
54        run(self)
55    }
56}
57
58/// Prepares and executes an emulator based on the provided context.
59/// 
60/// Returns any errors encountered while preparing (context-dependent) and any IO errors caused by running the command.
61pub fn run<C: EmulatorContext>(mut ctx: C) -> Result<Output, Error> {
62    ctx.prepare()?;
63    
64    command(ctx).output().map_err(|err| err.into())
65}
66
67/// Buildes a [`Command`] using data pulled from an [`EmulatorContext`].
68pub fn command<C: EmulatorContext>(ctx: C) -> Command {
69    let mut cmd = Command::new(ctx.cmd_name());
70    cmd.args(ctx.args())
71        .envs(ctx.env())
72        .current_dir(ctx.working_dir());
73    
74    cmd
75}