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