depl 2.4.3

Toolkit for a bunch of local and remote CI/CD actions
Documentation
//! Patch action.
//!
//! YAML example:
//!
//! ```yaml
//! type: patch
//! patch: path/to/patch/file.json
//! ```
//!
//! This action calls `PatchFile::patch` from `smart-patcher` library.

use anyhow::bail;
use serde::{Deserialize, Serialize};
use smart_patcher::PatchFile;
use std::path::PathBuf;

use crate::entities::environment::RunEnvironment;

/// Patch action.
///
/// This action type allows you to patch any files, including binaries and archives, based on rules.
#[derive(Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Default, Clone)]
pub struct PatchAction {
  /// Path to patch file.
  pub patch: PathBuf,

  /// Flag to ignore patch fails.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub ignore_fails: Option<bool>,
}

impl PatchAction {
  /// Performs the patch in the run folder.
  pub async fn execute(&self, env: &RunEnvironment<'_>) -> anyhow::Result<(bool, Vec<String>)> {
    let patch_file = env.run_dir.join(&self.patch);

    let file = std::fs::File::open(patch_file)?;
    let buf = std::io::BufReader::new(file);
    let patches: PatchFile = serde_json::from_reader(buf)?;

    match patches.patch(env.run_dir, env.run_dir) {
      Err(e) => Ok((false, vec![format!("Patch application failed: {e}")])),
      Ok(0) if self.ignore_fails.is_none_or(|v| !v) => Ok((false, vec!["The patch wasn't applied! The contents of the patch are probably incorrect, or the project files have changed.".to_string()])),
      Ok(num) => Ok((
        true,
        vec![format!("The patch has been applied {num} time(s).")],
      )),
    }
  }

  /// Tests the patch in the run folder.
  pub async fn test(&self, env: &RunEnvironment<'_>) -> anyhow::Result<(bool, Vec<String>)> {
    let patch_file = env.run_dir.join(&self.patch);

    let file = std::fs::File::open(patch_file)?;
    let buf = std::io::BufReader::new(file);
    let patches: PatchFile = serde_json::from_reader(buf)?;

    match patches.test(env.run_dir, env.run_dir) {
      Err(e) => Ok((false, vec![format!("Patch test failed: {e}")])),
      Ok(0) if self.ignore_fails.is_none_or(|v| !v) => Ok((false, vec!["The patch can't be applied! The contents of the patch are probably incorrect, or the project files have changed.".to_string()])),
      Ok(num) => Ok((
        true,
        vec![format!("The patch can be applied {num} time(s).")],
      )),
    }
  }

  /// Returns an error, because we can't convert patches to shell script yet.
  pub async fn to_shell(&self, _: &RunEnvironment<'_>) -> anyhow::Result<Vec<String>> {
    bail!("`Patch` actions can't be translated into shell script!")
  }
}