binaryninja/project/
file.rs

1use crate::project::{systime_from_bntime, Project, ProjectFolder};
2use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
3use crate::string::{BnString, IntoCStr};
4use binaryninjacore_sys::{
5    BNFreeProjectFile, BNFreeProjectFileList, BNNewProjectFileReference, BNProjectFile,
6    BNProjectFileAddDependency, BNProjectFileExistsOnDisk, BNProjectFileExport,
7    BNProjectFileGetCreationTimestamp, BNProjectFileGetDependencies, BNProjectFileGetDescription,
8    BNProjectFileGetFolder, BNProjectFileGetId, BNProjectFileGetName,
9    BNProjectFileGetPathInProject, BNProjectFileGetPathOnDisk, BNProjectFileGetProject,
10    BNProjectFileGetRequiredBy, BNProjectFileRemoveDependency, BNProjectFileSetDescription,
11    BNProjectFileSetFolder, BNProjectFileSetName,
12};
13use std::fmt::Debug;
14use std::hash::Hash;
15use std::path::{Path, PathBuf};
16use std::ptr::{null_mut, NonNull};
17use std::time::SystemTime;
18
19#[repr(transparent)]
20pub struct ProjectFile {
21    pub(crate) handle: NonNull<BNProjectFile>,
22}
23
24impl ProjectFile {
25    pub unsafe fn from_raw(handle: NonNull<BNProjectFile>) -> Self {
26        Self { handle }
27    }
28
29    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProjectFile>) -> Ref<Self> {
30        Ref::new(Self { handle })
31    }
32
33    /// Get the project that owns this file
34    pub fn project(&self) -> Ref<Project> {
35        unsafe {
36            Project::ref_from_raw(
37                NonNull::new(BNProjectFileGetProject(self.handle.as_ptr())).unwrap(),
38            )
39        }
40    }
41
42    /// Get the path on disk to this file's contents
43    pub fn path_on_disk(&self) -> Option<PathBuf> {
44        if !self.exists_on_disk() {
45            return None;
46        }
47        let path_str =
48            unsafe { BnString::into_string(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) };
49        Some(PathBuf::from(path_str))
50    }
51
52    /// Get the path in the project to this file's contents
53    pub fn path_in_project(&self) -> PathBuf {
54        let path_str =
55            unsafe { BnString::into_string(BNProjectFileGetPathInProject(self.handle.as_ptr())) };
56        PathBuf::from(path_str)
57    }
58
59    /// Check if this file's contents exist on disk
60    pub fn exists_on_disk(&self) -> bool {
61        unsafe { BNProjectFileExistsOnDisk(self.handle.as_ptr()) }
62    }
63
64    /// Get the unique id of this file
65    pub fn id(&self) -> String {
66        unsafe { BnString::into_string(BNProjectFileGetId(self.handle.as_ptr())) }
67    }
68
69    /// Get the name of this file
70    pub fn name(&self) -> String {
71        unsafe { BnString::into_string(BNProjectFileGetName(self.handle.as_ptr())) }
72    }
73
74    /// Set the name of this file
75    pub fn set_name(&self, value: &str) -> bool {
76        let value_raw = value.to_cstr();
77        unsafe { BNProjectFileSetName(self.handle.as_ptr(), value_raw.as_ptr()) }
78    }
79
80    /// Get the description of this file
81    pub fn description(&self) -> String {
82        unsafe { BnString::into_string(BNProjectFileGetDescription(self.handle.as_ptr())) }
83    }
84
85    /// Set the description of this file
86    pub fn set_description(&self, value: &str) -> bool {
87        let value_raw = value.to_cstr();
88        unsafe { BNProjectFileSetDescription(self.handle.as_ptr(), value_raw.as_ptr()) }
89    }
90
91    /// Get the file creation time
92    pub fn creation_time(&self) -> SystemTime {
93        systime_from_bntime(unsafe { BNProjectFileGetCreationTimestamp(self.handle.as_ptr()) })
94            .unwrap()
95    }
96
97    /// Get the folder that contains this file
98    pub fn folder(&self) -> Option<Ref<ProjectFolder>> {
99        let result = unsafe { BNProjectFileGetFolder(self.handle.as_ptr()) };
100        NonNull::new(result).map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
101    }
102
103    /// Set the folder that contains this file
104    pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
105        let folder_handle = folder.map(|x| x.handle.as_ptr()).unwrap_or(null_mut());
106        unsafe { BNProjectFileSetFolder(self.handle.as_ptr(), folder_handle) }
107    }
108
109    /// Export this file to disk, `true' if the export succeeded
110    ///
111    /// * `dest` - Destination file path for the exported contents, passing a directory will append the file name.
112    pub fn export(&self, dest: &Path) -> bool {
113        let dest_raw = dest.to_cstr();
114        unsafe { BNProjectFileExport(self.handle.as_ptr(), dest_raw.as_ptr()) }
115    }
116
117    /// Add a ProjectFile as a dependency of this file
118    pub fn add_dependency(&self, file: Ref<ProjectFile>) -> bool {
119        unsafe { BNProjectFileAddDependency(self.handle.as_ptr(), file.handle.as_ptr()) }
120    }
121
122    /// Remove a ProjectFile as a dependency of this file
123    pub fn remove_dependency(&self, file: Ref<ProjectFile>) -> bool {
124        unsafe { BNProjectFileRemoveDependency(self.handle.as_ptr(), file.handle.as_ptr()) }
125    }
126
127    /// Get the ProjectFiles that this file depends on
128    pub fn get_dependencies(&self) -> Array<ProjectFile> {
129        let mut count = 0;
130        let result = unsafe { BNProjectFileGetDependencies(self.handle.as_ptr(), &mut count) };
131        unsafe { Array::new(result, count, ()) }
132    }
133
134    /// Get the ProjectFiles that depend on this file
135    pub fn get_required_by(&self) -> Array<ProjectFile> {
136        let mut count = 0;
137        let result = unsafe { BNProjectFileGetRequiredBy(self.handle.as_ptr(), &mut count) };
138        unsafe { Array::new(result, count, ()) }
139    }
140}
141
142impl Debug for ProjectFile {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        f.debug_struct("ProjectFile")
145            .field("id", &self.id())
146            .field("name", &self.name())
147            .field("description", &self.description())
148            .field("creation_time", &self.creation_time())
149            .field("exists_on_disk", &self.exists_on_disk())
150            .field("project", &self.project())
151            .field("folder", &self.folder())
152            .finish()
153    }
154}
155
156unsafe impl Send for ProjectFile {}
157unsafe impl Sync for ProjectFile {}
158
159impl PartialEq for ProjectFile {
160    fn eq(&self, other: &Self) -> bool {
161        self.id() == other.id()
162    }
163}
164
165impl Eq for ProjectFile {}
166
167impl Hash for ProjectFile {
168    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
169        self.id().hash(state);
170    }
171}
172
173impl ToOwned for ProjectFile {
174    type Owned = Ref<Self>;
175
176    fn to_owned(&self) -> Self::Owned {
177        unsafe { RefCountable::inc_ref(self) }
178    }
179}
180
181unsafe impl RefCountable for ProjectFile {
182    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
183        Ref::new(Self {
184            handle: NonNull::new(BNNewProjectFileReference(handle.handle.as_ptr())).unwrap(),
185        })
186    }
187
188    unsafe fn dec_ref(handle: &Self) {
189        BNFreeProjectFile(handle.handle.as_ptr());
190    }
191}
192
193impl CoreArrayProvider for ProjectFile {
194    type Raw = *mut BNProjectFile;
195    type Context = ();
196    type Wrapped<'a> = Guard<'a, Self>;
197}
198
199unsafe impl CoreArrayProviderInner for ProjectFile {
200    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
201        BNFreeProjectFileList(raw, count)
202    }
203
204    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
205        let raw_ptr = NonNull::new(*raw).unwrap();
206        Guard::new(Self::from_raw(raw_ptr), context)
207    }
208}