use crate::cli::Args;
use crate::error::ConversionError;
use crate::openssl::{PemFormatter, PfxParser};
use crate::output::{OutputConfig, OutputHandler, ProgressReporter};
use std::fs;
use std::path::Path;
pub fn convert_pfx_to_pem(args: Args) -> Result<(), Box<dyn std::error::Error>> {
let output_config = OutputConfig::from_args(&args);
let mut output = OutputHandler::new(output_config.clone());
let progress = ProgressReporter::new(&output_config);
progress.start_conversion();
output.info("Starting PFX to PEM conversion...")?;
output.info(&format!("Input file: {}", args.pfx))?;
let output_dir = args.output_dir();
progress.reading_file(&args.pfx);
fs::create_dir_all(output_dir).map_err(|e| {
progress.error(&format!("Failed to create output directory: {}", e));
ConversionError::DirectoryCreation(output_dir.to_string(), e)
})?;
output.info(&format!("Output directory: {}", output_dir))?;
output.status("Converting PFX to PEM format...")?;
progress.parsing();
let parsed = PfxParser::parse_file(&args.pfx, args.password()).map_err(|e| {
progress.error(&format!("Failed to parse PFX: {}", e));
e
})?;
output.info("Successfully parsed PFX file")?;
if output_config.verbose {
output.info("Certificate information:")?;
let cert_info_text = parsed.cert_info();
for line in cert_info_text.lines() {
output.info(line)?;
}
}
output.print_cert_info(&parsed)?;
if parsed.has_chain() {
output.info(&format!(
"Found {} additional certificates in chain",
parsed.chain_length()
))?;
}
let key_path = Path::new(output_dir).join(args.key_filename());
let cert_path = Path::new(output_dir).join(args.cert_filename());
let combined_path = Path::new(output_dir).join(args.combined_filename());
progress.extracting_key();
let private_key_pem = PemFormatter::private_key_to_pem(&parsed)?;
write_file(&key_path, &private_key_pem)?;
output.success(&format!("Private key saved to: {}", key_path.display()))?;
progress.extracting_cert();
let cert_pem = PemFormatter::certificate_to_pem(&parsed)?;
write_file(&cert_path, &cert_pem)?;
output.success(&format!("Certificate saved to: {}", cert_path.display()))?;
if args.chain && parsed.has_chain() {
progress.extracting_chain(parsed.chain_length());
save_certificate_chain(&parsed, output_dir, &mut output)?;
}
if args.combined {
progress.writing_files();
let combined_pem = PemFormatter::combined_to_pem(&parsed, args.chain)?;
write_file(&combined_path, &combined_pem)?;
output.success(&format!(
"Combined PEM saved to: {}",
combined_path.display()
))?;
}
progress.complete();
output.print_summary(&args, &parsed)?;
Ok(())
}
fn save_certificate_chain(
parsed: &crate::openssl::ParsedPfx,
output_dir: &str,
output: &mut OutputHandler,
) -> Result<(), ConversionError> {
let chain_pem = PemFormatter::chain_to_pem(parsed)?;
let chain_path = Path::new(output_dir).join("certificate_chain.pem");
write_file(&chain_path, &chain_pem)?;
output
.success(&format!(
"Certificate chain saved to: {}",
chain_path.display()
))
.map_err(|e| ConversionError::FileWrite("output".to_string(), e))?;
let chain_certs_pem = PemFormatter::chain_certs_to_pem(parsed)?;
for (i, cert_pem) in chain_certs_pem.iter().enumerate() {
let cert_path = Path::new(output_dir).join(format!("chain_cert_{}.pem", i + 1));
write_file(&cert_path, cert_pem)?;
output
.info(&format!(
"Chain certificate {} saved to: {}",
i + 1,
cert_path.display()
))
.map_err(|e| ConversionError::FileWrite("output".to_string(), e))?;
}
Ok(())
}
fn write_file<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<(), ConversionError> {
let path = path.as_ref();
fs::write(path, data).map_err(|e| ConversionError::FileWrite(path.display().to_string(), e))
}