binaryninja/project/
folder.rs

1use crate::progress::{NoProgressCallback, ProgressCallback};
2use crate::project::file::ProjectFile;
3use crate::project::Project;
4use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
5use crate::string::{BnString, IntoCStr};
6use binaryninjacore_sys::{
7    BNFreeProjectFolder, BNFreeProjectFolderList, BNNewProjectFolderReference, BNProjectFolder,
8    BNProjectFolderExport, BNProjectFolderGetDescription, BNProjectFolderGetFiles,
9    BNProjectFolderGetId, BNProjectFolderGetName, BNProjectFolderGetParent,
10    BNProjectFolderGetProject, BNProjectFolderSetDescription, BNProjectFolderSetName,
11    BNProjectFolderSetParent,
12};
13use std::ffi::c_void;
14use std::fmt::Debug;
15use std::path::Path;
16use std::ptr::{null_mut, NonNull};
17
18#[repr(transparent)]
19pub struct ProjectFolder {
20    pub(crate) handle: NonNull<BNProjectFolder>,
21}
22
23impl ProjectFolder {
24    pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFolder>) -> Self {
25        Self { handle }
26    }
27
28    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProjectFolder>) -> Ref<Self> {
29        Ref::new(Self { handle })
30    }
31
32    /// Get the project that owns this folder
33    pub fn project(&self) -> Ref<Project> {
34        unsafe {
35            Project::ref_from_raw(
36                NonNull::new(BNProjectFolderGetProject(self.handle.as_ptr())).unwrap(),
37            )
38        }
39    }
40
41    /// Get the unique id of this folder
42    pub fn id(&self) -> String {
43        unsafe { BnString::into_string(BNProjectFolderGetId(self.handle.as_ptr())) }
44    }
45
46    /// Get the name of this folder
47    pub fn name(&self) -> String {
48        unsafe { BnString::into_string(BNProjectFolderGetName(self.handle.as_ptr())) }
49    }
50
51    /// Set the name of this folder
52    pub fn set_name(&self, value: &str) -> bool {
53        let value_raw = value.to_cstr();
54        unsafe { BNProjectFolderSetName(self.handle.as_ptr(), value_raw.as_ptr()) }
55    }
56
57    /// Get the description of this folder
58    pub fn description(&self) -> String {
59        unsafe { BnString::into_string(BNProjectFolderGetDescription(self.handle.as_ptr())) }
60    }
61
62    /// Set the description of this folder
63    pub fn set_description(&self, value: &str) -> bool {
64        let value_raw = value.to_cstr();
65        unsafe { BNProjectFolderSetDescription(self.handle.as_ptr(), value_raw.as_ptr()) }
66    }
67
68    /// Get the folder that contains this folder
69    pub fn parent(&self) -> Option<Ref<ProjectFolder>> {
70        let result = unsafe { BNProjectFolderGetParent(self.handle.as_ptr()) };
71        NonNull::new(result).map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
72    }
73
74    /// Set the folder that contains this folder
75    pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
76        let folder_handle = folder.map(|x| x.handle.as_ptr()).unwrap_or(null_mut());
77        unsafe { BNProjectFolderSetParent(self.handle.as_ptr(), folder_handle) }
78    }
79
80    /// Recursively export this folder to disk, returns `true' if the export succeeded
81    ///
82    /// * `dest` - Destination path for the exported contents
83    pub fn export(&self, dest: &Path) -> bool {
84        self.export_with_progress(dest, NoProgressCallback)
85    }
86
87    /// Recursively export this folder to disk, returns `true' if the export succeeded
88    ///
89    /// * `dest` - Destination path for the exported contents
90    /// * `progress` - [`ProgressCallback`] that will be called as contents are exporting
91    pub fn export_with_progress<P>(&self, dest: &Path, mut progress: P) -> bool
92    where
93        P: ProgressCallback,
94    {
95        let dest_raw = dest.to_cstr();
96        unsafe {
97            BNProjectFolderExport(
98                self.handle.as_ptr(),
99                dest_raw.as_ptr(),
100                &mut progress as *mut P as *mut c_void,
101                Some(P::cb_progress_callback),
102            )
103        }
104    }
105
106    /// Get the files contained in this folder
107    pub fn files(&self) -> Array<ProjectFile> {
108        let mut count = 0;
109        let result = unsafe { BNProjectFolderGetFiles(self.handle.as_ptr(), &mut count) };
110        unsafe { Array::new(result, count, ()) }
111    }
112}
113
114impl PartialEq for ProjectFolder {
115    fn eq(&self, other: &Self) -> bool {
116        self.id() == other.id()
117    }
118}
119
120impl Debug for ProjectFolder {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        f.debug_struct("ProjectFolder")
123            .field("id", &self.id())
124            .field("name", &self.name())
125            .field("description", &self.description())
126            .finish()
127    }
128}
129
130unsafe impl Send for ProjectFolder {}
131unsafe impl Sync for ProjectFolder {}
132
133impl ToOwned for ProjectFolder {
134    type Owned = Ref<Self>;
135
136    fn to_owned(&self) -> Self::Owned {
137        unsafe { RefCountable::inc_ref(self) }
138    }
139}
140
141unsafe impl RefCountable for ProjectFolder {
142    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
143        Ref::new(Self {
144            handle: NonNull::new(BNNewProjectFolderReference(handle.handle.as_ptr())).unwrap(),
145        })
146    }
147
148    unsafe fn dec_ref(handle: &Self) {
149        BNFreeProjectFolder(handle.handle.as_ptr());
150    }
151}
152
153impl CoreArrayProvider for ProjectFolder {
154    type Raw = *mut BNProjectFolder;
155    type Context = ();
156    type Wrapped<'a> = Guard<'a, Self>;
157}
158
159unsafe impl CoreArrayProviderInner for ProjectFolder {
160    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
161        BNFreeProjectFolderList(raw, count)
162    }
163
164    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
165        let raw_ptr = NonNull::new(*raw).unwrap();
166        Guard::new(Self::from_raw(raw_ptr), context)
167    }
168}