Skip to content

Commit aec0d26

Browse files
committed
[Rust] Move QualifiedName to own module
This is used in more places than types, so its best we keep it separate.
1 parent d1da7e1 commit aec0d26

File tree

3 files changed

+227
-218
lines changed

3 files changed

+227
-218
lines changed

rust/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub mod metadata;
6868
pub mod platform;
6969
pub mod progress;
7070
pub mod project;
71+
pub mod qualified_name;
7172
pub mod rc;
7273
pub mod references;
7374
pub mod relocation;

rust/src/qualified_name.rs

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner};
2+
use crate::string::{raw_to_string, strings_to_string_list, BnString};
3+
use binaryninjacore_sys::*;
4+
use std::borrow::Cow;
5+
use std::fmt::{Display, Formatter};
6+
use std::ops::{Index, IndexMut};
7+
8+
// TODO: Document usage, specifically how to make a qualified name and why it exists.
9+
#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
10+
pub struct QualifiedName {
11+
// TODO: Make this Option<String> where default is "::".
12+
pub separator: String,
13+
pub items: Vec<String>,
14+
}
15+
16+
impl QualifiedName {
17+
pub(crate) fn from_raw(value: &BNQualifiedName) -> Self {
18+
// TODO: This could be improved...
19+
let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) };
20+
let items = raw_names
21+
.iter()
22+
.filter_map(|&raw_name| raw_to_string(raw_name as *const _))
23+
.collect();
24+
let separator = raw_to_string(value.join).unwrap();
25+
Self { items, separator }
26+
}
27+
28+
pub(crate) fn from_owned_raw(value: BNQualifiedName) -> Self {
29+
let result = Self::from_raw(&value);
30+
Self::free_raw(value);
31+
result
32+
}
33+
34+
pub fn into_raw(value: Self) -> BNQualifiedName {
35+
let bn_join = BnString::new(&value.separator);
36+
BNQualifiedName {
37+
// NOTE: Leaking string list must be freed by core or us!
38+
name: strings_to_string_list(&value.items),
39+
// NOTE: Leaking string must be freed by core or us!
40+
join: BnString::into_raw(bn_join),
41+
nameCount: value.items.len(),
42+
}
43+
}
44+
45+
pub(crate) fn free_raw(value: BNQualifiedName) {
46+
unsafe { BnString::free_raw(value.join) };
47+
unsafe { BNFreeStringList(value.name, value.nameCount) };
48+
}
49+
50+
pub fn new(items: Vec<String>) -> Self {
51+
Self::new_with_separator(items, "::".to_string())
52+
}
53+
54+
pub fn new_with_separator(items: Vec<String>, separator: String) -> Self {
55+
Self { items, separator }
56+
}
57+
58+
pub fn with_item(&self, item: impl Into<String>) -> Self {
59+
let mut items = self.items.clone();
60+
items.push(item.into());
61+
Self::new_with_separator(items, self.separator.clone())
62+
}
63+
64+
pub fn push(&mut self, item: String) {
65+
self.items.push(item);
66+
}
67+
68+
pub fn pop(&mut self) -> Option<String> {
69+
self.items.pop()
70+
}
71+
72+
pub fn insert(&mut self, index: usize, item: String) {
73+
if index <= self.items.len() {
74+
self.items.insert(index, item);
75+
}
76+
}
77+
78+
pub fn split_last(&self) -> Option<(String, QualifiedName)> {
79+
self.items.split_last().map(|(a, b)| {
80+
(
81+
a.to_owned(),
82+
QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()),
83+
)
84+
})
85+
}
86+
87+
/// Replaces all occurrences of a substring with another string in all items of the `QualifiedName`
88+
/// and returns an owned version of the modified `QualifiedName`.
89+
///
90+
/// # Example
91+
///
92+
/// ```
93+
/// use binaryninja::types::QualifiedName;
94+
///
95+
/// let qualified_name =
96+
/// QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]);
97+
/// let replaced = qualified_name.replace("my", "your");
98+
/// assert_eq!(
99+
/// replaced.items,
100+
/// vec!["your::namespace".to_string(), "yourtype".to_string()]
101+
/// );
102+
/// ```
103+
pub fn replace(&self, from: &str, to: &str) -> Self {
104+
Self {
105+
items: self
106+
.items
107+
.iter()
108+
.map(|item| item.replace(from, to))
109+
.collect(),
110+
separator: self.separator.clone(),
111+
}
112+
}
113+
114+
/// Returns the last item, or `None` if it is empty.
115+
pub fn last(&self) -> Option<&String> {
116+
self.items.last()
117+
}
118+
119+
/// Returns a mutable reference to the last item, or `None` if it is empty.
120+
pub fn last_mut(&mut self) -> Option<&mut String> {
121+
self.items.last_mut()
122+
}
123+
124+
pub fn len(&self) -> usize {
125+
self.items.len()
126+
}
127+
128+
/// A [`QualifiedName`] is empty if it has no items.
129+
///
130+
/// If you want to know if the unqualified name is empty (i.e. no characters)
131+
/// you must first convert the qualified name to unqualified via the `to_string` method.
132+
pub fn is_empty(&self) -> bool {
133+
self.items.is_empty()
134+
}
135+
}
136+
137+
impl From<String> for QualifiedName {
138+
fn from(value: String) -> Self {
139+
Self {
140+
items: vec![value],
141+
// TODO: See comment in struct def.
142+
separator: String::from("::"),
143+
}
144+
}
145+
}
146+
147+
impl From<&str> for QualifiedName {
148+
fn from(value: &str) -> Self {
149+
Self::from(value.to_string())
150+
}
151+
}
152+
153+
impl From<&String> for QualifiedName {
154+
fn from(value: &String) -> Self {
155+
Self::from(value.to_owned())
156+
}
157+
}
158+
159+
impl From<Cow<'_, str>> for QualifiedName {
160+
fn from(value: Cow<'_, str>) -> Self {
161+
Self::from(value.to_string())
162+
}
163+
}
164+
165+
impl From<Vec<String>> for QualifiedName {
166+
fn from(value: Vec<String>) -> Self {
167+
Self::new(value)
168+
}
169+
}
170+
171+
impl From<Vec<&str>> for QualifiedName {
172+
fn from(value: Vec<&str>) -> Self {
173+
value
174+
.iter()
175+
.map(ToString::to_string)
176+
.collect::<Vec<_>>()
177+
.into()
178+
}
179+
}
180+
181+
impl From<QualifiedName> for String {
182+
fn from(value: QualifiedName) -> Self {
183+
value.to_string()
184+
}
185+
}
186+
187+
impl Index<usize> for QualifiedName {
188+
type Output = String;
189+
190+
fn index(&self, index: usize) -> &Self::Output {
191+
&self.items[index]
192+
}
193+
}
194+
195+
impl IndexMut<usize> for QualifiedName {
196+
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
197+
&mut self.items[index]
198+
}
199+
}
200+
201+
impl Display for QualifiedName {
202+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203+
write!(f, "{}", self.items.join(&self.separator))
204+
}
205+
}
206+
207+
impl CoreArrayProvider for QualifiedName {
208+
type Raw = BNQualifiedName;
209+
type Context = ();
210+
type Wrapped<'a> = Self;
211+
}
212+
213+
unsafe impl CoreArrayProviderInner for QualifiedName {
214+
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
215+
BNFreeTypeNameList(raw, count);
216+
}
217+
218+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
219+
QualifiedName::from_raw(raw)
220+
}
221+
}

0 commit comments

Comments
 (0)