summaryrefslogtreecommitdiff
path: root/rust/src/project.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/project.rs')
-rw-r--r--rust/src/project.rs1142
1 files changed, 226 insertions, 916 deletions
diff --git a/rust/src/project.rs b/rust/src/project.rs
index f08ad63e..fad1e5ef 100644
--- a/rust/src/project.rs
+++ b/rust/src/project.rs
@@ -1,16 +1,22 @@
+pub mod file;
+pub mod folder;
+
+use std::ffi::{c_char, c_void};
+use std::fmt::Debug;
use std::ptr::{null_mut, NonNull};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use std::{ffi, mem};
use binaryninjacore_sys::*;
use crate::metadata::Metadata;
-use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::file::ProjectFile;
+use crate::project::folder::ProjectFolder;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
-#[repr(C)]
pub struct Project {
- handle: NonNull<BNProject>,
+ pub(crate) handle: NonNull<BNProject>,
}
impl Project {
@@ -18,14 +24,8 @@ impl Project {
Project { handle }
}
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProject) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProject {
- &mut *self.handle.as_ptr()
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProject>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
pub fn all_open() -> Array<Project> {
@@ -39,35 +39,35 @@ impl Project {
///
/// * `path` - Path to the project directory (.bnpr)
/// * `name` - Name of the new project
- pub fn create<P: BnStrCompatible, S: BnStrCompatible>(path: P, name: S) -> Self {
+ pub fn create<P: BnStrCompatible, S: BnStrCompatible>(path: P, name: S) -> Option<Ref<Self>> {
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let handle = unsafe {
BNCreateProject(
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
+ path_raw.as_ref().as_ptr() as *const c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
)
};
- unsafe { Self::from_raw(NonNull::new(handle).unwrap()) }
+ NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) })
}
/// Open an existing project
///
/// * `path` - Path to the project directory (.bnpr) or project metadata file (.bnpm)
- pub fn open_project<P: BnStrCompatible>(path: P) -> Self {
+ pub fn open_project<P: BnStrCompatible>(path: P) -> Option<Ref<Self>> {
let path_raw = path.into_bytes_with_nul();
- let handle = unsafe { BNOpenProject(path_raw.as_ref().as_ptr() as *const ffi::c_char) };
- unsafe { Self::from_raw(NonNull::new(handle).unwrap()) }
+ let handle = unsafe { BNOpenProject(path_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) })
}
/// Check if the project is currently open
pub fn is_open(&self) -> bool {
- unsafe { BNProjectIsOpen(self.as_raw()) }
+ unsafe { BNProjectIsOpen(self.handle.as_ptr()) }
}
/// Open a closed project
pub fn open(&self) -> Result<(), ()> {
- if unsafe { BNProjectOpen(self.as_raw()) } {
+ if unsafe { BNProjectOpen(self.handle.as_ptr()) } {
Ok(())
} else {
Err(())
@@ -76,7 +76,7 @@ impl Project {
/// Close a open project
pub fn close(&self) -> Result<(), ()> {
- if unsafe { BNProjectClose(self.as_raw()) } {
+ if unsafe { BNProjectClose(self.handle.as_ptr()) } {
Ok(())
} else {
Err(())
@@ -85,35 +85,43 @@ impl Project {
/// Get the unique id of this project
pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetId(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetId(self.handle.as_ptr())) }
}
/// Get the path of the project
pub fn path(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetPath(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetPath(self.handle.as_ptr())) }
}
/// Get the name of the project
pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetName(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetName(self.handle.as_ptr())) }
}
/// Set the name of the project
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
- unsafe { BNProjectSetName(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char) }
+ unsafe {
+ BNProjectSetName(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
+ }
}
/// Get the description of the project
pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetDescription(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetDescription(self.handle.as_ptr())) }
}
/// Set the description of the project
pub fn set_description<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
- BNProjectSetDescription(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
+ BNProjectSetDescription(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
}
}
@@ -121,7 +129,7 @@ impl Project {
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Ref<Metadata> {
let key = key.into_bytes_with_nul();
let result = unsafe {
- BNProjectQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
+ BNProjectQueryMetadata(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char)
};
unsafe { Metadata::ref_from_raw(result) }
}
@@ -134,8 +142,8 @@ impl Project {
let key_raw = key.into_bytes_with_nul();
unsafe {
BNProjectStoreMetadata(
- self.as_raw(),
- key_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ key_raw.as_ref().as_ptr() as *const c_char,
value.handle,
)
}
@@ -146,14 +154,14 @@ impl Project {
let key_raw = key.into_bytes_with_nul();
unsafe {
BNProjectRemoveMetadata(
- self.as_raw(),
- key_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ key_raw.as_ref().as_ptr() as *const c_char,
)
}
}
pub fn push_folder(&self, file: &ProjectFolder) {
- unsafe { BNProjectPushFolder(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectPushFolder(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// Recursively create files and folders in the project from a path on disk
@@ -166,28 +174,12 @@ impl Project {
path: P,
parent: Option<&ProjectFolder>,
description: D,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
P: BnStrCompatible,
D: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
-
- unsafe {
- let result = BNProjectCreateFolderFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- parent_ptr,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_folder_from_path_with_progress(path, parent, description, NoProgressCallback)
}
/// Recursively create files and folders in the project from a path on disk
@@ -195,36 +187,33 @@ impl Project {
/// * `path` - Path to folder on disk
/// * `parent` - Parent folder in the project that will contain the new contents
/// * `description` - Description for created root folder
- /// * `progress_func` - Progress function that will be called
- pub fn create_folder_from_path_with_progress<P, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFolder`] is being created
+ pub fn create_folder_from_path_with_progress<P, D, PC>(
&self,
path: P,
parent: Option<&ProjectFolder>,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFolder, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFolder>, ()>
where
P: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
unsafe {
let result = BNProjectCreateFolderFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
parent_ptr,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -238,24 +227,22 @@ impl Project {
parent: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
unsafe {
let result = BNProjectCreateFolder(
- self.as_raw(),
+ self.handle.as_ptr(),
parent_ptr,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -271,7 +258,7 @@ impl Project {
name: N,
description: D,
id: I,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
@@ -279,26 +266,24 @@ impl Project {
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
let id_raw = id.into_bytes_with_nul();
unsafe {
let result = BNProjectCreateFolderUnsafe(
- self.as_raw(),
+ self.handle.as_ptr(),
parent_ptr,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
/// Get a list of folders in the project
pub fn folders(&self) -> Result<Array<ProjectFolder>, ()> {
let mut count = 0;
- let result = unsafe { BNProjectGetFolders(self.as_raw(), &mut count) };
+ let result = unsafe { BNProjectGetFolders(self.handle.as_ptr(), &mut count) };
if result.is_null() {
return Err(());
}
@@ -307,55 +292,39 @@ impl Project {
}
/// Retrieve a folder in the project by unique folder `id`
- pub fn folder_by_id<S: BnStrCompatible>(&self, id: S) -> Option<ProjectFolder> {
+ pub fn folder_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<ProjectFolder>> {
let id_raw = id.into_bytes_with_nul();
- let id_ptr = id_raw.as_ref().as_ptr() as *const ffi::c_char;
-
- let result = unsafe { BNProjectGetFolderById(self.as_raw(), id_ptr) };
+ let id_ptr = id_raw.as_ref().as_ptr() as *const c_char;
+ let result = unsafe { BNProjectGetFolderById(self.handle.as_ptr(), id_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFolder::from_raw(handle) })
+ Some(unsafe { ProjectFolder::ref_from_raw(handle) })
}
- /// Recursively delete a folder from the project
+ /// Recursively delete a [`ProjectFolder`] from the [`Project`].
///
- /// * `folder` - Folder to delete recursively
+ /// * `folder` - [`ProjectFolder`] to delete recursively
pub fn delete_folder(&self, folder: &ProjectFolder) -> Result<(), ()> {
- let result = unsafe {
- BNProjectDeleteFolder(
- self.as_raw(),
- folder.as_raw(),
- null_mut(),
- Some(cb_progress_func_nop),
- )
- };
- if result {
- Ok(())
- } else {
- Err(())
- }
+ self.delete_folder_with_progress(folder, NoProgressCallback)
}
- /// Recursively delete a folder from the project
+ /// Recursively delete a [`ProjectFolder`] from the [`Project`].
///
- /// * `folder` - Folder to delete recursively
- /// * `progress_func` - Progress function that will be called as objects get deleted
- pub fn delete_folder_with_progress<F>(
+ /// * `folder` - [`ProjectFolder`] to delete recursively
+ /// * `progress` - [`ProgressCallback`] that will be called as objects get deleted
+ pub fn delete_folder_with_progress<P: ProgressCallback>(
&self,
folder: &ProjectFolder,
- mut progress_func: F,
- ) -> Result<(), ()>
- where
- F: FnMut(usize, usize) -> bool,
- {
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ mut progress: P,
+ ) -> Result<(), ()> {
let result = unsafe {
BNProjectDeleteFolder(
- self.as_raw(),
- folder.as_raw(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ self.handle.as_ptr(),
+ folder.handle.as_ptr(),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
)
};
+
if result {
Ok(())
} else {
@@ -364,7 +333,7 @@ impl Project {
}
pub fn push_file(&self, file: &ProjectFile) {
- unsafe { BNProjectPushFile(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectPushFile(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// Create a file in the project from a path on disk
@@ -379,27 +348,19 @@ impl Project {
folder: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_from_path_with_progress(
+ path,
+ folder,
+ name,
+ description,
+ NoProgressCallback,
+ )
}
/// Create a file in the project from a path on disk
@@ -408,36 +369,37 @@ impl Project {
/// * `folder` - Folder to place the created file in
/// * `name` - Name to assign to the created file
/// * `description` - Description to assign to the created file
- /// * `progress_func` - Progress function that will be called as the file is being added
- pub fn create_file_from_path_with_progress<P, N, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being added
+ pub fn create_file_from_path_with_progress<P, N, D, PC>(
&self,
path: P,
folder: Option<&ProjectFolder>,
name: N,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -457,31 +419,22 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let id_raw = id.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileFromPathUnsafe(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
- systime_to_bntime(creation_time).unwrap(),
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_from_path_unsafe_with_progress(
+ path,
+ folder,
+ name,
+ description,
+ id,
+ creation_time,
+ NoProgressCallback,
+ )
}
/// Create a file in the project from a path on disk
@@ -492,9 +445,9 @@ impl Project {
/// * `description` - Description to assign to the created file
/// * `id` - id unique ID
/// * `creation_time` - Creation time of the file
- /// * `progress_func` - Progress function that will be called as the file is being added
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
#[allow(clippy::too_many_arguments)]
- pub unsafe fn create_file_from_path_with_progress_unsafe<P, N, D, I, F>(
+ pub unsafe fn create_file_from_path_unsafe_with_progress<P, N, D, I, PC>(
&self,
path: P,
folder: Option<&ProjectFolder>,
@@ -502,33 +455,34 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
let id_raw = id.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileFromPathUnsafe(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
systime_to_bntime(creation_time).unwrap(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -544,26 +498,12 @@ impl Project {
folder: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
{
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFile(
- self.as_raw(),
- contents.as_ptr(),
- contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_with_progress(contents, folder, name, description, NoProgressCallback)
}
/// Create a file in the project
@@ -572,35 +512,36 @@ impl Project {
/// * `folder` - Folder to place the created file in
/// * `name` - Name to assign to the created file
/// * `description` - Description to assign to the created file
- /// * `progress_func` - Progress function that will be called as the file is being added
- pub fn create_file_with_progress<N, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
+ pub fn create_file_with_progress<N, D, P>(
&self,
contents: &[u8],
folder: Option<&ProjectFolder>,
name: N,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: P,
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFile(
- self.as_raw(),
+ self.handle.as_ptr(),
contents.as_ptr(),
contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -620,30 +561,21 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
{
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let id_raw = id.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileUnsafe(
- self.as_raw(),
- contents.as_ptr(),
- contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
- systime_to_bntime(creation_time).unwrap(),
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_unsafe_with_progress(
+ contents,
+ folder,
+ name,
+ description,
+ id,
+ creation_time,
+ NoProgressCallback,
+ )
}
/// Create a file in the project
@@ -654,9 +586,9 @@ impl Project {
/// * `description` - Description to assign to the created file
/// * `id` - id unique ID
/// * `creation_time` - Creation time of the file
- /// * `progress_func` - Progress function that will be called as the file is being added
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
#[allow(clippy::too_many_arguments)]
- pub unsafe fn create_file_with_progress_unsafe<N, D, I, F>(
+ pub unsafe fn create_file_unsafe_with_progress<N, D, I, P>(
&self,
contents: &[u8],
folder: Option<&ProjectFolder>,
@@ -664,66 +596,67 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: P,
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
let id_raw = id.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileUnsafe(
- self.as_raw(),
+ self.handle.as_ptr(),
contents.as_ptr(),
contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
systime_to_bntime(creation_time).unwrap(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
/// Get a list of files in the project
- pub fn files(&self) -> Result<Array<ProjectFile>, ()> {
+ pub fn files(&self) -> Array<ProjectFile> {
let mut count = 0;
- let result = unsafe { BNProjectGetFiles(self.as_raw(), &mut count) };
+ let result = unsafe { BNProjectGetFiles(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
- Ok(unsafe { Array::new(result, count, ()) })
+ unsafe { Array::new(result, count, ()) }
}
/// Retrieve a file in the project by unique `id`
- pub fn file_by_id<S: BnStrCompatible>(&self, id: S) -> Option<ProjectFile> {
+ pub fn file_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<ProjectFile>> {
let id_raw = id.into_bytes_with_nul();
- let id_ptr = id_raw.as_ref().as_ptr() as *const ffi::c_char;
+ let id_ptr = id_raw.as_ref().as_ptr() as *const c_char;
- let result = unsafe { BNProjectGetFileById(self.as_raw(), id_ptr) };
+ let result = unsafe { BNProjectGetFileById(self.handle.as_ptr(), id_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFile::from_raw(handle) })
+ Some(unsafe { ProjectFile::ref_from_raw(handle) })
}
/// Retrieve a file in the project by the `path` on disk
- pub fn file_by_path<S: BnStrCompatible>(&self, path: S) -> Option<ProjectFile> {
+ pub fn file_by_path<S: BnStrCompatible>(&self, path: S) -> Option<Ref<ProjectFile>> {
let path_raw = path.into_bytes_with_nul();
- let path_ptr = path_raw.as_ref().as_ptr() as *const ffi::c_char;
+ let path_ptr = path_raw.as_ref().as_ptr() as *const c_char;
- let result = unsafe { BNProjectGetFileByPathOnDisk(self.as_raw(), path_ptr) };
+ let result = unsafe { BNProjectGetFileByPathOnDisk(self.handle.as_ptr(), path_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFile::from_raw(handle) })
+ Some(unsafe { ProjectFile::ref_from_raw(handle) })
}
/// Delete a file from the project
pub fn delete_file(&self, file: &ProjectFile) -> bool {
- unsafe { BNProjectDeleteFile(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectDeleteFile(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// A context manager to speed up bulk project operations.
@@ -733,44 +666,58 @@ impl Project {
///
/// ```no_run
/// # use binaryninja::project::Project;
- /// # let project: Project = todo!();
+ /// # let mut project: Project = todo!();
/// if let Ok(bulk) = project.bulk_operation() {
/// for file in std::fs::read_dir("/bin/").unwrap().into_iter() {
/// let file = file.unwrap();
/// let file_type = file.file_type().unwrap();
/// if file_type.is_file() && !file_type.is_symlink() {
- /// bulk.create_file_from_path(
- /// "/bin/",
- /// None,
- /// &file.file_name().to_string_lossy(),
- /// "",
- /// ).unwrap();
+ /// bulk.create_file_from_path("/bin/", None, &file.file_name().to_string_lossy(), "")
+ /// .unwrap();
/// }
/// }
/// }
/// ```
- // NOTE mut is used here, so only one lock can be aquired at once
+ // NOTE mut is used here, so only one lock can be acquired at once
pub fn bulk_operation(&mut self) -> Result<ProjectBulkOperationLock, ()> {
Ok(ProjectBulkOperationLock::lock(self))
}
}
-impl Drop for Project {
- fn drop(&mut self) {
- unsafe { BNFreeProject(self.as_raw()) }
+impl Debug for Project {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Project")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("description", &self.description())
+ .finish()
}
}
-impl Clone for Project {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectReference(self.as_raw())).unwrap()) }
+impl ToOwned for Project {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Project {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewProjectReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeProject(handle.handle.as_ptr());
}
}
impl CoreArrayProvider for Project {
type Raw = *mut BNProject;
type Context = ();
- type Wrapped<'a> = &'a Project;
+ type Wrapped<'a> = Guard<'a, Project>;
}
unsafe impl CoreArrayProviderInner for Project {
@@ -778,18 +725,20 @@ unsafe impl CoreArrayProviderInner for Project {
BNFreeProjectList(raw, count)
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
}
}
+// TODO: Rename to bulk operation guard?
pub struct ProjectBulkOperationLock<'a> {
lock: &'a mut Project,
}
impl<'a> ProjectBulkOperationLock<'a> {
pub fn lock(project: &'a mut Project) -> Self {
- unsafe { BNProjectBeginBulkOperation(project.as_raw()) };
+ unsafe { BNProjectBeginBulkOperation(project.handle.as_ptr()) };
Self { lock: project }
}
@@ -807,282 +756,7 @@ impl std::ops::Deref for ProjectBulkOperationLock<'_> {
impl Drop for ProjectBulkOperationLock<'_> {
fn drop(&mut self) {
- unsafe { BNProjectEndBulkOperation(self.lock.as_raw()) };
- }
-}
-
-#[repr(transparent)]
-pub struct ProjectFolder {
- handle: NonNull<BNProjectFolder>,
-}
-
-impl ProjectFolder {
- pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFolder>) -> Self {
- Self { handle }
- }
-
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProjectFolder) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProjectFolder {
- &mut *self.handle.as_ptr()
- }
-
- /// Get the project that owns this folder
- pub fn project(&self) -> Project {
- unsafe {
- Project::from_raw(NonNull::new(BNProjectFolderGetProject(self.as_raw())).unwrap())
- }
- }
-
- /// Get the unique id of this folder
- pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetId(self.as_raw())) }
- }
-
- /// Get the name of this folder
- pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetName(self.as_raw())) }
- }
-
- /// Set the name of this folder
- pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFolderSetName(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the description of this folder
- pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetDescription(self.as_raw())) }
- }
-
- /// Set the description of this folder
- pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFolderSetDescription(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the folder that contains this folder
- pub fn parent(&self) -> Option<ProjectFolder> {
- let result = unsafe { BNProjectFolderGetParent(self.as_raw()) };
- NonNull::new(result).map(|handle| unsafe { ProjectFolder::from_raw(handle) })
- }
-
- /// Set the folder that contains this folder
- pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
- let folder_handle = folder
- .map(|x| unsafe { x.as_raw() as *mut _ })
- .unwrap_or(null_mut());
- unsafe { BNProjectFolderSetParent(self.as_raw(), folder_handle) }
- }
-
- /// Recursively export this folder to disk, returns `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFolderExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- )
- }
- }
-
- /// Recursively export this folder to disk, returns `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- /// * `progress_func` - Progress function that will be called as contents are exporting
- pub fn export_with_progress<S, F>(&self, dest: S, mut progress: F) -> bool
- where
- S: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
- {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFolderExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- &mut progress as *mut _ as *mut ffi::c_void,
- Some(cb_progress_func::<F>),
- )
- }
- }
-}
-
-impl Drop for ProjectFolder {
- fn drop(&mut self) {
- unsafe { BNFreeProjectFolder(self.as_raw()) }
- }
-}
-
-impl Clone for ProjectFolder {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectFolderReference(self.as_raw())).unwrap()) }
- }
-}
-
-impl CoreArrayProvider for ProjectFolder {
- type Raw = *mut BNProjectFolder;
- type Context = ();
- type Wrapped<'a> = &'a Self;
-}
-
-unsafe impl CoreArrayProviderInner for ProjectFolder {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeProjectFolderList(raw, count)
- }
-
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
- }
-}
-
-#[repr(transparent)]
-pub struct ProjectFile {
- handle: NonNull<BNProjectFile>,
-}
-
-impl ProjectFile {
- pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFile>) -> Self {
- Self { handle }
- }
-
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProjectFile) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProjectFile {
- &mut *self.handle.as_ptr()
- }
-
- /// Get the project that owns this file
- pub fn project(&self) -> Project {
- unsafe { Project::from_raw(NonNull::new(BNProjectFileGetProject(self.as_raw())).unwrap()) }
- }
-
- /// Get the path on disk to this file's contents
- pub fn path_on_disk(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetPathOnDisk(self.as_raw())) }
- }
-
- /// Check if this file's contents exist on disk
- pub fn exists_on_disk(&self) -> bool {
- unsafe { BNProjectFileExistsOnDisk(self.as_raw()) }
- }
-
- /// Get the unique id of this file
- pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetId(self.as_raw())) }
- }
-
- /// Get the name of this file
- pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetName(self.as_raw())) }
- }
-
- /// Set the name of this file
- pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFileSetName(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the description of this file
- pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetDescription(self.as_raw())) }
- }
-
- /// Set the description of this file
- pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFileSetDescription(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the file creation time
- pub fn creation_time(&self) -> SystemTime {
- systime_from_bntime(unsafe { BNProjectFileGetCreationTimestamp(self.as_raw()) }).unwrap()
- }
-
- /// Get the folder that contains this file
- pub fn folder(&self) -> Option<ProjectFolder> {
- let result = unsafe { BNProjectFileGetFolder(self.as_raw()) };
- NonNull::new(result).map(|handle| unsafe { ProjectFolder::from_raw(handle) })
- }
-
- /// Set the folder that contains this file
- pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
- let folder_handle = folder
- .map(|x| unsafe { x.as_raw() as *mut _ })
- .unwrap_or(null_mut());
- unsafe { BNProjectFileSetFolder(self.as_raw(), folder_handle) }
- }
-
- /// Export this file to disk, `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFileExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-}
-
-impl Drop for ProjectFile {
- fn drop(&mut self) {
- unsafe { BNFreeProjectFile(self.as_raw()) }
- }
-}
-
-impl Clone for ProjectFile {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectFileReference(self.as_raw())).unwrap()) }
- }
-}
-
-impl CoreArrayProvider for ProjectFile {
- type Raw = *mut BNProjectFile;
- type Context = ();
- type Wrapped<'a> = &'a Self;
-}
-
-unsafe impl CoreArrayProviderInner for ProjectFile {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeProjectFileList(raw, count)
- }
-
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe { BNProjectEndBulkOperation(self.lock.handle.as_ptr()) };
}
}
@@ -1098,367 +772,3 @@ fn systime_to_bntime(time: SystemTime) -> Option<i64> {
.try_into()
.ok()
}
-
-unsafe extern "C" fn cb_progress_func<F: FnMut(usize, usize) -> bool>(
- ctxt: *mut ffi::c_void,
- progress: usize,
- total: usize,
-) -> bool {
- if ctxt.is_null() {
- return true;
- }
- let closure: &mut F = mem::transmute(ctxt);
- closure(progress, total)
-}
-
-unsafe extern "C" fn cb_progress_func_nop(
- _ctxt: *mut ffi::c_void,
- _progress: usize,
- _total: usize,
-) -> bool {
- true
-}
-
-#[cfg(test)]
-mod test {
- use std::time::SystemTime;
-
- use crate::metadata::Metadata;
- use crate::rc::Ref;
-
- use super::Project;
-
- fn unique_project(name: &str) -> String {
- format!("{}/{}", std::env::temp_dir().to_str().unwrap(), name)
- }
-
- #[test]
- fn create_delete_empty() {
- crate::headless::init();
-
- let project_name = "create_delete_empty_project";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
- assert!(project.is_open());
-
- // check project data
- let project_path_received = project.path();
- assert_eq!(&project_path, project_path_received.as_str());
- let project_name_received = project.name();
- assert_eq!(project_name, project_name_received.as_str());
-
- // close the project
- project.close().unwrap();
- assert!(!project.is_open());
- drop(project);
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
-
- crate::headless::shutdown();
- }
-
- #[test]
- fn create_close_open_close() {
- crate::headless::init();
-
- let project_name = "create_close_open_close";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
-
- // get the project id
- let id = project.id();
-
- // close the project
- project.close().unwrap();
- drop(project);
-
- let project = Project::open_project(&project_path);
- // assert same id
- let new_id = project.id();
- assert_eq!(id, new_id);
-
- // close the project
- project.close().unwrap();
- drop(project);
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
-
- crate::headless::shutdown();
- }
-
- #[test]
- fn modify_project() {
- crate::headless::init();
-
- let project_name = "modify_project";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
-
- // get project id
- let id = project.id();
-
- // create data and verify that data was created
- let data_1: Ref<Metadata> = "data1".into();
- let data_2: Ref<Metadata> = "data2".into();
- assert!(project.store_metadata("key", data_1.as_ref()));
- assert_eq!(
- data_1.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
- project.remove_metadata("key");
- assert!(project.store_metadata("key", data_2.as_ref()));
- assert_eq!(
- data_2.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
-
- // create file that will be imported to the project
- let tmp_folder_1_name = format!(
- "tmp_folder_{}",
- std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap()
- .as_millis()
- );
- let tmp_folder_2_name = format!("{tmp_folder_1_name }_2");
- let tmp_folder_1 = format!(
- "{}/{tmp_folder_1_name}",
- std::env::temp_dir().to_str().unwrap()
- );
- let tmp_folder_2 = format!(
- "{}/{tmp_folder_2_name}",
- std::env::temp_dir().to_str().unwrap()
- );
- std::fs::create_dir(&tmp_folder_1).unwrap();
- std::fs::create_dir(&tmp_folder_2).unwrap();
- let input_file_1 = format!("{tmp_folder_2}/input_1");
- let input_file_2 = format!("{tmp_folder_2}/input_2");
- let input_file_1_data = b"input_1_data";
- let input_file_2_data = b"input_1_data";
- std::fs::write(&input_file_1, input_file_1_data).unwrap();
- std::fs::write(&input_file_2, input_file_2_data).unwrap();
-
- // create and delete folders
- let folder_1_desc = "desc_folder_1";
- let folder_1 = project
- .create_folder(None, "folder_1", folder_1_desc)
- .unwrap();
- let folder_2_desc = "AAAAA";
- let folder_2_id = "1717416787371";
- let folder_2 = unsafe {
- project
- .create_folder_unsafe(Some(&folder_1), "folder_2", folder_2_desc, folder_2_id)
- .unwrap()
- };
- let folder_3_desc = ""; // TODO "çàáÁÀ";
- let folder_3 = project
- .create_folder_from_path(&tmp_folder_1, None, folder_3_desc)
- .unwrap();
- let folder_4_desc = "";
- let _folder_4 = project
- .create_folder_from_path_with_progress(
- &tmp_folder_2,
- Some(&folder_3),
- folder_4_desc,
- |_, _| true,
- )
- .unwrap();
- let folder_5 = project
- .create_folder(None, "deleted_folder", folder_4_desc)
- .unwrap();
-
- assert_eq!(project.folders().unwrap().len(), 5);
- let last_folder = project.folder_by_id(folder_5.id()).unwrap();
- project.delete_folder(&last_folder).unwrap();
- assert_eq!(project.folders().unwrap().len(), 4);
- drop(folder_5);
-
- // create, import and delete file
- let file_1_data = b"data_1";
- let file_1_desc = "desc_file_1";
- let _file_1 = project
- .create_file(file_1_data, None, "file_1", file_1_desc)
- .unwrap();
- let file_2_data = b"data_2";
- let file_2_desc = "my desc";
- let file_2_id = "12334545";
- let _file_2 = unsafe {
- project.create_file_unsafe(
- file_2_data,
- Some(&folder_2),
- "file_2",
- file_2_desc,
- file_2_id,
- SystemTime::UNIX_EPOCH,
- )
- }
- .unwrap();
- let file_3_data = b"data\x023";
- let file_3_desc = "!";
- let _file_3 = project
- .create_file_with_progress(
- file_3_data,
- Some(&folder_1),
- "file_3",
- file_3_desc,
- |_, _| true,
- )
- .unwrap();
- let file_4_time = SystemTime::now();
- let file_4_data = b"data_4\x00_4";
- let file_4_desc = "";
- let file_4_id = "123123123";
- let _file_4 = unsafe {
- project.create_file_with_progress_unsafe(
- file_4_data,
- Some(&folder_3),
- "file_4",
- file_4_desc,
- file_4_id,
- file_4_time,
- |_, _| true,
- )
- }
- .unwrap();
- let file_5_desc = "desc";
- let _file_5 = project
- .create_file_from_path(&input_file_1, None, "file_5", file_5_desc)
- .unwrap();
- let file_6_time = SystemTime::now();
- let file_6_desc = "de";
- let file_6_id = "90218347";
- let _file_6 = unsafe {
- project.create_file_from_path_unsafe(
- &input_file_2,
- Some(&folder_3),
- "file_6",
- file_6_desc,
- file_6_id,
- file_6_time,
- )
- }
- .unwrap();
- let file_7 = project
- .create_file_from_path_with_progress(
- &input_file_2,
- Some(&folder_2),
- "file_7",
- "no",
- |_, _| true,
- )
- .unwrap();
- let file_8 = unsafe {
- project.create_file_from_path_with_progress_unsafe(
- &input_file_1,
- None,
- "file_7",
- "no",
- "92736528",
- SystemTime::now(),
- |_, _| true,
- )
- }
- .unwrap();
-
- assert_eq!(project.files().unwrap().len(), 10);
- let file_a = project.file_by_id(file_8.id()).unwrap();
- let file_b = project.file_by_path(file_7.path_on_disk()).unwrap();
- project.delete_file(&file_a);
- project.delete_file(&file_b);
- assert_eq!(project.files().unwrap().len(), 8);
- drop(file_8);
- drop(file_7);
-
- project.set_name("project_name");
- project.set_description("project_description");
-
- // close the project
- project.close().unwrap();
- drop(project);
- drop(folder_1);
- drop(folder_2);
- drop(folder_3);
-
- // reopen the project and verify the information store on it
- let project = Project::open_project(&project_path);
-
- // assert same id
- assert_eq!(id, project.id());
-
- // verify metadata
- assert_eq!(
- data_2.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
-
- // check folders
- let folders = [
- ("folder_1", None),
- ("folder_2", Some(folder_2_id)),
- (&tmp_folder_1_name, None),
- (&tmp_folder_2_name, None),
- ];
- for folder in project.folders().unwrap().iter() {
- let found = folders
- .iter()
- .find(|f| folder.name().as_str() == f.0)
- .unwrap();
- if let Some(id) = found.1 {
- assert_eq!(folder.id().as_str(), id);
- }
- }
-
- // check files
- #[rustfmt::skip]
- let files = [
- ("file_1", &file_1_data[..], None, None),
- ("file_2", &file_2_data[..], Some(file_2_id), None),
- ("file_3", &file_3_data[..], None, None),
- ("file_4", &file_4_data[..], Some(file_4_id), Some(file_4_time)),
- ("file_5", &input_file_1_data[..], None, None),
- ("file_6", &input_file_2_data[..], Some(file_6_id), Some(file_6_time)),
- ("input_1", &input_file_1_data[..], None, None),
- ("input_2", &input_file_2_data[..], None, None),
- ];
- for file in project.files().unwrap().iter() {
- let found = files.iter().find(|f| file.name().as_str() == f.0).unwrap();
- if let Some(id) = found.2 {
- assert_eq!(file.id().as_str(), id);
- }
- if let Some(time) = found.3 {
- assert_eq!(
- file.creation_time()
- .duration_since(SystemTime::UNIX_EPOCH)
- .unwrap()
- .as_secs(),
- time.duration_since(SystemTime::UNIX_EPOCH)
- .unwrap()
- .as_secs()
- );
- }
- let content = std::fs::read(file.path_on_disk().as_str()).unwrap();
- assert_eq!(content, found.1);
- }
-
- assert_eq!(project.name().as_str(), "project_name");
- assert_eq!(project.description().as_str(), "project_description");
-
- // close the project
- project.close().unwrap();
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
- std::fs::remove_dir_all(tmp_folder_1).unwrap();
- std::fs::remove_dir_all(tmp_folder_2).unwrap();
-
- crate::headless::shutdown();
- }
-}