lezeh-deployment 0.0.2

CLI related with deployment operations, mostly to improve productivity working with deployment at work, for personal use. See https://github.com/sendyhalim/lezeh for details.
Documentation
use std::borrow::Cow;
use std::collections::HashMap;

use anyhow::anyhow;
use clap::App as Cli;
use clap::Arg;
use clap::ArgMatches;
use clap::SubCommand;
use serde::Serialize;

use crate::asset;
use crate::client::FailedMergeTaskOutput;
use crate::client::GlobalDeploymentClient;
use crate::client::SuccesfulMergeTaskOutput;
use crate::client::TaskInMasterBranch;
use crate::client::UserTaskMapping;
use crate::config::Config;
use lezeh_common::handlebars::HandlebarsRenderer;
use lezeh_common::types::ResultAnyError;

pub mod built_info {
  include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

#[derive(Debug, Serialize)]
struct TaskMergeSummary<'a> {
  success_merge_results: Vec<&'a SuccesfulMergeTaskOutput>,
  failed_merge_results: Vec<&'a FailedMergeTaskOutput>,
  already_in_master_branch_related_commits: Vec<&'a TaskInMasterBranch>,
}

impl<'a> Default for TaskMergeSummary<'a> {
  fn default() -> Self {
    return TaskMergeSummary {
      success_merge_results: Default::default(),
      failed_merge_results: Default::default(),
      already_in_master_branch_related_commits: Default::default(),
    };
  }
}

pub struct DeploymentCli {}

impl DeploymentCli {
  pub fn cmd<'a, 'b>(cli_name: Option<&str>) -> Cli<'a, 'b> {
    let task_id_args = Arg::with_name("task_ids")
      .multiple(true)
      .required(true)
      .help("task ids");

    return Cli::new(cli_name.unwrap_or("lezeh-deployment"))
        .version(built_info::PKG_VERSION)
        .author(built_info::PKG_AUTHORS)
        .about(built_info::PKG_DESCRIPTION)
        .setting(clap::AppSettings::ArgRequiredElseHelp)
        .subcommand(
          SubCommand::with_name("deploy")
          .about("Merge repo (given repo key) based on given deployment scheme config")
          .arg(Arg::with_name("repo_key")
            .required(true)
            .help("Repo key")
          )
          .arg(Arg::with_name("scheme_key")
            .required(true)
            .help("Deployment scheme key")
          )
        )
        .subcommand(
          SubCommand::with_name("merge-feature-branches")
          .about("Rebase and merge all feature branches for all repos in the config based on the given task ids")
          .arg(task_id_args),
        );
  }

  pub async fn run(
    cli: &ArgMatches<'_>,
    config: Config,
    logger: &'static slog::Logger,
  ) -> ResultAnyError<()> {
    let deployment_client = GlobalDeploymentClient::new(config.clone(), logger)?;

    if let Some(deploy_cli) = cli.subcommand_matches("deploy") {
      let repo_key: &str = deploy_cli.value_of("repo_key").unwrap();
      let scheme_key: &str = deploy_cli.value_of("scheme_key").unwrap();

      return deployment_client.deploy(repo_key, scheme_key).await;
    } else if let Some(merge_feature_branches_cli) =
      cli.subcommand_matches("merge-feature-branches")
    {
      let task_ids = merge_feature_branches_cli
        .values_of("task_ids")
        .unwrap()
        .map(Into::into)
        .collect();

      let merge_feature_branches_output =
        deployment_client.merge_feature_branches(&task_ids).await?;
      let not_found_user_task_mapping_by_task_id: HashMap<String, &UserTaskMapping> =
        merge_feature_branches_output
          .not_found_user_task_mappings
          .iter()
          .map(|user_task_mapping| {
            return (user_task_mapping.1.id.clone(), user_task_mapping);
          })
          .collect();

      let mut template_data: HashMap<&str, Box<dyn erased_serde::Serialize>> = HashMap::new();

      let mut merge_result_summary_by_task_id: HashMap<String, TaskMergeSummary> =
        Default::default();

      for (task_id, _) in merge_feature_branches_output.task_by_id.iter() {
        let task_summary = merge_result_summary_by_task_id
          .entry(task_id.clone())
          .or_default();

        for merge_all_task_output in merge_feature_branches_output.merge_all_tasks_outputs.iter() {
          let tasks_in_master_branch = merge_all_task_output
            .tasks_in_master_branch_by_task_id
            .get(task_id);

          if tasks_in_master_branch.is_some() {
            task_summary
              .already_in_master_branch_related_commits
              .extend(tasks_in_master_branch.unwrap());
          }

          let successful_merge_task = merge_all_task_output
            .successful_merge_task_output_by_task_id
            .get(task_id);

          if successful_merge_task.is_some() {
            task_summary
              .success_merge_results
              .push(successful_merge_task.unwrap());
          }

          let failed_merge_task = merge_all_task_output
            .failed_merge_task_output_by_task_id
            .get(task_id);

          if failed_merge_task.is_some() {
            task_summary
              .failed_merge_results
              .push(failed_merge_task.unwrap());
          }
        }
      }

      template_data.insert(
        "merge_result_summary_by_task_id",
        Box::from(merge_result_summary_by_task_id),
      );

      template_data.insert(
        "merge_feature_branches_output",
        Box::from(&merge_feature_branches_output),
      );

      template_data.insert(
        "not_found_user_task_mapping_by_task_id",
        Box::from(not_found_user_task_mapping_by_task_id),
      );

      let template_path = &config
        .merge_feature_branches
        .unwrap_or(Default::default())
        .output_template_path
        .unwrap();

      let template: Cow<[u8]> =
        asset::Asset::get(template_path).ok_or(anyhow!("Cannot get template {}", template_path))?;
      let mut template_buffer = template.as_ref();

      let output: String =
        HandlebarsRenderer::new().render_from_template_path(&mut template_buffer, template_data)?;

      println!("{}", output);
    }

    return Ok(());
  }
}