use colored::Colorize;
use std::io::Read;
use std::process::{Command, Stdio};
use std::str::from_utf8;
use error_stack::{IntoReport, ResultExt};
use serde::{Deserialize, Serialize};
use crate::batbelt::BatEnumerator;
use crate::commands::{CommandError, CommandResult};
use crate::config::BatAuditorConfig;
#[derive(
Default,
Debug,
Serialize,
Deserialize,
Clone,
strum_macros::EnumIter,
strum_macros::Display,
PartialOrd,
PartialEq,
)]
pub enum CodeEditor {
CLion,
VSCode,
#[default]
None,
}
impl BatEnumerator for CodeEditor {}
impl CodeEditor {
pub fn open_file_in_editor(path: &str, line_index: Option<usize>) -> CommandResult<()> {
let bat_auditor_config = BatAuditorConfig::get_config().change_context(CommandError)?;
if !bat_auditor_config.use_code_editor {
log::warn!("Code editor disabled");
println!("Path to file: {:#?}:{}", path, line_index.unwrap_or(0));
return Ok(());
}
let starting_line = line_index.unwrap_or(0);
println!(
"Opening {} on {}!",
path.trim_start_matches("../").green(),
bat_auditor_config.code_editor.get_colored_name(false)
);
match bat_auditor_config.code_editor {
CodeEditor::CLion => {
execute_command(
"clion",
&["--line", &format!("{}", starting_line), path],
false,
)
.change_context(CommandError)?;
}
CodeEditor::VSCode => {
if starting_line == 0 {
execute_command("code", &["-a", path], false).change_context(CommandError)?;
} else {
let goto_path = format!("{}:{}", path, starting_line);
execute_command("code", &["-a", "--goto", &goto_path], false)
.change_context(CommandError)?;
};
}
_ => {
println!("Path to file: {:#?}:{}", path, starting_line);
}
}
Ok(())
}
}
pub fn execute_command(command: &str, args: &[&str], print_output: bool) -> CommandResult<String> {
let message = format!(
"Error executing a process for parameters: \n command: {} \n args: {:#?}",
command, args
);
let output = Command::new(command)
.args(args)
.output()
.into_report()
.change_context(CommandError)
.attach_printable(message)?;
let message = format!(
"Error reading parsing output to string: \n {:#?}",
output.stdout
);
let output_string = from_utf8(output.stdout.as_slice())
.into_report()
.change_context(CommandError)
.attach_printable(message)?
.to_string();
log::debug!(target:"execute_command", "command: {}\n args: {:#?}\noutput: \n{}", command, args, output_string);
if print_output {
println!("{}", output_string);
}
Ok(output_string)
}
pub fn execute_command_with_child_process(command: &str, args: &[&str]) -> CommandResult<String> {
let message = format!(
"Error spawning a child process for parameters: \n command: {} \n args: {:#?}",
command, args
);
let mut output = Command::new(command)
.args(args)
.stdout(Stdio::piped())
.spawn()
.into_report()
.change_context(CommandError)
.attach_printable(message)?;
let message = format!("Error waiting child process: \n {:#?}", output.stdout);
output
.wait()
.into_report()
.change_context(CommandError)
.attach_printable(message)?;
let message = format!("Error waiting child process: \n {:#?}", output.stdout);
let mut output_string = String::new();
output
.stdout
.ok_or(CommandError)
.into_report()
.attach_printable(message)?
.read_to_string(&mut output_string)
.into_report()
.change_context(CommandError)?;
log::debug!(target:"execute_command_with_child_process", "command: {}\n args: {:#?}\noutput: \n{}", command, args, output_string);
Ok(output_string)
}