use crate::{AnsibleVault, AnsibleError};
use crate::command_config::CommandConfig;
use crate::async_support::{AsyncResult, AsyncExecute, IntoAsync, FromAsync};
use std::ffi::OsStr;
use std::fmt::Display;
use tokio::process::Command;
#[derive(Debug, Clone)]
pub struct AsyncAnsibleVault {
command: String,
cfg: CommandConfig,
vault_id: Option<String>,
vault_password_file: Option<String>,
}
impl Default for AsyncAnsibleVault {
fn default() -> Self {
Self {
command: "ansible-vault".to_string(),
cfg: CommandConfig::default(),
vault_id: None,
vault_password_file: None,
}
}
}
impl AsyncAnsibleVault {
pub fn new() -> Self {
Self::default()
}
pub fn set_system_envs(&mut self) -> &mut Self {
self.cfg.set_system_envs();
self
}
pub fn filter_envs<T, S>(&mut self, iter: T) -> &mut Self
where
T: IntoIterator<Item = S>,
S: AsRef<OsStr> + Display,
{
self.cfg.filter_envs(iter);
self
}
pub fn add_env(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.cfg.add_env(key, value);
self
}
pub fn arg<S: AsRef<OsStr> + Display>(&mut self, arg: S) -> &mut Self {
self.cfg.arg(arg);
self
}
pub fn set_vault_id(&mut self, vault_id: impl Into<String>) -> &mut Self {
self.vault_id = Some(vault_id.into());
self
}
pub fn set_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
self.vault_password_file = Some(file_path.into());
self
}
pub fn set_new_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
self.arg("--new-vault-password-file").arg(file_path.into());
self
}
pub async fn encrypt(&self, file_path: impl AsRef<str>) -> AsyncResult<String> {
self.execute_vault_command(&["encrypt", file_path.as_ref()]).await
}
pub async fn decrypt(&self, file_path: impl AsRef<str>) -> AsyncResult<String> {
self.execute_vault_command(&["decrypt", file_path.as_ref()]).await
}
pub async fn view(&self, file_path: impl AsRef<str>) -> AsyncResult<String> {
self.execute_vault_command(&["view", file_path.as_ref()]).await
}
pub async fn edit(&self, file_path: impl AsRef<str>) -> AsyncResult<String> {
self.execute_vault_command(&["edit", file_path.as_ref()]).await
}
pub async fn rekey(&self, file_path: impl AsRef<str>) -> AsyncResult<String> {
self.execute_vault_command(&["rekey", file_path.as_ref()]).await
}
pub async fn encrypt_string(&self, string_to_encrypt: impl AsRef<str>) -> AsyncResult<String> {
let mut cmd = Command::new(&self.command);
cmd.envs(&self.cfg.envs);
cmd.args(&self.cfg.args);
cmd.arg("encrypt_string");
if let Some(ref vault_id) = self.vault_id {
cmd.arg("--vault-id").arg(vault_id);
}
if let Some(ref password_file) = self.vault_password_file {
cmd.arg("--vault-password-file").arg(password_file);
}
cmd.arg(string_to_encrypt.as_ref());
let output = cmd.output().await?;
if !output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
return Err(AnsibleError::command_failed(
"Ansible vault encrypt_string failed",
output.status.code(),
Some(stdout),
Some(stderr),
));
}
let result = String::from_utf8_lossy(&output.stdout);
Ok(result.to_string())
}
async fn execute_vault_command(&self, args: &[&str]) -> AsyncResult<String> {
let mut cmd = Command::new(&self.command);
cmd.envs(&self.cfg.envs);
cmd.args(&self.cfg.args);
cmd.args(args);
if let Some(ref vault_id) = self.vault_id {
cmd.arg("--vault-id").arg(vault_id);
}
if let Some(ref password_file) = self.vault_password_file {
cmd.arg("--vault-password-file").arg(password_file);
}
let output = cmd.output().await?;
if !output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
return Err(AnsibleError::command_failed(
"Ansible vault command failed",
output.status.code(),
Some(stdout),
Some(stderr),
));
}
let result = [output.stdout, "\n".as_bytes().to_vec(), output.stderr].concat();
let s = String::from_utf8_lossy(&result);
Ok(s.to_string())
}
}
impl AsyncExecute for AsyncAnsibleVault {
async fn execute_async(&self) -> AsyncResult<String> {
self.execute_vault_command(&["--version"]).await
}
}
impl IntoAsync<AsyncAnsibleVault> for AnsibleVault {
fn into_async(self) -> AsyncAnsibleVault {
AsyncAnsibleVault {
command: self.command,
cfg: self.cfg,
vault_id: self.vault_id,
vault_password_file: self.vault_password_file,
}
}
}
impl FromAsync<AsyncAnsibleVault> for AnsibleVault {
fn from_async(async_vault: AsyncAnsibleVault) -> Self {
AnsibleVault {
command: async_vault.command,
cfg: async_vault.cfg,
vault_id: async_vault.vault_id,
vault_password_file: async_vault.vault_password_file,
}
}
}
impl std::fmt::Display for AsyncAnsibleVault {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command)
}
}