binaryninja/
qualified_name.rs

1//! The [`QualifiedName`] is the canonical way to represent structured names in Binary Ninja.
2
3use crate::rc::{CoreArrayProvider, CoreArrayProviderInner};
4use crate::string::{raw_to_string, strings_to_string_list, BnString};
5use binaryninjacore_sys::*;
6use std::borrow::Cow;
7use std::fmt::{Display, Formatter};
8use std::ops::{Index, IndexMut};
9
10/// A [`QualifiedName`] represents a name composed of multiple components, typically used for symbols
11/// and type names within namespaces, classes, or modules.
12///
13/// # Creating a Qualified Name
14///
15/// ```
16/// use binaryninja::qualified_name::QualifiedName;
17///
18/// // Uses the default separator "::"
19/// let qn_vec = QualifiedName::new(vec!["my", "namespace", "func"]);
20/// assert_eq!(qn_vec.to_string(), "my::namespace::func");
21///
22/// // Using `QualifiedName::from` will not split on the default separator "::".
23/// let qn_from = QualifiedName::from("std::string");
24/// assert_eq!(qn_from.len(), 1);
25/// assert_eq!(qn_from.to_string(), "std::string");
26/// ```
27///
28/// # Using a Custom Separator
29///
30/// While `::` is the default, you can specify a custom separator:
31///
32/// ```
33/// use binaryninja::qualified_name::QualifiedName;
34///
35/// let qn = QualifiedName::new_with_separator(["a", "b", "c"], ".");
36/// assert_eq!(qn.to_string(), "a.b.c");
37/// ```
38#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
39pub struct QualifiedName {
40    // TODO: Make this Option<String> where default is "::".
41    pub separator: String,
42    pub items: Vec<String>,
43}
44
45impl QualifiedName {
46    pub fn from_raw(value: &BNQualifiedName) -> Self {
47        // TODO: This could be improved...
48        let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) };
49        let items = raw_names
50            .iter()
51            .filter_map(|&raw_name| raw_to_string(raw_name as *const _))
52            .collect();
53        let separator = raw_to_string(value.join).unwrap();
54        Self { items, separator }
55    }
56
57    pub fn from_owned_raw(value: BNQualifiedName) -> Self {
58        let result = Self::from_raw(&value);
59        Self::free_raw(value);
60        result
61    }
62
63    pub fn into_raw(value: Self) -> BNQualifiedName {
64        let bn_join = BnString::new(&value.separator);
65        BNQualifiedName {
66            // NOTE: Leaking string list must be freed by core or us!
67            name: strings_to_string_list(&value.items),
68            // NOTE: Leaking string must be freed by core or us!
69            join: BnString::into_raw(bn_join),
70            nameCount: value.items.len(),
71        }
72    }
73
74    pub fn free_raw(value: BNQualifiedName) {
75        unsafe { BnString::free_raw(value.join) };
76        unsafe { BNFreeStringList(value.name, value.nameCount) };
77    }
78
79    /// Creates a new [`QualifiedName`] with the default separator `::`.
80    pub fn new<I, S>(items: I) -> Self
81    where
82        I: IntoIterator<Item = S>,
83        S: Into<String>,
84    {
85        Self::new_with_separator(items, "::")
86    }
87
88    /// Creates a new `QualifiedName` with a custom separator.
89    pub fn new_with_separator<I, S>(items: I, separator: impl Into<String>) -> Self
90    where
91        I: IntoIterator<Item = S>,
92        S: Into<String>,
93    {
94        let items = items.into_iter().map(Into::into).collect::<Vec<String>>();
95        Self {
96            items,
97            separator: separator.into(),
98        }
99    }
100
101    pub fn with_item(&self, item: impl Into<String>) -> Self {
102        let mut items = self.items.clone();
103        items.push(item.into());
104        Self::new_with_separator(items, self.separator.clone())
105    }
106
107    pub fn push(&mut self, item: String) {
108        self.items.push(item);
109    }
110
111    pub fn pop(&mut self) -> Option<String> {
112        self.items.pop()
113    }
114
115    pub fn insert(&mut self, index: usize, item: String) {
116        if index <= self.items.len() {
117            self.items.insert(index, item);
118        }
119    }
120
121    pub fn split_last(&self) -> Option<(String, QualifiedName)> {
122        self.items.split_last().map(|(a, b)| {
123            (
124                a.to_owned(),
125                QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()),
126            )
127        })
128    }
129
130    /// Replaces all occurrences of a substring with another string in all items of the `QualifiedName`
131    /// and returns an owned version of the modified `QualifiedName`.
132    ///
133    /// # Example
134    ///
135    /// ```
136    /// use binaryninja::qualified_name::QualifiedName;
137    ///
138    /// let qualified_name =
139    ///     QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]);
140    /// let replaced = qualified_name.replace("my", "your");
141    /// assert_eq!(
142    ///     replaced.items,
143    ///     vec!["your::namespace".to_string(), "yourtype".to_string()]
144    /// );
145    /// ```
146    pub fn replace(&self, from: &str, to: &str) -> Self {
147        Self {
148            items: self
149                .items
150                .iter()
151                .map(|item| item.replace(from, to))
152                .collect(),
153            separator: self.separator.clone(),
154        }
155    }
156
157    /// Returns the last item, or `None` if it is empty.
158    pub fn last(&self) -> Option<&String> {
159        self.items.last()
160    }
161
162    /// Returns a mutable reference to the last item, or `None` if it is empty.
163    pub fn last_mut(&mut self) -> Option<&mut String> {
164        self.items.last_mut()
165    }
166
167    pub fn len(&self) -> usize {
168        self.items.len()
169    }
170
171    /// A [`QualifiedName`] is empty if it has no items.
172    ///
173    /// If you want to know if the unqualified name is empty (i.e. no characters)
174    /// you must first convert the qualified name to unqualified via the `to_string` method.
175    pub fn is_empty(&self) -> bool {
176        self.items.is_empty()
177    }
178}
179
180impl Default for QualifiedName {
181    fn default() -> Self {
182        Self::new(Vec::<String>::new())
183    }
184}
185
186impl From<String> for QualifiedName {
187    fn from(value: String) -> Self {
188        Self {
189            items: vec![value],
190            // TODO: See comment in struct def.
191            separator: String::from("::"),
192        }
193    }
194}
195
196impl From<&str> for QualifiedName {
197    fn from(value: &str) -> Self {
198        Self::from(value.to_string())
199    }
200}
201
202impl From<&String> for QualifiedName {
203    fn from(value: &String) -> Self {
204        Self::from(value.to_owned())
205    }
206}
207
208impl From<Cow<'_, str>> for QualifiedName {
209    fn from(value: Cow<'_, str>) -> Self {
210        Self::from(value.to_string())
211    }
212}
213
214impl From<Vec<String>> for QualifiedName {
215    fn from(value: Vec<String>) -> Self {
216        Self::new(value)
217    }
218}
219
220impl From<Vec<&str>> for QualifiedName {
221    fn from(value: Vec<&str>) -> Self {
222        value
223            .iter()
224            .map(ToString::to_string)
225            .collect::<Vec<_>>()
226            .into()
227    }
228}
229
230impl From<QualifiedName> for String {
231    fn from(value: QualifiedName) -> Self {
232        value.to_string()
233    }
234}
235
236impl Index<usize> for QualifiedName {
237    type Output = String;
238
239    fn index(&self, index: usize) -> &Self::Output {
240        &self.items[index]
241    }
242}
243
244impl IndexMut<usize> for QualifiedName {
245    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
246        &mut self.items[index]
247    }
248}
249
250impl Display for QualifiedName {
251    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
252        write!(f, "{}", self.items.join(&self.separator))
253    }
254}
255
256impl CoreArrayProvider for QualifiedName {
257    type Raw = BNQualifiedName;
258    type Context = ();
259    type Wrapped<'a> = Self;
260}
261
262unsafe impl CoreArrayProviderInner for QualifiedName {
263    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
264        BNFreeTypeNameList(raw, count);
265    }
266
267    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
268        QualifiedName::from_raw(raw)
269    }
270}