releez 1.0.3

An utility tool to run application release-checklist safely.
Documentation
use crate::constants;
use crate::out;
use crate::prelude::*;
use crate::types::{HaltConfig, LastCheckedConfig, ReleaseConfig};
use chrono::offset::Local;
use colored::*;
use semver::Version;
use std::collections::HashMap;
use std::path::Path;
use tokio::fs;

pub async fn calculate_start_task_ids(release_config: &ReleaseConfig, halt_config: Option<&HaltConfig>) -> (u64, u64) {
    let mut last_checked_task_ids: Option<(u64, u64)> = None;

    if let Some(halt_config) = halt_config {
        if halt_config.version == release_config.version
            && ask_user_to_continue_with_halt(release_config, halt_config)
                .await
                .unwrap_or(false)
        {
            last_checked_task_ids = Some((halt_config.last_checked.task_id, halt_config.last_checked.sub_task_id));
        }
    }

    let (last_checked_task_id, last_checked_sub_task_id) = match last_checked_task_ids {
        Some(ids) => ids,
        None => return (0, 0),
    };

    if let Some(last_checked_task) = release_config.checklist.get(last_checked_task_id as usize) {
        if (last_checked_sub_task_id + 1) < (last_checked_task.sub_tasks().len() as u64) {
            (last_checked_task_id, last_checked_sub_task_id + 1)
        } else {
            (last_checked_task_id + 1, 0)
        }
    } else {
        (0, 0)
    }
}

pub async fn ask_confirmation(message: &str, default_val: &str, padding: &str) -> crate::Result<bool> {
    out::print(format!(
        "{}{} {} ({}){}",
        padding,
        "?".cyan(),
        message.trim(),
        default_val,
        "".bright_black(),
    ))
    .await?;

    let mut line = out::read_line().await?;

    if line.is_empty() {
        line = default_val.to_owned();
    }

    Ok(constants::BOOL_POSSIBLE_TRUTHY_INPUTS.contains(&line.to_lowercase().as_str()))
}

pub async fn ask_user_to_continue_with_halt(
    release_config: &ReleaseConfig,
    halt_config: &HaltConfig,
) -> crate::Result<bool> {
    out::print("\n").await?;

    out::print(format!("Previous release was not completed properly.\n")).await?;

    let (last_checked_task_name, is_partial_completion) = release_config
        .checklist
        .get(halt_config.last_checked.task_id as usize)
        .map(|t| {
            (
                t.name.as_str(),
                halt_config.last_checked.sub_task_id < ((t.sub_tasks().len() as u64) - 1),
            )
        })
        .unwrap_or(("na", false));

    if is_partial_completion {
        out::print(format!(
            "Last partially checked task: '{}'.\n",
            last_checked_task_name.cyan()
        ))
        .await?;
    } else {
        out::print(format!("Last checked task: '{}'.\n", last_checked_task_name.cyan())).await?;
    }

    let confirmation = ask_confirmation("Do you want to resume the release?", "yes", "").await?;

    Ok(confirmation)
}

pub fn gen_vars_data(release_version: &Version) -> HashMap<String, String> {
    let mut h = HashMap::new();
    h.insert(constants::VAR_NAME_VERSION.to_owned(), release_version.to_string());
    h
}

pub async fn save_last_checked(
    release_config: &ReleaseConfig,
    last_checked_task_id: u64,
    last_checked_sub_task_id: u64,
    root_dir: &Path,
) -> crate::Result<()> {
    let halt_config = HaltConfig {
        version: release_config.version.clone(),
        last_checked: LastCheckedConfig {
            task_id: last_checked_task_id,
            sub_task_id: last_checked_sub_task_id,
            date: Local::now().timestamp_millis(),
        },
    };

    let data = serde_yaml::to_vec(&halt_config).context("Couldn't convert halt config to yaml")?;

    fs::write(root_dir.join(constants::HALT_CONFIG_FILE_NAME), data)
        .await
        .context(format!(
            "Couldn't write task's checked state to halt config file: {}",
            constants::HALT_CONFIG_FILE_NAME
        ))?;

    Ok(())
}