use crate::error::{AzureError, Result};
use serde::de::DeserializeOwned;
use std::process::{Command, Stdio};
pub async fn execute_az_command<T>(args: &[&str]) -> Result<T>
where
T: DeserializeOwned,
{
let output = execute_az_command_raw(args).await?;
if output.is_empty() {
return Err(AzureError::CliExecution {
command: format!("az {}", args.join(" ")),
error: "Empty output".to_string(),
});
}
let parsed: T = serde_json::from_str(&output)?;
Ok(parsed)
}
pub async fn execute_az_command_raw(args: &[&str]) -> Result<String> {
let mut command = Command::new("az");
command
.args(args)
.arg("--output")
.arg("json")
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let output = command.output()?;
if !output.status.success() {
return Err(AzureError::from_command_output(
&format!("az {}", args.join(" ")),
output,
));
}
let stdout = String::from_utf8_lossy(&output.stdout);
Ok(stdout.to_string())
}
pub async fn check_az_cli() -> Result<()> {
let mut command = Command::new("az");
command
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null());
match command.output() {
Ok(output) => {
if output.status.success() {
Ok(())
} else {
Err(AzureError::CliNotFound)
}
}
Err(_) => Err(AzureError::CliNotFound),
}
}
pub async fn check_authentication() -> Result<()> {
let result = execute_az_command_raw(&["account", "show"]).await;
match result {
Ok(_) => Ok(()),
Err(AzureError::CliError { stderr, .. }) if stderr.contains("az login") => {
Err(AzureError::Authentication)
}
Err(e) => Err(e),
}
}
pub fn format_optional_param(param: &str, value: &Option<String>) -> Vec<String> {
match value {
Some(v) => vec![param.to_string(), v.clone()],
None => vec![],
}
}
pub fn format_bool_param(param: &str, value: Option<bool>) -> Vec<String> {
match value {
Some(true) => vec![param.to_string()],
Some(false) => vec![format!("{}=false", param)],
None => vec![],
}
}
pub struct AzCommandBuilder {
args: Vec<String>,
}
impl AzCommandBuilder {
pub fn new() -> Self {
Self { args: Vec::new() }
}
pub fn subcommand(mut self, cmd: &str) -> Self {
self.args.push(cmd.to_string());
self
}
pub fn param(mut self, param: &str, value: &str) -> Self {
self.args.push(param.to_string());
self.args.push(value.to_string());
self
}
pub fn optional_param(mut self, param: &str, value: &Option<String>) -> Self {
if let Some(v) = value {
self.args.push(param.to_string());
self.args.push(v.clone());
}
self
}
pub fn flag(mut self, flag: &str) -> Self {
self.args.push(flag.to_string());
self
}
pub fn conditional_flag(mut self, flag: &str, condition: bool) -> Self {
if condition {
self.args.push(flag.to_string());
}
self
}
pub fn subscription(mut self, subscription: Option<&str>) -> Self {
if let Some(sub) = subscription {
self.args.push("--subscription".to_string());
self.args.push(sub.to_string());
}
self
}
pub fn build(self) -> Vec<String> {
self.args
}
pub async fn execute<T>(self) -> Result<T>
where
T: DeserializeOwned,
{
let args: Vec<&str> = self.args.iter().map(|s| s.as_str()).collect();
execute_az_command(&args).await
}
pub async fn execute_raw(self) -> Result<String> {
let args: Vec<&str> = self.args.iter().map(|s| s.as_str()).collect();
execute_az_command_raw(&args).await
}
}
impl Default for AzCommandBuilder {
fn default() -> Self {
Self::new()
}
}