cplus_demangle/
lib.rs

1//! # cplus_demangle
2//! [![crates.io](https://img.shields.io/crates/v/cplus_demangle)](https://crates.io/crates/cplus_demangle) [![docs.rs](https://docs.rs/cplus_demangle/badge.svg)](https://docs.rs/cplus_demangle/latest/cplus_demangle/)
3//!
4//! This library converts C++ mangled symbol names to human-readable strings. It is a safe Rust wrapper to GNU libiberty's C function `cplus_demangle`.
5//!
6//! ## Example
7//! Suppose you compile the following C++ program:
8//! ```cpp
9//! namespace test {
10//!   void myfn(int x) { }
11//! }
12//! ```
13//!
14//! In the resulting binary, the symbol that gets generated for `myfn` is `_ZN4test4myfnEi`. We can convert it back with this Rust code:
15//! ```rust
16//! assert_eq!(cplus_demangle::demangle("_ZN4test4myfnEi").unwrap(), "test::myfn(int)");
17//! ```
18
19use libc::{c_char, c_int};
20use std::ffi::{CStr, CString};
21
22extern "C" {
23    fn cplus_demangle_wrapper(
24        mangled_name: *const c_char,
25        show_params: c_int,
26        show_ansi: c_int,
27    ) -> *mut c_char;
28}
29
30#[derive(Debug)]
31pub struct Error(&'static str);
32
33/// Description of options from demangle.h:
34/// ```
35/// #define DMGL_PARAMS      (1 << 0)       /* Include function args */
36/// #define DMGL_ANSI        (1 << 1)       /* Include const, volatile, etc */
37/// ```
38pub struct Options {
39    pub show_params: bool,
40    pub show_ansi: bool,
41}
42
43impl Options {
44    pub fn default() -> Options {
45        Options {
46            show_params: true,
47            show_ansi: true,
48        }
49    }
50}
51
52/// Demangle the given name, with default options.
53pub fn demangle(mangled_name: &str) -> Result<String, Error> {
54    demangle_with_options(mangled_name, Options::default())
55}
56
57/// Demangle the given name with the specified options.
58///
59/// Fails if: the name contains a null character, or demangling fails.
60pub fn demangle_with_options(mangled_name: &str, options: Options) -> Result<String, Error> {
61    let mangled_name = match CString::new(mangled_name) {
62        Ok(mangled_name) => mangled_name,
63        Err(std::ffi::NulError { .. }) => return Err(Error("mangled_name contains null")),
64    };
65    let result: *mut c_char = unsafe {
66        cplus_demangle_wrapper(
67            mangled_name.as_ptr(),
68            options.show_params as i32,
69            options.show_ansi as i32,
70        )
71    };
72    if result.is_null() {
73        // Unfortunately cplus_demangle appears to give us precisely 0 helpful
74        // error info, we have to go with a generic message.
75        return Err(Error("Failed to demangle"));
76    }
77    let demangled = unsafe { CStr::from_ptr(result) };
78    Ok(demangled.to_str().unwrap().to_owned())
79}
80
81#[cfg(test)]
82mod tests {
83    #[test]
84    fn it_works() {
85        assert_eq!(
86            crate::demangle("_ZNK5boost16cpp_regex_traitsIcE7isctypeEcj").unwrap(),
87            "boost::cpp_regex_traits<char>::isctype(char, unsigned int) const"
88        );
89        assert_eq!(
90            crate::demangle_with_options(
91                "_ZNK5boost16cpp_regex_traitsIcE7isctypeEcj",
92                crate::Options {
93                    show_params: false,
94                    show_ansi: true,
95                }
96            )
97            .unwrap(),
98            "boost::cpp_regex_traits<char>::isctype"
99        );
100    }
101}