//! 生成对应架构的链接脚本

use std::{env, fmt::Debug, fs::File, path::PathBuf, result};

mod aarch64;
mod dummy;

/// 链接脚本构建器
#[derive(Debug, Default)]
pub struct MkLinker {
    pub(crate) offset: usize,
}

impl MkLinker {
    /// 链接脚本的起始偏移地址
    ///
    /// 内核标准库中一般偏移地址就是实际加载物理地址, 这样方便在汇编阶段完成内存映射
    pub fn set_start_offset(&mut self, offset: usize) {
        self.offset = offset;
    }

    /// 构建链接脚本
    ///
    /// # Errors
    /// 对应架构配置不正确时返回错误
    pub fn genlinker(self) -> Result<()> {
        let target = env::var("TARGET").map_err(|_| MlError::FsError)?;
        let out = &PathBuf::from(env::var_os("OUT_DIR").ok_or(MlError::FsError)?);
        let mut f = File::create(out.join("linker.lds")).map_err(|_| MlError::FsError)?;
        if target.starts_with("aarch64") {
            aarch64::arch_write_linker(&mut f, &self)?;
        } else {
            dummy::arch_write_linker(&mut f, &self)?;
        }

        println!("cargo:rustc-link-search={}", out.display());
        println!("cargo:rustc-link-arg=-Tlinker.lds");
        Ok(())
    }
}

/// 构建链接脚本结果
pub type Result<T> = result::Result<T, MlError>;

/// 构建链接脚本错误
pub enum MlError {
    /// 未知错误
    Unknown,
    /// 未实现
    UnImpl,
    /// 本机文件系统错误
    FsError,
    /// 环境变量错误
    EnvError,
}

impl Debug for MlError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let error = match self {
            Self::Unknown => "未知错误",
            Self::UnImpl => "未实现",
            Self::FsError => "本机文件系统错误",
            Self::EnvError => "环境变量错误",
        };
        write!(f, "{error}")
    }
}
