use super::CommandError;
use std::env;
use crate::batbelt::templates::TemplateGenerator;
use crate::batbelt::{BatEnumerator, ShareableData};
use crate::config::{BatAuditorConfig, BatConfig};
use colored::Colorize;
use error_stack::{FutureExt, Report, ResultExt};
use error_stack::{IntoReport, Result};
use crate::batbelt;
use crate::batbelt::git::git_commit::GitCommit;
use crate::batbelt::parser::entrypoint_parser::EntrypointParser;
use crate::batbelt::path::BatFile::GitIgnore;
use crate::batbelt::path::{BatFile, BatFolder};
use crate::batbelt::templates::code_overhaul_template::CodeOverhaulTemplate;
use crate::batbelt::templates::package_json_template::PackageJsonTemplate;
use crate::commands::{BatCommandEnumerator, CommandResult};
use clap::Subcommand;
use crate::batbelt::git::git_action::GitAction;
use crate::commands::sonar_commands::SonarCommand;
use std::path::Path;
use std::process::Command;
#[derive(
Subcommand, Debug, strum_macros::Display, PartialEq, Clone, strum_macros::EnumIter, Default,
)]
pub enum ProjectCommands {
#[default]
New,
Reload,
}
impl BatEnumerator for ProjectCommands {}
impl BatCommandEnumerator for ProjectCommands {
fn execute_command(&self) -> CommandResult<()> {
match self {
ProjectCommands::New => self.new_bat_project(),
ProjectCommands::Reload => self.reload_bat_project(),
}
}
fn check_metadata_is_initialized(&self) -> bool {
false
}
fn check_correct_branch(&self) -> bool {
false
}
}
impl ProjectCommands {
fn reload_bat_project(&self) -> CommandResult<()> {
let bat_auditor_toml_file = BatFile::BatAuditorToml;
if !bat_auditor_toml_file
.file_exists()
.change_context(CommandError)?
{
BatAuditorConfig::new_with_prompt().change_context(CommandError)?;
} else {
let mut bat_auditor_config =
BatAuditorConfig::get_config().change_context(CommandError)?;
bat_auditor_config
.get_external_bat_metadata()
.change_context(CommandError)?;
bat_auditor_config.save().change_context(CommandError)?;
}
let auditor_notes_bat_folder = BatFolder::AuditorNotes;
if !auditor_notes_bat_folder
.folder_exists()
.change_context(CommandError)?
{
let bat_auditor_config = BatAuditorConfig::get_config().change_context(CommandError)?;
project_commands_functions::init_auditor_configuration(
bat_auditor_config.auditor_name,
)?;
} else {
GitAction::CheckoutAuditorBranch
.execute_action()
.change_context(CommandError)?;
project_commands_functions::update_co_to_review()?;
project_commands_functions::update_package_json()?;
project_commands_functions::update_git_ignore()?;
GitCommit::BatReload
.create_commit(true)
.change_context(CommandError)?;
}
println!("bat project {}", "reloaded!".bright_green());
Ok(())
}
fn new_bat_project(&self) -> Result<(), CommandError> {
let bat_config = BatConfig::new_with_prompt().change_context(CommandError)?;
println!("Creating {:#?} project", bat_config);
TemplateGenerator
.create_new_project_folders()
.change_context(CommandError)?;
let bat_config: BatConfig = BatConfig::get_config().change_context(CommandError)?;
println!("Creating audit branch");
project_commands_functions::initialize_audit_branch()?;
PackageJsonTemplate::create_package_json(None).change_context(CommandError)?;
println!(
"\n\nRunning Sonar to update {}\n\n",
"BatMetadata.json!".bright_green()
);
SonarCommand::Run {
skip_source_code: false,
only_context_accounts: false,
only_entry_points: false,
only_traits: false,
only_function_dependencies: false,
}
.execute_command()?;
for auditor_name in bat_config.auditor_names {
BatFile::BatAuditorToml
.create_empty(false)
.change_context(CommandError)?;
let bat_auditor_config = BatAuditorConfig {
auditor_name: auditor_name.clone(),
miro_oauth_access_token: "".to_string(),
use_code_editor: false,
code_editor: Default::default(),
external_bat_metadata: vec![],
};
bat_auditor_config.save().change_context(CommandError)?;
project_commands_functions::init_auditor_configuration(auditor_name.clone())?;
BatFile::BatAuditorToml
.remove_file()
.change_context(CommandError)?;
}
BatAuditorConfig::new_with_prompt().change_context(CommandError)?;
BatFile::ProgramLib
.open_in_editor(false, None)
.change_context(CommandError)?;
println!("Project {} successfully created", bat_config.project_name);
Ok(())
}
}
mod project_commands_functions {
use super::*;
use lazy_regex::regex;
use walkdir::DirEntry;
pub fn init_auditor_configuration(auditor_name: String) -> CommandResult<()> {
let bat_config = BatConfig::get_config().change_context(CommandError)?;
let auditor_project_branch_name = format!("{}-{}", auditor_name, bat_config.project_name);
let auditor_project_branch_exists =
batbelt::git::check_if_branch_exists(&auditor_project_branch_name)
.change_context(CommandError)?;
if !auditor_project_branch_exists {
println!("Creating branch {:?}", auditor_project_branch_name);
Command::new("git")
.args(["checkout", "develop"])
.output()
.unwrap();
Command::new("git")
.args(["checkout", "-b", &auditor_project_branch_name])
.output()
.unwrap();
} else {
println!("Checking out {:?} branch", auditor_project_branch_name);
Command::new("git")
.args(["checkout", auditor_project_branch_name.as_str()])
.output()
.unwrap();
}
TemplateGenerator
.create_folders_for_current_auditor()
.change_context(CommandError)?;
initialize_code_overhaul_files()?;
GitCommit::InitAuditor
.create_commit(true)
.change_context(CommandError)?;
Ok(())
}
pub fn update_co_to_review() -> CommandResult<()> {
println!("Updating code overhaul files");
let co_bat_folder = BatFolder::CodeOverhaulFolderPath;
let co_dir_file_name = co_bat_folder
.get_all_bat_files(false, None, None)
.change_context(CommandError)?;
let entry_points_names = EntrypointParser::get_entrypoint_names_from_program_lib(true)
.change_context(CommandError)?;
let (old_ep, deprecated_ep): (Vec<BatFile>, Vec<BatFile>) =
co_dir_file_name.clone().into_iter().partition(|bat_file| {
entry_points_names.contains(
&bat_file
.get_file_name()
.unwrap()
.trim_end_matches(".md")
.to_string(),
)
});
let (_, new_ep): (Vec<String>, Vec<String>) =
entry_points_names.clone().into_iter().partition(|ep_name| {
co_dir_file_name.clone().into_iter().any(|bat_file| {
bat_file.get_file_name().unwrap().trim_end_matches(".md") == ep_name
})
});
let mut updated_eps = vec![];
for ep_name in new_ep {
println!(
"Creating code overhaul file for new entry point: {}{}",
ep_name.bright_blue(),
".md".bright_blue()
);
let bat_file = BatFile::CodeOverhaulToReview { file_name: ep_name };
bat_file.create_empty(false).change_context(CommandError)?;
updated_eps.push(bat_file.get_path(false).change_context(CommandError)?);
}
let deprecated_regex = regex!(r#"/code-overhaul/deprecated/"#);
let filtered_dep = deprecated_ep
.into_iter()
.filter(|dep_bat_file| {
!deprecated_regex.is_match(&dep_bat_file.get_path(false).unwrap())
})
.collect::<Vec<_>>();
if !filtered_dep.is_empty() {
let deprecated_co_bat_folder = BatFolder::CodeOverhaulDeprecated;
if !deprecated_co_bat_folder
.folder_exists()
.change_context(CommandError)?
{
deprecated_co_bat_folder
.create_folder()
.change_context(CommandError)?;
}
for ep_file in filtered_dep {
println!(
"Moving code overhaul file to deprecated folder: {}",
ep_file.get_path(false).unwrap().bright_blue()
);
let file_content = ep_file.read_content(false).change_context(CommandError)?;
let file_name = ep_file.get_file_name().change_context(CommandError)?;
let deprecated_file = BatFile::CodeOverhaulDeprecated { file_name };
deprecated_file
.write_content(false, &file_content)
.change_context(CommandError)?;
ep_file.remove_file().change_context(CommandError)?;
updated_eps.push(
deprecated_file
.get_path(false)
.change_context(CommandError)?,
);
updated_eps.push(ep_file.get_path(false).change_context(CommandError)?);
}
}
if !updated_eps.is_empty() {
GitCommit::CodeOverhaulUpdated { updated_eps }
.create_commit(true)
.change_context(CommandError)?;
}
Ok(())
}
pub fn update_package_json() -> CommandResult<()> {
println!("Updating package.json");
PackageJsonTemplate::create_package_json(None).change_context(CommandError)
}
pub fn update_git_ignore() -> CommandResult<()> {
println!("Updating .gitignore");
GitIgnore
.write_content(true, &TemplateGenerator.get_git_ignore_content())
.change_context(CommandError)
}
pub fn initialize_code_overhaul_files() -> Result<(), CommandError> {
let entrypoints_names =
EntrypointParser::get_entrypoint_names_from_program_lib(false).unwrap();
for entrypoint_name in entrypoints_names {
create_overhaul_file(entrypoint_name.clone())?;
}
Ok(())
}
pub fn create_overhaul_file(entrypoint_name: String) -> Result<(), CommandError> {
let code_overhaul_file_path = BatFile::CodeOverhaulToReview {
file_name: entrypoint_name.clone(),
}
.get_path(false)
.change_context(CommandError)?;
if Path::new(&code_overhaul_file_path).is_file() {
return Err(Report::new(CommandError).attach_printable(format!(
"code overhaul file already exists for: {entrypoint_name:?}"
)));
}
BatFile::CodeOverhaulToReview {
file_name: entrypoint_name.clone(),
}
.write_content(false, "")
.change_context(CommandError)?;
println!(
"code-overhaul file created: {}{}",
entrypoint_name.green(),
".md".green()
);
Ok(())
}
pub fn initialize_audit_branch() -> Result<(), CommandError> {
println!("Creating develop branch");
GitAction::CreateBranch {
branch_name: "develop".to_string(),
}
.execute_action()
.change_context(CommandError)?;
println!("Committing audit files");
GitAction::AddAll
.execute_action()
.change_context(CommandError)?;
GitCommit::Init
.create_commit(true)
.change_context(CommandError)?;
Ok(())
}
}