use std::{
ffi::OsStr,
path::Path,
process::{Command, ExitStatus},
};
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_family = "windows")] {
const CMD: &str = "cmd.exe";
const OPT: &str = "/C";
} else {
const CMD: &str = "bash";
const OPT: &str = "-c";
}
}
const NODE_ENV: &str = "NODE_ENV";
const NPM: &str = "npm";
const NPM_INIT: &str = "init";
const NPM_INSTALL: &str = "install";
const NPM_UNINSTALL: &str = "uninstall";
const NPM_UPDATE: &str = "update";
const NPM_RUN: &str = "run";
pub enum NodeEnv {
Development,
Production,
Custom(String),
}
pub struct NpmEnv(Command);
pub struct Npm {
cmd: Command,
args: Vec<String>,
}
impl Default for NodeEnv {
fn default() -> Self {
NodeEnv::Development
}
}
impl NodeEnv {
pub fn from_cargo_profile() -> Result<Self, std::env::VarError> {
Ok(match &std::env::var("PROFILE")?[..] {
"debug" => Self::Development,
"release" => Self::Production,
x => Self::Custom(x.to_string()),
})
}
}
impl Default for NpmEnv {
fn default() -> Self {
let mut cmd = Command::new(CMD);
cmd.arg(OPT);
cmd.current_dir(std::env::current_dir().unwrap());
Self(cmd)
}
}
impl Clone for NpmEnv {
fn clone(&self) -> Self {
let mut cmd = Command::new(self.0.get_program());
cmd.args(self.0.get_args());
cmd.current_dir(self.0.get_current_dir().unwrap());
Self(cmd)
}
}
impl NpmEnv {
pub fn with_node_env(self, node_env: &NodeEnv) -> Self {
let env = match node_env {
NodeEnv::Development => "development",
NodeEnv::Production => "production",
NodeEnv::Custom(c) => c,
};
self.with_env(NODE_ENV, env)
}
pub fn with_env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.0.env(key, val);
self
}
pub fn with_envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.0.envs(vars);
self
}
pub fn clear_envs(mut self) -> Self {
self.0.env_clear();
self
}
pub fn remove_env<K>(mut self, key: K) -> Self
where
K: AsRef<OsStr>,
{
self.0.env_remove(key);
self
}
pub fn set_path<P>(mut self, path: P) -> Self
where
P: AsRef<Path>,
{
self.0.current_dir(path);
self
}
pub fn init_env(self) -> Npm {
Npm {
cmd: self.0,
args: Default::default(),
}
}
}
impl Default for Npm {
fn default() -> Self {
NpmEnv::default().init_env()
}
}
impl Npm {
fn npm_append(&mut self, npm_cmd: &str, chain: &[&str]) {
self.args.push(
[NPM, npm_cmd]
.iter()
.chain(chain)
.copied()
.collect::<Vec<_>>()
.join(" "),
);
}
pub fn init(mut self) -> Self {
self.npm_append(NPM_INIT, &["-y"]);
self
}
pub fn install(mut self, args: Option<&[&str]>) -> Self {
self.npm_append(NPM_INSTALL, args.unwrap_or_default());
self
}
pub fn uninstall(mut self, pkg: &[&str]) -> Self {
self.npm_append(NPM_UNINSTALL, pkg);
self
}
pub fn update(mut self, pkg: Option<&[&str]>) -> Self {
self.npm_append(NPM_UPDATE, pkg.unwrap_or_default());
self
}
pub fn run(mut self, command: &str) -> Self {
self.args.push([NPM, NPM_RUN, command].join(" "));
self
}
pub fn custom(mut self, command: &str, args: Option<&[&str]>) -> Self {
self.npm_append(command, args.unwrap_or_default());
self
}
pub fn exec(mut self) -> Result<ExitStatus, std::io::Error> {
self.cmd.arg(self.args.join(" && "));
self.cmd.status()
}
}