regcommsgen 0.1.0

Code generator for peripherals with registers over embedded comms
Documentation
use serde::{Serialize, Deserialize};
use crate::register_spec::RegisterSpec;
use crate::endian::Endian;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PeripheralSpec {
    pub name: String,
    pub address_len: u8,
    pub byte_order: Endian,
    pub registers: Vec<RegisterSpec>,
}

impl PeripheralSpec {
    pub fn peripheral_struct_name(&self) -> String {
        stringcase::pascal_case(&self.name)
    }

    pub fn peripheral_mod_name(&self) -> String {
        stringcase::snake_case(&self.name)
    }

    pub fn endian(&self) -> Endian {
        self.byte_order
    }

    pub fn address_word_size(&self) -> u8 {
        match self.address_len {
            1 => 1,
            2 => 2,
            4 => 4,
            8 => 8,
            _ => panic!("Invalid word size from address_word_size"),
        }

    }

    pub fn address_word_name(&self) -> &'static str {
        match self.address_word_size() {
            1 => "u8",
            2 => "u16",
            4 => "u32",
            8 => "u64",
            _ => panic!("Invalid word size from address_word_size"),
        }
    }

    pub fn regcomms_params(&self) -> String {
        format!("<{}, {}>", self.address_word_size(), self.address_word_name())
    }

    pub fn generate_librs(&self) -> String {
        let mut out = String::new();
        for register in self.registers.iter() {
            out.push_str(&format!("mod {};\n", register.reg_mod_name()));
        }
        out.push_str(&format!("use regcomms::{{RegComms, RegCommsError}};\n"));
        out.push_str(&format!("pub enum AccessProc {{\n"));
        out.push_str(&format!("    Standard,\n"));
        out.push_str(&format!("}}\n"));
        out.push_str(&format!("pub struct {}<C: RegComms<{}, {}>>(pub C);\n", self.peripheral_struct_name(), self.address_word_size(), self.address_word_name()));
        out.push_str(&format!("impl<C: RegComms{}> {}<C> {{\n", self.regcomms_params(), self.peripheral_struct_name()));
        out.push_str(&format!("    pub fn comms_read(&mut self, reg_address: {}, buf: &mut [u8], _access_proc: AccessProc) -> Result<(), RegCommsError> {{\n", self.address_word_name()));
        out.push_str(&format!("        self.0.comms_read(reg_address, buf)\n"));
        out.push_str(&format!("    }}\n"));
        out.push_str(&format!("    pub fn comms_write(&mut self, reg_address: {}, buf: &[u8], _access_proc: AccessProc) -> Result<(), RegCommsError> {{\n", self.address_word_name()));
        out.push_str(&format!("        self.0.comms_write(reg_address, buf)\n"));
        out.push_str(&format!("    }}\n"));
        out.push_str(&format!("    pub async fn comms_read_async(&mut self, reg_address: {}, buf: &mut [u8], _access_proc: AccessProc) -> Result<(), RegCommsError> {{\n", self.address_word_name()));
        out.push_str(&format!("        self.0.comms_read_async(reg_address, buf).await\n"));
        out.push_str(&format!("    }}\n"));
        out.push_str(&format!("    pub async fn comms_write_async(&mut self, reg_address: {}, buf: &[u8], _access_proc: AccessProc) -> Result<(), RegCommsError> {{\n", self.address_word_name()));
        out.push_str(&format!("        self.0.comms_write_async(reg_address, buf).await\n"));
        out.push_str(&format!("    }}\n"));

        for reg in self.registers.iter() {
            out.push_str(&format!("    pub fn {}<'a>(&'a mut self) -> {}::{}<'a, C> {{\n", reg.reg_method_name(), reg.reg_mod_name(), reg.reg_struct_name()));
            out.push_str(&format!("        {}::{}(self)\n", reg.reg_mod_name(), reg.reg_struct_name()));
            out.push_str(&format!("    }}\n"));
        }
        out.push_str(&format!("}}\n"));
        out
    }

    pub fn generate_module(&self) -> Vec<(String, String)> {
        let mut out = Vec::new();
        out.push((String::from("lib.rs"), self.generate_librs()));
        for register in self.registers.iter() {
            let register_source = register.generate_file(&self);
            let register_source_name = format!("{}.rs", register.reg_mod_name());
            out.push((register_source_name, register_source));
        }
        out
    }

    pub fn generate_cargo_toml(&self, regcomms_override: Option<String>) -> String {
        let mut out = String::new();
        out.push_str(&format!("[package]\n"));
        out.push_str(&format!("name = \"{}\"\n", self.peripheral_mod_name()));
        out.push_str(&format!("edition = \"2024\"\n"));
        out.push_str(&format!("version = \"0.1.0\"\n\n"));
        out.push_str(&format!("[dependencies]\n"));
        let rc_configs = regcomms_override.unwrap_or("{{ }}".to_string());
        out.push_str(&format!("regcomms = {}\n", rc_configs));
        out
    }
}