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}