use crate::error::DeploymentError;
use foundation_core::valtron::{from_future, StreamIterator, StreamIteratorExt};
use std::path::PathBuf;
use std::process::Command;
use tracing::{debug, info};
pub fn fetch_standard_spec(
provider_name: &str,
spec_url: &str,
output_dir: PathBuf,
) -> Result<
impl StreamIterator<D = Result<PathBuf, DeploymentError>, P = ()> + Send + 'static,
DeploymentError,
> {
let provider_name = provider_name.to_string();
let spec_url = spec_url.to_string();
let future = async move {
info!("Fetching {} OpenAPI spec from {}", provider_name, spec_url);
std::fs::create_dir_all(&output_dir).map_err(|e| {
DeploymentError::IoError(std::io::Error::other(format!(
"Failed to create output directory: {e}"
)))
})?;
let output_path = output_dir.join("openapi.json");
let output = Command::new("curl")
.args(["-s", "-o"])
.arg(&output_path)
.arg(&spec_url)
.output()
.map_err(|e| DeploymentError::ProcessFailed {
command: format!("curl -o {} {}", output_path.display(), spec_url),
exit_code: None,
stdout: String::new(),
stderr: format!("curl execution failed: {e}"),
})?;
if !output.status.success() {
return Err(DeploymentError::ProcessFailed {
command: format!("curl -o {} {}", output_path.display(), spec_url),
exit_code: output.status.code(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
});
}
let content = std::fs::read_to_string(&output_path).map_err(|e| {
DeploymentError::IoError(std::io::Error::other(format!(
"Failed to read fetched spec: {e}"
)))
})?;
let spec: serde_json::Value =
serde_json::from_str(&content).map_err(|e| DeploymentError::ConfigInvalid {
file: format!("{provider_name} OpenAPI spec"),
reason: format!("Invalid JSON: {e}"),
})?;
debug!(
"Successfully wrote {} spec to {:?}",
provider_name, output_path
);
let version = crate::providers::openapi::extract_version(&spec)
.unwrap_or_else(|| "unknown".to_string());
info!(
"Successfully fetched {} spec (version: {}) to {:?}",
provider_name, version, output_path
);
Ok(output_path)
};
let task = from_future(future);
let stream = foundation_core::valtron::execute(task, None)
.map_err(|e| DeploymentError::Generic(format!("Valtron scheduling failed: {e}")))?;
Ok(stream.map_pending(|_| ()))
}