#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use std::{env::current_exe, error::Error, fmt, fs};
mod symbolic_object;
use findshlibs::SharedLibrary;
use symbolic_debuginfo::Function;
use symbolic_demangle::{Demangle, DemangleOptions};
pub use findshlibs::IterationControl;
pub fn declaration_of<T>(_: &T) -> Option<DeclarationSite> {
declaration::<T>()
}
pub fn declaration<T>() -> Option<DeclarationSite> {
declaration_by_name(core::any::type_name::<T>())
}
pub fn declaration_by_name(name: &str) -> Option<DeclarationSite> {
let mut result = None;
for_some_currently_loaded_rust_functions(|demangled_name, function| {
if demangled_name == name {
result = (&function).try_into().ok();
IterationControl::Break
} else {
IterationControl::Continue
}
});
result
}
pub fn for_some_currently_loaded_rust_functions<C>(mut callback: impl FnMut(String, Function) -> C)
where
C: Into<IterationControl>,
{
let mut libraries = vec![];
findshlibs::TargetSharedLibrary::each(|library| {
libraries.push((
library.name().to_owned(),
library.debug_name().map(ToOwned::to_owned),
));
});
for (library_path, debug_path) in libraries {
let path = if let Some(debug_path) = debug_path {
debug_path.into()
} else if library_path.len() == 0 {
match current_exe() {
Ok(it) => it,
Err(_) => continue,
}
} else {
library_path.into()
};
let file_data = match fs::read(path) {
Ok(it) => it,
_ => continue,
};
let archive = match symbolic_object::Archive::parse(&file_data) {
Ok(it) => it,
Err(_) => continue,
};
for object in archive.objects() {
let object = match object {
Ok(it) => it,
Err(_) => continue,
};
let session = match object.debug_session() {
Ok(it) => it,
Err(_) => continue,
};
for function in session.functions() {
if let Ok(function) = function {
if let Some(demangled_name) =
function.name.demangle(DemangleOptions::name_only())
{
match callback(demangled_name, function).into() {
IterationControl::Break => return,
IterationControl::Continue => (),
}
}
}
}
}
}
}
pub struct DeclarationSite {
pub file: String,
pub line: u32,
}
impl fmt::Display for DeclarationSite {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}:{}", self.file, self.line)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum DeclarationSiteError {
MissingLines,
}
impl fmt::Display for DeclarationSiteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeclarationSiteError::MissingLines => write!(
f,
"debug info contains no source locations for this function"
),
}
}
}
impl Error for DeclarationSiteError {}
impl<'a> TryFrom<&Function<'a>> for DeclarationSite {
type Error = DeclarationSiteError;
fn try_from(value: &Function<'a>) -> Result<Self, Self::Error> {
let line = &value
.lines
.get(0)
.ok_or(DeclarationSiteError::MissingLines)?;
let file = line.file.path_str();
Ok(DeclarationSite {
file,
line: line.line as u32,
})
}
}