aboutsummaryrefslogtreecommitdiff
path: root/mingling_pathf/src/type_mapping_builder.rs
blob: 3422af86c60667a5ffb78b2b91ae9271c84a81fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use std::collections::HashSet;
use std::path::Path;

use crate::config::PathfinderConfig;
use crate::error::MinglingPathfinderError;
use crate::module_pathf;
use crate::pattern_analyzer;

/// Analyzes the Mingling types of the specified crate directory and generates mapping files to the specified output directory.
///
/// `crate_dir` — crate root directory (i.e., the directory containing Cargo.toml)
/// `output_dir` — directory where mapping files will be written
/// `config` — pathfinder configuration (e.g., dispatch_tree detection)
///
/// Mapping file format per line: `TypeName = crate::module::path::TypeName`
pub fn analyze_and_build_type_mapping_for(
    crate_dir: &Path,
    output_dir: &Path,
    config: &PathfinderConfig,
) -> Result<(), MinglingPathfinderError> {
    let module_mapping = module_pathf::analyze(crate_dir)?;
    let analyzer = pattern_analyzer::init_with_config(config.clone());

    let mut type_mappings: Vec<(String, String)> = Vec::new();

    for item in module_mapping {
        let file_abs = crate_dir.join(item.file_path());
        if !file_abs.is_file() {
            continue;
        }

        let module_path = item.module_path();
        let Ok(analyze_items) = analyzer.analyze_file_items(&file_abs) else {
            continue;
        };

        for ai in analyze_items {
            let full_path = if ai.module.is_empty() {
                format!("{}::{}", module_path, ai.item_name)
            } else {
                format!("{}::{}::{}", module_path, ai.module, ai.item_name)
            };
            type_mappings.push((ai.item_name, full_path));
        }
    }

    // Sort by full path (ASCII order)
    type_mappings.sort_by(|a, b| a.1.cmp(&b.1));

    // Deduplicate by type name, keeping the first occurrence
    let mut seen = HashSet::new();
    type_mappings.retain(|(name, _)| seen.insert(name.clone()));

    // Create output directory
    std::fs::create_dir_all(output_dir)?;

    // Write files
    let output_path = output_dir.join("MAPPING");
    let type_using_path = output_dir.join("type_using.rs");

    let mut content_mapping = String::new();
    for (name, path) in &type_mappings {
        content_mapping.push_str(&format!("{name} = {path}\n"));
    }
    std::fs::write(&output_path, content_mapping)?;

    let mut content_using = String::new();
    for (_, path) in &type_mappings {
        content_using.push_str(&format!("use {path};\n"));
    }
    std::fs::write(&type_using_path, content_using)?;

    Ok(())
}

/// Convenience version to be called from `build.rs`, automatically reading configuration
/// from environment variables.
///
/// Reads `CARGO_PKG_NAME` and `OUT_DIR`, and outputs to `{OUT_DIR}/{CARGO_PKG_NAME}/`.
pub fn analyze_and_build_type_mapping() -> Result<(), MinglingPathfinderError> {
    let crate_name = std::env::var("CARGO_PKG_NAME").map_err(|_| {
        MinglingPathfinderError::IoError(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            "CARGO_PKG_NAME not set (not running in build.rs?)",
        ))
    })?;

    let out_dir = std::env::var("OUT_DIR").map_err(|_| {
        MinglingPathfinderError::IoError(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            "OUT_DIR not set (not running in build.rs?)",
        ))
    })?;

    let crate_dir = std::env::current_dir()?;
    let output_dir = Path::new(&out_dir).join(&crate_name);

    analyze_and_build_type_mapping_for(&crate_dir, &output_dir, &PathfinderConfig::default())?;

    // Notify Cargo to re-run build.rs when source files change
    println!("cargo:rerun-if-changed=src/");
    println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_OS");
    println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH");

    Ok(())
}