#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![warn(clippy::all, clippy::pedantic)]
extern crate jargon_args;
extern crate lliw;
use proton_call::error::{Error, Kind};
use proton_call::{pass, throw, Config, Index, Proton, RunTimeVersion, RuntimeOption, Version};
use std::path::PathBuf;
use std::process::exit;
#[derive(Debug)]
struct Args {
program: PathBuf,
version: Version,
custom: Option<PathBuf>,
options: Vec<RuntimeOption>,
args: Vec<String>,
runtime_version: Option<RunTimeVersion>,
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let program: String = args[0].split('/').last().unwrap_or(&args[0]).to_string();
if let Err(e) = proton_caller(args) {
eprintln!("{}: {}", program, e);
let code = e.kind() as i32;
exit(code);
}
}
fn proton_caller(args: Vec<String>) -> Result<(), Error> {
use jargon_args::Jargon;
let mut parser: Jargon = Jargon::from_vec(args);
if parser.contains(["-h", "--help"]) {
help();
} else if parser.contains(["-v", "--version"]) {
version();
} else if parser.contains(["-i", "--index"]) {
let config: Config = Config::open()?;
let common_index = Index::new(&config.common())?;
println!("{}", common_index);
} else if parser.contains(["-a", "--add"]) {
todo!("command")
} else {
let config: Config = Config::open()?;
let mut args = Args {
program: parser.result_arg(["-r", "--run"])?,
version: parser.option_arg(["-p", "--proton"]).unwrap_or_default(),
custom: parser.option_arg(["-c", "--custom"]),
runtime_version: parser.option_arg::<RunTimeVersion, [&str; 2]>(["-R", "--runtime"]),
options: Vec::new(),
args: Vec::new(),
};
let (options, argv) = if parser.contains(["-o", "--options"]) {
let mut opts: Vec<RuntimeOption> = Vec::new();
if parser.contains(["-l", "--log"]) {
opts.insert(opts.len(), RuntimeOption::log)
}
let finish = parser.finish();
let mut arv = Vec::new();
for arg in finish {
if let Ok(opt) = arg.parse::<RuntimeOption>() {
opts.insert(opts.len(), opt)
} else {
arv.insert(arv.len(), arg)
}
}
(opts, arv)
} else {
let mut opts: Vec<RuntimeOption> = Vec::new();
if parser.contains(["-l", "--log"]) {
opts.insert(opts.len(), RuntimeOption::log)
}
(opts, parser.finish())
};
args.options = options;
args.args = argv;
let proton = if args.custom.is_some() {
custom_mode(&config, args)?
} else {
normal_mode(&config, args)?
};
let exit = proton.run()?;
if !exit.success() {
if let Some(code) = exit.code() {
throw!(Kind::ProtonExit, "code: {}", code);
}
throw!(Kind::ProtonExit, "an error");
}
}
Ok(())
}
fn get_proton_path(index: &mut Index, version: Version) -> Result<PathBuf, Error> {
if let Some(path) = index.get(&version) {
return Ok(path);
}
eprintln!(
"{}info:{} Proton {} not found, reindexing...",
lliw::Fg::Blue,
lliw::Reset,
version
);
index.index()?;
index.get(&version).ok_or_else(|| {
Error::new(
Kind::ProtonMissing,
format!("Proton {} does not exist", version),
)
})
}
fn normal_mode(config: &Config, args: Args) -> Result<Proton, Error> {
let mut index: Index = Index::new(&config.common())?;
let proton_path: PathBuf = get_proton_path(&mut index, args.version)?;
let proton: Proton = Proton::new(
args.version,
proton_path,
args.program,
args.args,
args.options,
config.data(),
config.steam(),
args.runtime_version,
config.common(),
);
pass!(proton)
}
fn custom_mode(config: &Config, args: Args) -> Result<Proton, Error> {
if let Some(custom) = args.custom {
let proton: Proton = Proton::new(
Version::from_custom(custom.as_path()),
custom,
args.program,
args.args,
args.options,
config.data(),
config.steam(),
args.runtime_version,
config.common(),
);
return pass!(proton);
}
throw!(Kind::Internal, "failed to run custom mode")
}
#[doc(hidden)]
static HELP: &str = "\
Usage: proton-call [OPTIONS]... EXE [EXTRA]...
Options:
-c, --custom [PATH] Path to a directory containing Proton to use
-h, --help View this help message
-i, --index View an index of installed Proton versions
-l, --log Pass PROTON_LOG variable to Proton
-o, --options [OPTIONS] Pass options to Runtime
-p, --proton [VERSION] Use Proton VERSION from `common`
-r, --run EXE Run EXE in proton
-R, --runtime [VERSION] Use runtime VERSION
-v, --version View version information
Config:
The config file should be located at '$XDG_CONFIG_HOME/proton.conf' or '$HOME/.config/proton.conf'
The config requires two values.
Data: a location to any directory to contain Proton's runtime files.
Steam: the directory to where steam is installed (the one which contains the steamapps directory).
Common: the directory to where your proton versions are stored, usually Steam's steamapps/common directory.
Example:
data = \"/home/avery/Documents/Proton/env/\"
steam = \"/home/avery/.steam/steam/\"
common = \"/home/avery/.steam/steam/steamapps/common/\"
";
#[doc(hidden)]
fn help() {
println!("{}", HELP);
}
#[doc(hidden)]
fn version() {
println!(
"Proton Caller (proton-call) {} Copyright (C) 2021 {}",
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_AUTHORS")
);
}