c2a-core 4.0.2

Core of Command Centric Architecture
Documentation
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;

use semver::Version;

use clang::{token::TokenKind::Punctuation, Clang, Index};

fn main() {
    println!("cargo:source_dir={}", env!("CARGO_MANIFEST_DIR"));

    let ver = env!("CARGO_PKG_VERSION");
    let ver = Version::parse(ver).unwrap();
    dbg!(&ver);

    let header_ver = get_core_version_from_header();
    dbg!(&header_ver);

    assert_eq!(ver, header_ver);

    bind(".".into(), "c2a_core_main.h");

    //bind("system/time_manager".into(), "time_manager.h");
    //bind("system/watchdog_timer".into(), "watchdog_timer.h");

    bind("hal".into(), "ccsds.h");
    bind("hal".into(), "i2c.h");
    bind("hal".into(), "spi.h");
    bind("hal".into(), "uart.h");
    bind("hal".into(), "wdt.h");
}

fn bind(module: PathBuf, header: &str) {
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let gen_rs = header.replace(".h", ".rs");
    let gen_rs = out_dir.join(gen_rs);

    let header_path = module.join(header);

    c2a_bind_utils::bind_c2a(header_path, gen_rs)
}

fn get_core_version_from_header() -> Version {
    let macros = get_definitions("c2a_core_main.h");

    let major: u64 = macros
        .get("C2A_CORE_VER_MAJOR")
        .expect("failed to get major ver")
        .as_ref()
        .unwrap()
        .parse()
        .expect("failed to parse as u64");
    let minor: u64 = macros
        .get("C2A_CORE_VER_MINOR")
        .expect("failed to get minor ver")
        .as_ref()
        .unwrap()
        .parse()
        .expect("failed to parse as u64");
    let patch: u64 = macros
        .get("C2A_CORE_VER_PATCH")
        .expect("failed to get patch ver")
        .as_ref()
        .unwrap()
        .parse()
        .expect("failed to parse as u64");
    let pre = macros
        .get("C2A_CORE_VER_PRE")
        .expect("failed to get pre ver")
        .as_ref()
        .unwrap();
    let pre = semver::Prerelease::new(pre).expect("failed to parse as pre release");

    Version {
        major,
        minor,
        patch,
        pre,
        build: semver::BuildMetadata::EMPTY,
    }
}

fn get_definitions(src_file: &str) -> HashMap<String, Option<String>> {
    let mut macros = HashMap::new();

    let clang = Clang::new().expect("failed to acquire clang instance");
    let index = Index::new(&clang, false, false);

    let tu = index
        .parser(src_file)
        .detailed_preprocessing_record(true)
        .parse()
        .expect("failed to parse c2a-core main header");
    let entity = tu.get_entity();

    let childlen = entity.get_children().into_iter();

    for cursor in childlen {
        match cursor.get_kind() {
            clang::EntityKind::MacroDefinition => {
                let location = cursor.get_location().unwrap().get_file_location();
                if let Some(file) = location.file {
                    let file = file.get_path();
                    let _f = file.to_str().unwrap();
                } else {
                    continue;
                }

                let name = cursor.get_display_name().unwrap();
                let mut token = cursor.get_range().unwrap().tokenize();
                token.remove(0); // remove macro Identifier token
                if token.is_empty() {
                    macros.insert(name, None);
                    continue; // remove define only
                }

                let first = token.first().unwrap();
                let last = token.last().unwrap();
                if first.get_kind() == Punctuation
                    && last.get_kind() == Punctuation
                    && first.get_spelling() == "("
                    && last.get_spelling() == ")"
                {
                    token.remove(0);
                    token.remove(token.len() - 1);
                }

                if token.len() == 1 {
                    let value = token[0].get_spelling();

                    let value = if value.starts_with('\"') && value.ends_with('\"') {
                        let value = value.strip_prefix('\"').unwrap();
                        value.strip_suffix('\"').unwrap().to_string()
                    } else {
                        value
                    };
                    macros.insert(name, Some(value));
                } else {
                    // 単純な値ではなかった(ex: 関数マクロ)
                    dbg!(token);
                }
            }
            _ => {}
        }
    }

    macros
}