depl 2.4.3

Toolkit for a bunch of local and remote CI/CD actions
Documentation
//! `AddToStorage` action module.
//!
//! Based on automatic rule to extract version from project, allows you to
//! load your artifacts as content to Deployer's storage.

use anyhow::bail;
use serde::{Deserialize, Serialize};
use std::io::Read;
use std::path::PathBuf;

use crate::bmap;
use crate::entities::auto_version::AutoVersionExtractFromRule;
use crate::entities::environment::RunEnvironment;
use crate::entities::info::ContentInfo;
use crate::entities::placements::Placement;
use crate::storage::{add_single_to_storage, add_to_storage};

/// AddToStorage Action.
#[derive(Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct AddToStorageAction {
  /// Content's short name. Used to load to and sync from storage.
  pub short_name: String,
  /// Automatic rule to extract version from project.
  pub auto_version_rule: AutoVersionExtractFromRule,
  /// What to put in the storage?
  #[serde(default, skip_serializing_if = "PutContentRule::is_default")]
  pub content: PutContentRule,
}

impl AddToStorageAction {
  /// Adds content to storage from given run environment.
  pub async fn execute(&self, env: &RunEnvironment<'_>) -> anyhow::Result<(bool, Vec<String>)> {
    let version = match &self.auto_version_rule {
      AutoVersionExtractFromRule::CmdStdout { cmd } => {
        let (succ, out) = cmd.execute(env, &bmap!()).await?;
        if !succ || out.is_empty() {
          return Ok((false, out));
        }
        out.last().unwrap().trim().replace(">>> ", "")
      }
      AutoVersionExtractFromRule::PlainFile { path } => {
        let mut file = std::fs::File::open(path)?;
        let mut ver = String::new();
        file.read_to_string(&mut ver)?;
        ver.trim().to_string()
      }
    };

    let info = ContentInfo::new(self.short_name.as_str(), version.as_str())?;

    Ok(match &self.content {
      PutContentRule::Artifacts => {
        if let Err(e) = add_to_storage(env.storage_dir, env.artifacts_dir, &info) {
          (false, vec![e.to_string()])
        } else {
          (true, vec![])
        }
      }
      PutContentRule::FixedFiles { placements } => {
        for placement in placements {
          if let Err(e) = add_single_to_storage(
            env.storage_dir,
            &env.run_dir.join(&placement.from),
            &PathBuf::from(placement.to.as_str()),
            &info,
          ) {
            return Ok((false, vec![e.to_string()]));
          }
        }
        (true, vec![])
      }
    })
  }

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

/// Put content rule.
#[derive(Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PutContentRule {
  /// Place all artifacts as content.
  #[default]
  Artifacts,
  /// Place only chosen files as content.
  FixedFiles {
    /// Chosen files and their placements.
    placements: Vec<Placement>,
  },
}

impl PutContentRule {
  fn is_default(&self) -> bool {
    matches!(self, PutContentRule::Artifacts)
  }
}