如何使用 Rust 和 clang 将 C++ 结构体字段导出为 JSON
在我们之前的文章如何使用 clang 和 boost::json 将 C++ 结构体字段导出为 JSON 中,我们讨论了如何将 libclang 与 boost::json 结合使用,解析 C++ 源文件并将结构体定义导出为 JSON。
在本文中,我们将使用 Rust 和 clang crate 来实现同样的目标。
src/main.rs
use clang::{Clang, Entity, EntityKind, Index};
use serde_json::{json, Value};
use std::env;
use std::fs;
use std::path::Path;
fn extract_struct_fields(entity: &Entity) -> Option<Value> {
if entity.get_kind() != EntityKind::StructDecl {
return None;
}
// 结构体名称(匿名结构体可能为 None)
let struct_name = entity.get_name().unwrap_or_default();
// 收集字段并维护递增索引
let mut index = 0usize;
let mut fields = vec![];
entity.visit_children(|child, _parent| {
if child.get_kind() == EntityKind::FieldDecl {
let field_name = child.get_name().unwrap_or_default();
let field_type = child
.get_type()
.map(|t| t.get_display_name())
.unwrap_or_default();
fields.push(json!({
"index": index,
"name": field_name,
"type": field_type,
}));
index += 1;
}
clang::EntityVisitResult::Continue
});
Some(json!({
"name": struct_name,
"fields": fields
}))
}
fn main() {
// --- 参数与文件检查(与 C++ 版本保持一致) ---
let mut args = env::args();
let exe = args.next().unwrap_or_else(|| "program".into());
let Some(filename) = args.next() else {
eprintln!("Usage: {} <source file>", exe);
std::process::exit(1);
};
let path = Path::new(&filename);
if !path.exists() {
eprintln!("File does not exist: {}", filename);
std::process::exit(1);
}
let code = match fs::read_to_string(&path) {
Ok(s) => s,
Err(_) => {
eprintln!("Error opening file: {}", filename);
std::process::exit(1);
}
};
if code.is_empty() {
eprintln!("File is empty: {}", filename);
std::process::exit(1);
}
// --- libclang 初始化,并以名为 "test.cpp" 的未保存文件进行解析 ---
let clang = Clang::new().expect("Failed to load libclang");
let index = Index::new(&clang, /*exclude_decls_from_pch=*/ false, /*display_diagnostics=*/ false);
let unsaved = clang::Unsaved::new("test.cpp", &code);
// 无需特殊参数(与 CXTranslationUnit_None 保持一致)
let tu = match index.parser("test.cpp").unsaved(&[unsaved]).parse() {
Ok(tu) => tu,
Err(_) => {
eprintln!("Failed to parse translation unit.");
std::process::exit(1);
}
};
// --- 遍历翻译单元并收集结构体 ---
let root = tu.get_entity();
let mut structs = vec![];
root.visit_children(|child, _parent| {
if let Some(struct_json) = extract_struct_fields(&child) {
structs.push(struct_json);
}
clang::EntityVisitResult::Continue
});
// --- 输出与 C++ 版本结构相同的 JSON ---
let result = json!({ "structs": structs });
println!("{}", serde_json::to_string(&result).unwrap());
}Cargo.toml
[package]
name = "rust_clang_structs"
version = "0.1.0"
edition = "2021"
[dependencies]
clang = "2.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow