use super::encode::auto_decode;
use crate::Result;
use serde::de;
use serde::{Deserialize, Serialize};
use std::ffi::OsStr;
use std::io;
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt as _;
use std::path::PathBuf;
use std::process::Command;
use std::process::{ExitStatus, Stdio};
use std::str;
pub const CREATE_NEW_CONSOLE: u32 = 0x00000010;
pub const CREATE_NO_WINDOW: u32 = 0x08000000;
pub const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
#[derive(Debug, Clone)]
pub struct CmdOutput {
pub stdout: String,
pub status: ExitStatus,
pub stderr: Vec<u8>,
}
pub fn cmd<I, S>(
exe: S,
args: I,
cwd: Option<PathBuf>,
has_window: bool,
_autodecode: bool,
) -> io::Result<CmdOutput>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let exe_full = if let Some(x) = &cwd {
x.join(exe.as_ref()).to_string_lossy().to_string()
} else {
exe.as_ref().to_string_lossy().to_string()
};
let mut binding = Command::new(&*exe_full);
let cmd = binding
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(x) = cwd {
cmd.current_dir(x);
}
#[cfg(target_os = "windows")]
{
let flag = if has_window {
CREATE_NEW_CONSOLE
} else {
CREATE_NO_WINDOW
};
cmd.creation_flags(flag | CREATE_NEW_PROCESS_GROUP);
}
let output = cmd.output()?;
let stdout =
{ auto_decode(&output.stdout).unwrap_or(String::from_utf8_lossy(&output.stdout).to_string()) };
Ok(CmdOutput {
stdout,
status: output.status,
stderr: output.stderr,
})
}
pub fn cmd_spawn<I, S>(
exe: S,
args: I,
cwd: Option<PathBuf>,
has_window: bool,
) -> io::Result<std::process::Child>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let exe_full = if let Some(x) = &cwd {
x.join(exe.as_ref()).to_string_lossy().to_string()
} else {
exe.as_ref().to_string_lossy().to_string()
};
let mut binding = Command::new(&*exe_full);
let cmd = binding
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(x) = cwd {
cmd.current_dir(x);
}
#[cfg(target_os = "windows")]
{
let flag = if has_window {
CREATE_NEW_CONSOLE
} else {
CREATE_NO_WINDOW
};
cmd.creation_flags(flag);
}
cmd.spawn()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CmdResult<T> {
pub content: String,
pub status: bool,
pub opts: T,
}
impl<T> CmdResult<T> {
pub fn set_opts(&mut self, opts: T) -> &mut Self {
self.opts = opts;
self
}
pub fn merge(&mut self, target: Self) -> &mut Self {
self.content = target.content;
self.status = target.status;
self
}
pub fn set_status(&mut self, state: bool) -> &mut Self {
self.status = state;
self
}
pub fn set_content(&mut self, content: String) -> &mut Self {
self.content = content;
self
}
pub fn opts(&self) -> &T {
&self.opts
}
}
impl<'a, T> CmdResult<T>
where
T: de::Deserialize<'a>,
{
pub fn from_str(value: &'a str) -> Result<Self> {
let s = value.trim().trim_start_matches("R<").trim_end_matches(">R");
let res: CmdResult<T> = serde_json::from_str(s)?;
Ok(res)
}
}
impl<T> CmdResult<T>
where
T: Serialize,
{
pub fn to_str(&self) -> Result<String> {
let s = format!("R<{}>R", serde_json::to_string(&self)?);
Ok(s)
}
pub fn to_string_pretty(&self) -> Result<String> {
let s = format!("R<{}>R", serde_json::to_string_pretty(&self)?);
Ok(s)
}
}
#[cfg(feature = "fs")]
pub fn shell_open(target: &str) -> Result<()> {
let binding = crate::fs::convert_path(target);
let pathname: &str = binding.as_str();
#[cfg(target_os = "macos")]
crate::system::cmd_spawn("open", ["-R", pathname], None, false)?;
#[cfg(target_os = "windows")]
crate::system::cmd_spawn("explorer.exe", [pathname], None, false)?;
#[cfg(target_os = "linux")]
crate::system::cmd_spawn("xdg-open", [pathname], None, false)?;
Ok(())
}