blob: 8856a29992a470f6a4c52b8588fad2403ca6a64a [file]
//! A process wrapper for `mdbook serve`.
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs};
use runfiles::rlocation;
#[cfg(target_family = "unix")]
const PATH_SEP: &str = ":";
#[cfg(target_family = "windows")]
const PATH_SEP: &str = ";";
struct Args {
pub mdbook: PathBuf,
pub config: PathBuf,
pub hostname: String,
pub port: String,
pub plugins: Vec<PathBuf>,
pub mdbook_args: Vec<String>,
}
impl Args {
pub fn parse() -> Self {
let runfiles = runfiles::Runfiles::create().unwrap();
let args_env = env::var("RULES_MDBOOK_SERVE_ARGS_FILE").unwrap();
let args_file = rlocation!(runfiles, args_env).unwrap();
let raw_args = action_args::try_parse_args(&args_file).unwrap();
let mut mdbook: Option<PathBuf> = None;
let mut config: Option<PathBuf> = None;
let mut hostname: Option<String> = None;
let mut port: Option<String> = None;
let mut plugins: Vec<PathBuf> = Vec::new();
for arg in raw_args {
if arg.starts_with("--mdbook=") {
let val = arg.split_once("=").unwrap().1;
mdbook = Some(rlocation!(runfiles, val).unwrap());
} else if arg.starts_with("--plugin=") {
let val = arg.split_once("=").unwrap().1.to_string();
plugins.push(rlocation!(runfiles, val).unwrap());
} else if arg.starts_with("--config=") {
let val = arg.split_once("=").unwrap().1.to_string();
config = Some(rlocation!(runfiles, val).unwrap());
} else if arg.starts_with("--hostname=") {
hostname = Some(arg.split_once("=").unwrap().1.to_string());
} else if arg.starts_with("--port=") {
port = Some(arg.split_once("=").unwrap().1.to_string());
}
}
Self {
mdbook: mdbook.unwrap(),
config: config.unwrap(),
hostname: hostname.unwrap(),
port: port.unwrap(),
plugins,
mdbook_args: env::args().skip(1).collect(),
}
}
}
const RULES_MDBOOK_TMP_NAME: &str = "rules_mdbook_server";
fn make_temp_dir() -> PathBuf {
if let Ok(var) = env::var("TMP") {
return PathBuf::from(var).join(RULES_MDBOOK_TMP_NAME);
}
if let Ok(var) = env::var("TEMP") {
return PathBuf::from(var).join(RULES_MDBOOK_TMP_NAME);
}
if let Ok(var) = env::var("TMPDIR") {
return PathBuf::from(var).join(RULES_MDBOOK_TMP_NAME);
}
if let Ok(var) = env::var("TEMPDIR") {
return PathBuf::from(var).join(RULES_MDBOOK_TMP_NAME);
}
let tmp = PathBuf::from("/tmp");
if tmp.exists() {
return tmp.join(RULES_MDBOOK_TMP_NAME);
}
if let Ok(var) = env::var("USERPROFILE") {
let tmp = PathBuf::from(var)
.join("AppData")
.join("Local")
.join("Temp");
if tmp.exists() {
return tmp.join(RULES_MDBOOK_TMP_NAME);
}
}
panic!("Could not determine how to create temp dir.")
}
fn main() {
let args = Args::parse();
let mut command = Command::new(&args.mdbook);
// Inject plugin paths into PATH
let pwd = env::current_dir().expect("Unable to determine current working directory");
if !args.plugins.is_empty() {
let path = env::var("PATH").unwrap_or_default();
let plugin_path = args
.plugins
.iter()
.map(|p| {
if p.is_absolute() {
p.parent().unwrap().to_string_lossy().to_string()
} else {
let abs = pwd.join(p);
abs.parent().unwrap().to_string_lossy().to_string()
}
})
.collect::<Vec<_>>()
.join(PATH_SEP);
command.env("PATH", format!("{}{}{}", plugin_path, PATH_SEP, path));
}
command
.arg("serve")
.arg(args.config.parent().unwrap())
.args(&args.mdbook_args);
// Add default hostname value if commandline was not specified.
if !args.mdbook_args.iter().any(|arg| {
["-n", "--hostname"].contains(&arg.as_str())
|| arg.starts_with("-n=")
|| arg.starts_with("--hostname=")
}) {
command.args(["--hostname", &args.hostname]);
}
// Add default port value if commandline was not specified.
if !args.mdbook_args.iter().any(|arg| {
["-p", "--port"].contains(&arg.as_str())
|| arg.starts_with("-p=")
|| arg.starts_with("--port=")
}) {
command.args(["--port", &args.port]);
}
// Check if `-d` or `--dest-dir` was passed. If not, make a temp dir
let temp_dir: Option<PathBuf> = if !args.mdbook_args.iter().any(|a| {
["-d", "--dest-dir"].contains(&a.as_str())
|| a.starts_with("-d=")
|| a.starts_with("--dest-dir=")
}) {
let temp_dir = make_temp_dir();
command.arg("--dest-dir").arg(&temp_dir);
Some(temp_dir)
} else {
None
};
// Run mdbook and save output
let status = command
.status()
.unwrap_or_else(|e| panic!("Failed to spawn mdbook command\n{:?}\n{:#?}", e, command));
if let Some(path) = temp_dir {
fs::remove_dir_all(&path).unwrap();
}
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
}
}