use crate::container::Container;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RttiVersion {
V1,
V2,
}
impl fmt::Display for RttiVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::V1 => "V1",
Self::V2 => "V2",
})
}
}
#[derive(Debug, Clone)]
pub struct RttiSymbol {
pub version: RttiVersion,
pub symbol_name: String,
pub address: u64,
pub type_fragment: Option<String>,
}
pub fn scan(container: &Container<'_>) -> Vec<RttiSymbol> {
let mut result = Vec::new();
for sym in container.symbols() {
let name = sym.name.as_ref();
if !name.ends_with('_') {
continue;
}
if let Some(_inner) = name.strip_prefix("NTIv2") {
result.push(RttiSymbol {
version: RttiVersion::V2,
symbol_name: name.to_string(),
address: sym.vm_addr,
type_fragment: None,
});
} else if let Some(inner) = name.strip_prefix("NTI") {
let body = inner.strip_suffix('_').unwrap_or(inner);
let type_fragment = extract_v1_type_fragment(body).map(|s| s.to_string());
result.push(RttiSymbol {
version: RttiVersion::V1,
symbol_name: name.to_string(),
address: sym.vm_addr,
type_fragment,
});
}
}
result
}
fn extract_v1_type_fragment(body: &str) -> Option<&str> {
if let Some(pos) = body.find("__") {
let frag = &body[..pos];
if !frag.is_empty() {
return Some(frag);
}
}
if !body.is_empty() { Some(body) } else { None }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extract_v1_fragment_with_hash() {
assert_eq!(
extract_v1_type_fragment("seqLintT__abc123"),
Some("seqLintT")
);
}
#[test]
fn extract_v1_fragment_no_hash() {
assert_eq!(extract_v1_type_fragment("int"), Some("int"));
}
#[test]
fn extract_v1_fragment_empty() {
assert_eq!(extract_v1_type_fragment(""), None);
}
}