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
use std::process::{Command, Output};
use camino::Utf8PathBuf;
pub mod contexts;
pub mod includes;
#[derive(Debug)]
pub enum Error {
StdIo(std::io::Error),
MissingExecutable(Utf8PathBuf),
MissingBash(Utf8PathBuf),
MissingConfig(Utf8PathBuf),
MissingRom(Utf8PathBuf),
MissingMovie(Utf8PathBuf),
MissingLua(Utf8PathBuf),
IncompatibleOSVersion,
AbsolutePathFailed,
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::StdIo(value)
}
}
/// Behavior used to run an emulator.
pub trait EmulatorContext: Sized {
/// Returns the name of the base command to be executed.
///
/// Typically this is the executable file of the emulator (e.g. `BizHawk.exe` or `fceux.exe`).
///
/// If the executable is a linux binary in the working-directory, be sure to use `./cmd_name` rather than just `cmd_name`.
fn cmd_name(&self) -> String;
/// Returns a list of arguments to be passed to [`Command::args`].
fn args(&self) -> Vec<String>;
/// Returns a list of environment variables to be passed to [`Command::envs`].
fn env(&self) -> Vec<(String, String)>;
/// Returns the path to the working directory intended for the command's child process.
///
/// Refer to [`Command::current_dir`] for more details.
fn working_dir(&self) -> Utf8PathBuf;
/// Perform any file copying or final checks to ensure context is ready for running.
///
/// Returns an error if preparation failed.
fn prepare(&mut self) -> Result<(), Error>;
/// Creates and executes a [`Command`] and returns the output result.
///
/// Default trait implementation simply calls [`run`].
fn run(self) -> Result<Output, Error> {
run(self)
}
}
/// Prepares and executes an emulator based on the provided context.
///
/// Returns any errors encountered while preparing (context-dependent) and any IO errors caused by running the command.
pub fn run<C: EmulatorContext>(mut ctx: C) -> Result<Output, Error> {
ctx.prepare()?;
command(ctx).output().map_err(|err| err.into())
}
/// Buildes a [`Command`] using data pulled from an [`EmulatorContext`].
pub fn command<C: EmulatorContext>(ctx: C) -> Command {
let mut cmd = Command::new(ctx.cmd_name());
cmd.args(ctx.args())
.envs(ctx.env())
.current_dir(ctx.working_dir());
cmd
}