forklaunch 1.18.0

Launch faster with forklaunch
use std::{collections::HashSet, path::Path};

use anyhow::Result;
use serde_json::{from_str, to_string_pretty};
use walkdir::WalkDir;

use crate::{
    constants::Database,
    core::{
        ast::transformations::transform_base_entity_ts::transform_base_entity_ts,
        database::{get_database_port, get_postinstall_script, is_in_memory_database},
        env::Env,
        manifest::ProjectEntry,
        package_json::{
            application_package_json::ApplicationPackageJson,
            package_json_constants::{
                MIKRO_ORM_DATABASE_VERSION, PROJECT_SEED_SCRIPT,
                project_retention_enforce_script,
            },
            project_package_json::ProjectPackageJson,
        },
        removal_template::RemovalTemplate,
        rendered_template::{RenderedTemplate, RenderedTemplatesCache, TEMPLATES_DIR},
        watermark::apply_watermark,
    },
};

pub(crate) fn change_database_base_entity(
    base_path: &Path,
    database: &Database,
    existing_database: &Database,
    projects: Vec<ProjectEntry>,
    project_name: &str,
    rendered_templates_cache: &mut RenderedTemplatesCache,
) -> Result<Option<RemovalTemplate>> {
    let import_source_from = match existing_database {
        Database::MongoDB => "nosql.base.properties.ts",
        _ => "sql.base.properties.ts",
    };
    let import_source_to = match database {
        Database::MongoDB => "nosql.base.properties.ts",
        _ => "sql.base.properties.ts",
    };

    let base_entity_from = match existing_database {
        Database::MongoDB => "nosqlBaseProperties",
        _ => "sqlBaseProperties",
    };
    let base_entity_to = match database {
        Database::MongoDB => "nosqlBaseProperties",
        _ => "sqlBaseProperties",
    };

    if let Some(base_entity_ts_content) = transform_base_entity_ts(
        rendered_templates_cache,
        &base_path.parent().unwrap(),
        database,
    )? {
        let entity_path = base_path
            .parent()
            .unwrap()
            .join("core")
            .join("persistence")
            .join(import_source_to);
        rendered_templates_cache.insert(
            entity_path.to_string_lossy(),
            RenderedTemplate {
                path: entity_path.clone(),
                content: base_entity_ts_content,
                context: None,
            },
        );
    }

    let entities_path = base_path.join("persistence").join("entities");
    for entry in WalkDir::new(&entities_path) {
        let entry = entry?;
        if entry.file_type().is_file() {
            let path = entry.path();
            if let Some(template) = rendered_templates_cache.get(&path)? {
                let content = template.content;
                let new_content = content
                    .replace(base_entity_from, base_entity_to)
                    .replace(import_source_from, import_source_to);
                if content != new_content {
                    rendered_templates_cache.insert(
                        path.to_string_lossy(),
                        RenderedTemplate {
                            path: path.to_path_buf(),
                            content: new_content,
                            context: None,
                        },
                    );
                }
            }
        }
    }

    let core_path = base_path.parent().unwrap().join("core");
    let persistence_path = core_path.join("persistence");
    let existing_base_entity_path = persistence_path.join(import_source_from);

    let mut all_projects_scan = HashSet::new();

    all_projects_scan.insert(database.clone());

    for project in projects.iter() {
        if project.name != project_name {
            if let Some(resources) = &project.resources {
                match &resources.database {
                    Some(database) => all_projects_scan.insert(database.parse::<Database>()?),
                    None => false,
                };
            }
        }
    }

    let entities_index_path = persistence_path.join("index.ts");
    rendered_templates_cache.insert(
        entities_index_path.to_string_lossy(),
        RenderedTemplate {
            path: entities_index_path.clone(),
            content: all_projects_scan
                .iter()
                .map(|export_source| {
                    let mut database_exports = String::new();
                    let exclusive_files = export_source.metadata().exclusive_files;
                    if let Some(export_sources) = exclusive_files {
                        for export_source in export_sources {
                            database_exports.push_str(&format!(
                                "export * from './{}';",
                                export_source.replace(".ts", "").to_string()
                            ));
                        }
                    }
                    database_exports
                })
                .collect::<Vec<String>>()
                .join("\n"),
            context: None,
        },
    );

    if database == &Database::MongoDB {
        let mut core_package_json = from_str::<ProjectPackageJson>(
            &rendered_templates_cache
                .get(core_path.join("package.json"))?
                .unwrap()
                .content,
        )?;
        let mut core_package_json_dependencies = core_package_json.dependencies.unwrap();
        core_package_json_dependencies.databases = all_projects_scan.clone();
        core_package_json_dependencies.mikro_orm_database =
            Some(MIKRO_ORM_DATABASE_VERSION.to_string());
        core_package_json.dependencies = Some(core_package_json_dependencies);
        rendered_templates_cache.insert(
            core_path.join("package.json").to_string_lossy(),
            RenderedTemplate {
                path: core_path.join("package.json").to_path_buf(),
                content: to_string_pretty(&core_package_json)?,
                context: None,
            },
        );
    }

    if !all_projects_scan.contains(&existing_database)
        && apply_watermark(&RenderedTemplate {
            path: existing_base_entity_path.clone().into(),
            content: TEMPLATES_DIR
                .get_file(
                    Path::new("project")
                        .join("core")
                        .join("persistence")
                        .join(import_source_from),
                )
                .unwrap()
                .contents_utf8()
                .unwrap()
                .to_string(),
            context: None,
        })?
        .trim()
            == rendered_templates_cache
                .get(&existing_base_entity_path)?
                .unwrap()
                .content
                .trim()
    {
        return Ok(Some(RemovalTemplate {
            path: existing_base_entity_path,
        }));
    }

    Ok(None)
}

pub(crate) fn change_database_env_variables(
    env_content: &mut Env,
    app_name: &str,
    worker_name: &str,
    database: &Database,
) {
    env_content.db_name = Some(format!("{}-{}-dev", app_name, worker_name));
    if !is_in_memory_database(database) {
        env_content.db_host = Some("localhost".to_string());
        env_content.db_user = Some(format!("{}", database.to_string()));
        env_content.db_password = Some(format!("{}", database.to_string()));
        if let Some(port) = get_database_port(database) {
            env_content.db_port = Some(format!("{}", port));
        }
    }
}

pub(crate) fn change_database_postinstall_script(
    application_package_json: &mut ApplicationPackageJson,
    database: &Database,
) {
    application_package_json
        .scripts
        .as_mut()
        .unwrap()
        .postinstall = get_postinstall_script(database);
}

pub(crate) fn change_database_seed_script(
    project_package_json: &mut ProjectPackageJson,
    database: &Database,
) {
    project_package_json.scripts.as_mut().unwrap().seed = match database {
        Database::MongoDB => None,
        _ => Some(PROJECT_SEED_SCRIPT.to_string()),
    };
}

pub(crate) fn change_database_retention_script(
    project_package_json: &mut ProjectPackageJson,
    runtime: &crate::constants::Runtime,
    has_database: bool,
) {
    project_package_json
        .scripts
        .as_mut()
        .unwrap()
        .retention_enforce = if has_database {
        Some(project_retention_enforce_script(runtime))
    } else {
        None
    };
}