prustio 1.0.2

The Rust embedded project management.
//! Wrapper for the GNU AVR Toolchain.
//!
//! This module provides functions to interface with the AVR utilities
//! that are downloaded and managed internally by PlatformIO. 
//! It handles executing these binaries to perform tasks like 
//! converting compiled executables into flashable formats.

use std::{path::PathBuf, process::Command};

use crate::wrapper::platformio;

const PIO_AVR_PACKAGE: &str = "toolchain-atmelavr";
const BINARY_DIRECTORY: &str = "bin";
const OBJ_COPY_BINARY_NAME: &str = "avr-objcopy";

pub const GCC_BINARY_NAME: &str = "avr-gcc";

/// Converts a compiled ELF executable into an Intel HEX binary.
///
/// This function invokes the `avr-objcopy` tool to extract the compiled code 
/// and data from the `.elf` file and format it into a `.hex` file.
///
/// # Arguments
/// * `elf_file_path` - The path to the ELF file generated by Cargo.
/// * `hex_file_path` - The path where the HEX file should be saved.
///
/// # Errors
/// Returns an error string if:
/// * The `avr-objcopy` binary cannot be located or downloaded.
/// * The command fails to execute.
/// * The `avr-objcopy` process returns a non-zero exit status.
pub fn elf_to_hex(elf_file_path: &PathBuf, hex_file_path: &PathBuf) -> Result<(), String>{
    let bin_path = obtain_bin_path(OBJ_COPY_BINARY_NAME)?;
    
    let mut cmd = Command::new(&bin_path);
    let output = cmd.arg("-O")
                    .arg("ihex")
                    .arg("-R")
                    .arg(".eeprom")
                    .arg(elf_file_path)
                    .arg(hex_file_path)
                    .output();
    match output {
        Ok(out) => {
            if !out.status.success() {
                let stderr_str = String::from_utf8_lossy(&out.stderr);
                return Err(format!("Tool avr-objcopy failed with error:\n{}", stderr_str));
            }
        },
        Err(_) => {
            return Err("Failed to run avr-objcopy tool.".to_string());
        }
    }
    Ok(())
}

/// Resolves the absolute path to a specific binary within the AVR toolchain.
///
/// If the `toolchain-atmelavr` package is not currently installed in the internal 
/// PlatformIO environment, this function will automatically instruct PlatformIO 
/// to download it before returning the path.
///
/// # Arguments
/// * `bin_name` - The name of the executable to find (e.g., "avr-gcc").
///
/// # Errors
/// Returns an error string if querying PlatformIO directories fails or if the 
/// toolchain download process fails.
pub fn obtain_bin_path(bin_name: &str) -> Result<PathBuf, String> {
    let (_, core_dir) = platformio::get_pio_dirs()?;

    let bin_path = core_dir.join("packages")
                           .join(PIO_AVR_PACKAGE)
                           .join(BINARY_DIRECTORY)
                           .join(bin_name);
    if !bin_path.exists() {
        platformio::download_pio_toolchain(PIO_AVR_PACKAGE)?;
    }

    Ok(bin_path)
}