extern crate cbindgen;
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let output_file = PathBuf::from(&crate_dir).join("bindings").join("aether.h");
std::fs::create_dir_all(PathBuf::from(&crate_dir).join("bindings")).ok();
cbindgen::Builder::new()
.with_crate(crate_dir.clone())
.with_language(cbindgen::Language::C)
.with_cpp_compat(true)
.with_include_guard("AETHER_H")
.with_documentation(true)
.generate()
.expect("Unable to generate C bindings")
.write_to_file(output_file);
println!("cargo:rerun-if-changed=src/ffi.rs");
validate_stdlib(&crate_dir);
}
fn validate_stdlib(crate_dir: &str) {
println!("cargo:warning=检查所有内置标准库...");
let stdlib_dir = PathBuf::from(crate_dir).join("stdlib");
if !stdlib_dir.exists() {
println!(
"cargo:warning=Standard library directory not found: {:?}",
stdlib_dir
);
return;
}
let mut file_count = 0;
let mut aether_files = Vec::new();
match fs::read_dir(&stdlib_dir) {
Ok(entries) => {
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("aether") {
file_count += 1;
println!("cargo:rerun-if-changed={}", path.display());
aether_files.push(path);
}
}
}
Err(e) => {
println!("cargo:warning=Failed to read stdlib directory: {}", e);
return;
}
}
if file_count == 0 {
println!("cargo:warning=No .aether files found in stdlib directory");
return;
}
let mut all_valid = true;
for path in &aether_files {
match validate_aether_file_basic(path) {
Ok(()) => {
}
Err(e) => {
println!(
"cargo:error=✗ {}: {}",
path.file_name().unwrap().to_string_lossy(),
e
);
all_valid = false;
}
}
}
if all_valid {
println!("cargo:warning=共 {} 个标准库文件检查成功!", file_count);
} else {
println!("cargo:error=ERROR: Some standard library files have potential issues!");
println!("cargo:error=Run 'cargo test' to verify standard library functionality.");
}
}
fn validate_aether_file_basic(path: &PathBuf) -> Result<(), String> {
let content = fs::read_to_string(path).map_err(|e| format!("Failed to read file: {}", e))?;
if content.is_empty() {
return Err("File is empty".to_string());
}
let lines: Vec<&str> = content.lines().collect();
let mut has_unclosed_braces = 0;
let mut has_unclosed_brackets = 0;
let mut has_unclosed_parens = 0;
for (line_num, line) in lines.iter().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with("//") {
continue;
}
let mut in_string = false;
let mut escape_next = false;
let mut in_multiline_string = false;
let chars: Vec<char> = line.chars().collect();
let mut i = 0;
while i < chars.len() {
let ch = chars[i];
if !in_string
&& i + 2 < chars.len()
&& chars[i] == '"'
&& chars[i + 1] == '"'
&& chars[i + 2] == '"'
{
in_multiline_string = !in_multiline_string;
i += 3;
continue;
}
if in_multiline_string {
i += 1;
continue;
}
if escape_next {
escape_next = false;
i += 1;
continue;
}
match ch {
'\\' if in_string => {
escape_next = true;
}
'"' => {
in_string = !in_string;
}
'{' if !in_string => has_unclosed_braces += 1,
'}' if !in_string => has_unclosed_braces -= 1,
'[' if !in_string => has_unclosed_brackets += 1,
']' if !in_string => has_unclosed_brackets -= 1,
'(' if !in_string => has_unclosed_parens += 1,
')' if !in_string => has_unclosed_parens -= 1,
_ => {}
}
i += 1;
}
if has_unclosed_braces < 0 {
return Err(format!(
"Line {}: Unmatched closing brace '}}' ",
line_num + 1
));
}
if has_unclosed_brackets < 0 {
return Err(format!(
"Line {}: Unmatched closing bracket ']'",
line_num + 1
));
}
if has_unclosed_parens < 0 {
return Err(format!(
"Line {}: Unmatched closing parenthesis ')'",
line_num + 1
));
}
}
if has_unclosed_braces > 0 {
return Err(format!(
"Unclosed braces: {} '{{' without matching '}}'",
has_unclosed_braces
));
}
if has_unclosed_brackets > 0 {
return Err(format!(
"Unclosed brackets: {} '[' without matching ']'",
has_unclosed_brackets
));
}
if has_unclosed_parens > 0 {
return Err(format!(
"Unclosed parentheses: {} '(' without matching ')'",
has_unclosed_parens
));
}
Ok(())
}