summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorRubens Brandao <git@rubens.io>2024-06-18 09:43:55 -0300
committerKyle Martin <krm504@nyu.edu>2024-07-09 18:16:54 -0400
commit273fa764cee0e0b972546bbc460e802879b109f8 (patch)
tree3b049eb3e8a71318940cbc95f0670c1fc68b70ee /rust/src
parentae7ab2c432641e52b6c45b447c9974c1dc04f787 (diff)
implement rust TypeArchive
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/lib.rs1
-rw-r--r--rust/src/typearchive.rs947
2 files changed, 948 insertions, 0 deletions
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 3c64cf60..8cd6ba60 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -166,6 +166,7 @@ pub mod string;
pub mod symbol;
pub mod tags;
pub mod templatesimplifier;
+pub mod typearchive;
pub mod types;
use std::path::PathBuf;
diff --git a/rust/src/typearchive.rs b/rust/src/typearchive.rs
new file mode 100644
index 00000000..71a7058e
--- /dev/null
+++ b/rust/src/typearchive.rs
@@ -0,0 +1,947 @@
+use core::{ffi, mem, ptr};
+
+use binaryninjacore_sys::*;
+
+use crate::databuffer::DataBuffer;
+use crate::metadata::Metadata;
+use crate::platform::Platform;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{BnStrCompatible, BnString};
+use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type};
+
+/// Type Archives are a collection of types which can be shared between different analysis
+/// sessions and are backed by a database file on disk. Their types can be modified, and
+/// a history of previous versions of types is stored in snapshots in the archive.
+#[repr(transparent)]
+pub struct TypeArchive {
+ handle: ptr::NonNull<BNTypeArchive>,
+}
+
+impl Drop for TypeArchive {
+ fn drop(&mut self) {
+ unsafe { BNFreeTypeArchiveReference(self.as_raw()) }
+ }
+}
+
+impl Clone for TypeArchive {
+ fn clone(&self) -> Self {
+ unsafe {
+ Self::from_raw(ptr::NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap())
+ }
+ }
+}
+
+impl PartialEq for TypeArchive {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for TypeArchive {}
+
+impl core::hash::Hash for TypeArchive {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ (self.handle.as_ptr() as usize).hash(state);
+ }
+}
+
+impl core::fmt::Debug for TypeArchive {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let path = self.path().map(|x| x.to_string());
+ f.debug_struct("TypeArchive").field("path", &path).finish()
+ }
+}
+
+impl TypeArchive {
+ pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNTypeArchive>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeArchive) -> &Self {
+ assert!(!handle.is_null());
+ mem::transmute(handle)
+ }
+
+ #[allow(clippy::mut_from_ref)]
+ pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeArchive {
+ &mut *self.handle.as_ptr()
+ }
+
+ /// Open the Type Archive at the given path, if it exists.
+ pub fn open<S: BnStrCompatible>(path: S) -> Option<TypeArchive> {
+ let path = path.into_bytes_with_nul();
+ let handle = unsafe { BNOpenTypeArchive(path.as_ref().as_ptr() as *const ffi::c_char) };
+ ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
+ }
+
+ /// Create a Type Archive at the given path, returning None if it could not be created.
+ pub fn create<S: BnStrCompatible>(path: S, platform: &Platform) -> Option<TypeArchive> {
+ let path = path.into_bytes_with_nul();
+ let handle = unsafe {
+ BNCreateTypeArchive(
+ path.as_ref().as_ptr() as *const ffi::c_char,
+ platform.handle,
+ )
+ };
+ ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
+ }
+
+ /// Create a Type Archive at the given path and id, returning None if it could not be created.
+ pub fn create_with_id<P: BnStrCompatible, I: BnStrCompatible>(
+ path: P,
+ id: I,
+ platform: &Platform,
+ ) -> Option<TypeArchive> {
+ let path = path.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let handle = unsafe {
+ BNCreateTypeArchiveWithId(
+ path.as_ref().as_ptr() as *const ffi::c_char,
+ platform.handle,
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
+ }
+
+ /// Get a reference to the Type Archive with the known id, if one exists.
+ pub fn lookup_by_id<S: BnStrCompatible>(id: S) -> Option<TypeArchive> {
+ let id = id.into_bytes_with_nul();
+ let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const ffi::c_char) };
+ ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
+ }
+
+ /// Get the path to the Type Archive's file
+ pub fn path(&self) -> Option<BnString> {
+ let result = unsafe { BNGetTypeArchivePath(self.as_raw()) };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Get the guid for a Type Archive
+ pub fn id(&self) -> Option<BnString> {
+ let result = unsafe { BNGetTypeArchiveId(self.as_raw()) };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Get the associated Platform for a Type Archive
+ pub fn platform(&self) -> Ref<Platform> {
+ let result = unsafe { BNGetTypeArchivePlatform(self.as_raw()) };
+ assert!(!result.is_null());
+ unsafe { Platform::ref_from_raw(result) }
+ }
+
+ /// Get the id of the current snapshot in the type archive
+ pub fn current_snapshot_id(&self) -> BnString {
+ let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.as_raw()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Revert the type archive's current snapshot to the given snapshot
+ pub fn set_current_snapshot_id<S: BnStrCompatible>(&self, id: S) {
+ let id = id.into_bytes_with_nul();
+ unsafe {
+ BNSetTypeArchiveCurrentSnapshot(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ }
+ }
+
+ /// Get a list of every snapshot's id
+ pub fn all_snapshot_ids(&self) -> Array<BnString> {
+ let mut count = 0;
+ let result = unsafe { BNGetTypeArchiveAllSnapshotIds(self.as_raw(), &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get the ids of the parents to the given snapshot
+ pub fn get_snapshot_parent_ids<S: BnStrCompatible>(
+ &self,
+ snapshot: S,
+ ) -> Option<Array<BnString>> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveSnapshotParentIds(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
+ }
+
+ /// Get the ids of the children to the given snapshot
+ pub fn get_snapshot_child_ids<S: BnStrCompatible>(
+ &self,
+ snapshot: S,
+ ) -> Option<Array<BnString>> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveSnapshotChildIds(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
+ }
+
+ /// Add named types to the type archive. Type must have all dependant named types added
+ /// prior to being added, or this function will fail.
+ /// If the type already exists, it will be overwritten.
+ ///
+ /// * `name` - Name of new type
+ /// * `type` - Definition of new type
+ pub fn add_type(&self, name: &QualifiedNameAndType) {
+ self.add_types(core::slice::from_ref(name))
+ }
+
+ /// Add named types to the type archive. Types must have all dependant named
+ /// types prior to being added, or included in the list, or this function will fail.
+ /// Types already existing with any added names will be overwritten.
+ ///
+ /// * `new_types` - Names and definitions of new types
+ pub fn add_types(&self, new_types: &[QualifiedNameAndType]) {
+ // SAFETY BNQualifiedNameAndType and QualifiedNameAndType are transparent
+ let new_types_raw: &[BNQualifiedNameAndType] = unsafe { mem::transmute(new_types) };
+ let result = unsafe {
+ BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types.len())
+ };
+ assert!(result);
+ }
+
+ /// Change the name of an existing type in the type archive.
+ ///
+ /// * `old_name` - Old type name in archive
+ /// * `new_name` - New type name
+ pub fn rename_type(&self, old_name: &QualifiedName, new_name: &QualifiedNameAndType) {
+ let id = self
+ .get_type_id(old_name, self.current_snapshot_id())
+ .unwrap();
+ return self.rename_type_by_id(id, new_name.name());
+ }
+
+ /// Change the name of an existing type in the type archive.
+ ///
+ /// * `id` - Old id of type in archive
+ /// * `new_name` - New type name
+ pub fn rename_type_by_id<S: BnStrCompatible>(&self, id: S, new_name: &QualifiedName) {
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNRenameTypeArchiveType(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ &new_name.0,
+ )
+ };
+ assert!(result);
+ }
+
+ /// Delete an existing type in the type archive.
+ pub fn delete_type(&self, name: &QualifiedName) {
+ let id = self.get_type_id(name, self.current_snapshot_id());
+ let Some(id) = id else {
+ panic!("Unknown type {}", name.string())
+ };
+ self.delete_type_by_id(id);
+ }
+
+ /// Delete an existing type in the type archive.
+ pub fn delete_type_by_id<S: BnStrCompatible>(&self, id: S) {
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const ffi::c_char)
+ };
+ assert!(result);
+ }
+
+ /// Retrieve a stored type in the archive
+ ///
+ /// * `name` - Type name
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_by_name<S: BnStrCompatible>(
+ &self,
+ name: &QualifiedName,
+ snapshot: S,
+ ) -> Option<Ref<Type>> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeByName(
+ self.as_raw(),
+ &name.0,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Retrieve a stored type in the archive by id
+ ///
+ /// * `id` - Type id
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_by_id<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> Option<Ref<Type>> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeById(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Retrieve a type's name by its id
+ ///
+ /// * `id` - Type id
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_name_by_id<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> QualifiedName {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeName(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ QualifiedName(result)
+ }
+
+ /// Retrieve a type's id by its name
+ ///
+ /// * `name` - Type name
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_id<S: BnStrCompatible>(
+ &self,
+ name: &QualifiedName,
+ snapshot: S,
+ ) -> Option<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeId(
+ self.as_raw(),
+ &name.0,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Retrieve all stored types in the archive at a snapshot
+ ///
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_types_and_ids<S: BnStrCompatible>(
+ &self,
+ snapshot: S,
+ ) -> Array<QualifiedNameTypeAndId> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypes(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get a list of all types' ids in the archive at a snapshot
+ ///
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_ids<S: BnStrCompatible>(&self, snapshot: S) -> Array<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypeIds(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get a list of all types' names in the archive at a snapshot
+ ///
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_names<S: BnStrCompatible>(&self, snapshot: S) -> Array<QualifiedName> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypeNames(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get a list of all types' names and ids in the archive at a current snapshot
+
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_names_and_ids<S: BnStrCompatible>(
+ &self,
+ snapshot: S,
+ ) -> (Array<QualifiedName>, Array<BnString>) {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let mut count = 0;
+ let mut names = ptr::null_mut();
+ let mut ids = ptr::null_mut();
+ let result = unsafe {
+ BNGetTypeArchiveTypeNamesAndIds(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut names,
+ &mut ids,
+ &mut count,
+ )
+ };
+ assert!(result);
+ (unsafe { Array::new(names, count, ()) }, unsafe {
+ Array::new(ids, count, ())
+ })
+ }
+
+ /// Get all types a given type references directly
+ ///
+ /// * `id` - Source type id
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_outgoing_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> Array<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveOutgoingDirectTypeReferences(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get all types a given type references, and any types that the referenced types reference
+ ///
+ /// :param id: Source type id
+ /// :param snapshot: Snapshot id to search for types
+ pub fn get_outgoing_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> Array<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveOutgoingRecursiveTypeReferences(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get all types that reference a given type
+ ///
+ /// * `id` - Target type id
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_incoming_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> Array<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveIncomingDirectTypeReferences(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get all types that reference a given type, and all types that reference them, recursively
+ ///
+ /// * `id` - Target type id
+ /// * `snapshot` - Snapshot id to search for types, or empty string to search the latest snapshot
+ pub fn get_incoming_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: S,
+ ) -> Array<BnString> {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveIncomingRecursiveTypeReferences(
+ self.as_raw(),
+ id.as_ref().as_ptr() as *const ffi::c_char,
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ &mut count,
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Look up a metadata entry in the archive
+ pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Ref<Metadata>> {
+ let key = key.into_bytes_with_nul();
+ let result = unsafe {
+ BNTypeArchiveQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
+ };
+ (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) })
+ }
+
+ /// Store a key/value pair in the archive's metadata storage
+ ///
+ /// * `key` - key value to associate the Metadata object with
+ /// * `md` - object to store.
+ pub fn store_metadata<S: BnStrCompatible>(&self, key: S, md: &Metadata) {
+ let key = key.into_bytes_with_nul();
+ let result = unsafe {
+ BNTypeArchiveStoreMetadata(
+ self.as_raw(),
+ key.as_ref().as_ptr() as *const ffi::c_char,
+ md.handle,
+ )
+ };
+ assert!(result);
+ }
+
+ /// Delete a given metadata entry in the archive from the `key`
+ pub fn remove_metadata<S: BnStrCompatible>(&self, key: S) -> bool {
+ let key = key.into_bytes_with_nul();
+ unsafe {
+ BNTypeArchiveRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
+ }
+ }
+
+ /// Turn a given `snapshot` id into a data stream
+ pub fn serialize_snapshot<S: BnStrCompatible>(&self, snapshot: S) -> DataBuffer {
+ let snapshot = snapshot.into_bytes_with_nul();
+ let result = unsafe {
+ BNTypeArchiveSerializeSnapshot(
+ self.as_raw(),
+ snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ };
+ assert!(!result.is_null());
+ DataBuffer::from_raw(result)
+ }
+
+ /// Take a serialized snapshot `data` stream and create a new snapshot from it
+ pub fn deserialize_snapshot(&self, data: &DataBuffer) -> BnString {
+ let result = unsafe { BNTypeArchiveDeserializeSnapshot(self.as_raw(), data.as_raw()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Register a notification listener
+ pub fn register_notification_callback<T: TypeArchiveNotificationCallback>(
+ &self,
+ callback: T,
+ ) -> TypeArchiveCallbackHandle<T> {
+ // SAFETY free on [TypeArchiveCallbackHandle::Drop]
+ let callback = Box::leak(Box::new(callback));
+ let mut notification = BNTypeArchiveNotification {
+ context: callback as *mut T as *mut ffi::c_void,
+ typeAdded: Some(cb_type_added::<T>),
+ typeUpdated: Some(cb_type_updated::<T>),
+ typeRenamed: Some(cb_type_renamed::<T>),
+ typeDeleted: Some(cb_type_deleted::<T>),
+ };
+ unsafe { BNRegisterTypeArchiveNotification(self.as_raw(), &mut notification) }
+ TypeArchiveCallbackHandle {
+ callback,
+ type_archive: self.clone(),
+ }
+ }
+
+ // NOTE NotificationClosure is left private, there is no need for the user
+ // to know or use it.
+ #[allow(private_interfaces)]
+ pub fn register_notification_closure<A, U, R, D>(
+ &self,
+ type_added: A,
+ type_updated: U,
+ type_renamed: R,
+ type_deleted: D,
+ ) -> TypeArchiveCallbackHandle<NotificationClosure<A, U, R, D>>
+ where
+ A: FnMut(&TypeArchive, &str, &Type),
+ U: FnMut(&TypeArchive, &str, &Type, &Type),
+ R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
+ D: FnMut(&TypeArchive, &str, &Type),
+ {
+ self.register_notification_callback(NotificationClosure {
+ fun_type_added: type_added,
+ fun_type_updated: type_updated,
+ fun_type_renamed: type_renamed,
+ fun_type_deleted: type_deleted,
+ })
+ }
+
+ /// Close a type archive, disconnecting it from any active views and closing
+ /// any open file handles
+ pub fn close(self) {
+ unsafe { BNCloseTypeArchive(self.as_raw()) }
+ // NOTE self must be dropped after, don't make it `&self`
+ }
+
+ /// Determine if `file` is a Type Archive
+ pub fn is_type_archive<P: BnStrCompatible>(file: P) -> bool {
+ let file = file.into_bytes_with_nul();
+ unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const ffi::c_char) }
+ }
+
+ // TODO implement TypeContainer
+ ///// Get the TypeContainer interface for this Type Archive, presenting types
+ ///// at the current snapshot in the archive.
+ //pub fn type_container(&self) -> TypeContainer {
+ // let result = unsafe { BNGetTypeArchiveTypeContainer(self.as_raw()) };
+ // unsafe { TypeContainer::from_raw(ptr::NonNull::new(result).unwrap()) }
+ //}
+
+ /// Do some function in a transaction making a new snapshot whose id is passed to func. If func throws,
+ /// the transaction will be rolled back and the snapshot will not be created.
+ ///
+ /// * `func` - Function to call
+ /// * `parents` - Parent snapshot ids
+ ///
+ /// Returns Created snapshot id
+ pub fn new_snapshot_transaction<P, F>(&self, mut function: F, parents: &[BnString]) -> BnString
+ where
+ P: BnStrCompatible,
+ F: FnMut(&str) -> bool,
+ {
+ unsafe extern "C" fn cb_callback<F: FnMut(&str) -> bool>(
+ ctxt: *mut ffi::c_void,
+ id: *const ffi::c_char,
+ ) -> bool {
+ let fun: &mut F = &mut *(ctxt as *mut F);
+ fun(&ffi::CStr::from_ptr(id).to_string_lossy())
+ }
+
+ // SAFETY BnString and `*const ffi::c_char` are transparent
+ let parents_raw = parents.as_ptr() as *const *const ffi::c_char;
+
+ let result = unsafe {
+ BNTypeArchiveNewSnapshotTransaction(
+ self.as_raw(),
+ Some(cb_callback::<F>),
+ &mut function as *mut F as *mut ffi::c_void,
+ parents_raw,
+ parents.len(),
+ )
+ };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Merge two snapshots in the archive to produce a new snapshot
+ ///
+ /// * `base_snapshot` - Common ancestor of snapshots
+ /// * `first_snapshot` - First snapshot to merge
+ /// * `second_snapshot` - Second snapshot to merge
+ /// * `merge_conflicts` - List of all conflicting types, id <-> target snapshot
+ /// * `progress` - Function to call for progress updates
+ ///
+ /// Returns Snapshot id, if merge was successful, otherwise the List of
+ /// conflicting type ids
+ pub fn merge_snapshots<B, F, S, P, M, MI, MK>(
+ &self,
+ base_snapshot: B,
+ first_snapshot: F,
+ second_snapshot: S,
+ merge_conflicts: M,
+ mut progress: P,
+ ) -> Result<BnString, Array<BnString>>
+ where
+ B: BnStrCompatible,
+ F: BnStrCompatible,
+ S: BnStrCompatible,
+ P: FnMut(usize, usize) -> bool,
+ M: IntoIterator<Item = (MI, MK)>,
+ MI: BnStrCompatible,
+ MK: BnStrCompatible,
+ {
+ unsafe extern "C" fn cb_callback<F: FnMut(usize, usize) -> bool>(
+ ctxt: *mut ffi::c_void,
+ progress: usize,
+ total: usize,
+ ) -> bool {
+ let ctxt: &mut F = &mut *(ctxt as *mut F);
+ ctxt(progress, total)
+ }
+
+ let base_snapshot = base_snapshot.into_bytes_with_nul();
+ let first_snapshot = first_snapshot.into_bytes_with_nul();
+ let second_snapshot = second_snapshot.into_bytes_with_nul();
+ let (merge_keys, merge_values): (Vec<BnString>, Vec<BnString>) = merge_conflicts
+ .into_iter()
+ .map(|(k, v)| (BnString::new(k), BnString::new(v)))
+ .unzip();
+ // SAFETY BnString and `*const ffi::c_char` are transparent
+ let merge_keys_raw = merge_keys.as_ptr() as *const *const ffi::c_char;
+ let merge_values_raw = merge_values.as_ptr() as *const *const ffi::c_char;
+
+ let mut conflicts_errors = ptr::null_mut();
+ let mut conflicts_errors_count = 0;
+
+ let mut result = ptr::null_mut();
+
+ let success = unsafe {
+ BNTypeArchiveMergeSnapshots(
+ self.as_raw(),
+ base_snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ first_snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ second_snapshot.as_ref().as_ptr() as *const ffi::c_char,
+ merge_keys_raw,
+ merge_values_raw,
+ merge_keys.len(),
+ &mut conflicts_errors,
+ &mut conflicts_errors_count,
+ &mut result,
+ Some(cb_callback::<P>),
+ (&mut progress) as *mut P as *mut ffi::c_void,
+ )
+ };
+ if success {
+ assert!(!result.is_null());
+ Ok(unsafe { BnString::from_raw(result) })
+ } else {
+ assert!(!conflicts_errors.is_null());
+ Err(unsafe { Array::new(conflicts_errors, conflicts_errors_count, ()) })
+ }
+ }
+}
+
+impl CoreArrayProvider for TypeArchive {
+ type Raw = *mut BNTypeArchive;
+ type Context = ();
+ type Wrapped<'a> = &'a TypeArchive;
+}
+
+unsafe impl CoreArrayProviderInner for TypeArchive {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeArchiveList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::ref_from_raw(raw)
+ }
+}
+
+pub struct TypeArchiveCallbackHandle<T: TypeArchiveNotificationCallback> {
+ callback: *mut T,
+ type_archive: TypeArchive,
+}
+
+impl<T: TypeArchiveNotificationCallback> Drop for TypeArchiveCallbackHandle<T> {
+ fn drop(&mut self) {
+ let mut notification = BNTypeArchiveNotification {
+ context: self.callback as *mut ffi::c_void,
+ typeAdded: Some(cb_type_added::<T>),
+ typeUpdated: Some(cb_type_updated::<T>),
+ typeRenamed: Some(cb_type_renamed::<T>),
+ typeDeleted: Some(cb_type_deleted::<T>),
+ };
+ // unregister the notification callback
+ unsafe {
+ BNUnregisterTypeArchiveNotification(self.type_archive.as_raw(), &mut notification)
+ }
+ // free the context created at [TypeArchive::register_notification_callback]
+ drop(unsafe { Box::from_raw(self.callback) });
+ }
+}
+
+pub trait TypeArchiveNotificationCallback {
+ /// Called when a type is added to the archive
+ ///
+ /// * `archive` - Source Type archive
+ /// * `id` - Id of type added
+ /// * `definition` - Definition of type
+ fn type_added(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
+
+ /// Called when a type in the archive is updated to a new definition
+ ///
+ /// * `archive` - Source Type archive
+ /// * `id` - Id of type
+ /// * `old_definition` - Previous definition
+ /// * `new_definition` - Current definition
+ fn type_updated(
+ &mut self,
+ _archive: &TypeArchive,
+ _id: &str,
+ _old_definition: &Type,
+ _new_definition: &Type,
+ ) {
+ }
+
+ /// Called when a type in the archive is renamed
+ ///
+ /// * `archive` - Source Type archive
+ /// * `id` - Type id
+ /// * `old_name` - Previous name
+ /// * `new_name` - Current name
+ fn type_renamed(
+ &mut self,
+ _archive: &TypeArchive,
+ _id: &str,
+ _old_name: &QualifiedName,
+ _new_name: &QualifiedName,
+ ) {
+ }
+
+ /// Called when a type in the archive is deleted from the archive
+ ///
+ /// * `archive` - Source Type archive
+ /// * `id` - Id of type deleted
+ /// * `definition` - Definition of type deleted
+ fn type_deleted(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
+}
+
+struct NotificationClosure<A, U, R, D>
+where
+ A: FnMut(&TypeArchive, &str, &Type),
+ U: FnMut(&TypeArchive, &str, &Type, &Type),
+ R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
+ D: FnMut(&TypeArchive, &str, &Type),
+{
+ fun_type_added: A,
+ fun_type_updated: U,
+ fun_type_renamed: R,
+ fun_type_deleted: D,
+}
+
+impl<A, U, R, D> TypeArchiveNotificationCallback for NotificationClosure<A, U, R, D>
+where
+ A: FnMut(&TypeArchive, &str, &Type),
+ U: FnMut(&TypeArchive, &str, &Type, &Type),
+ R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
+ D: FnMut(&TypeArchive, &str, &Type),
+{
+ fn type_added(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
+ (self.fun_type_added)(archive, id, definition)
+ }
+
+ fn type_updated(
+ &mut self,
+ archive: &TypeArchive,
+ id: &str,
+ old_definition: &Type,
+ new_definition: &Type,
+ ) {
+ (self.fun_type_updated)(archive, id, old_definition, new_definition)
+ }
+
+ fn type_renamed(
+ &mut self,
+ archive: &TypeArchive,
+ id: &str,
+ old_name: &QualifiedName,
+ new_name: &QualifiedName,
+ ) {
+ (self.fun_type_renamed)(archive, id, old_name, new_name)
+ }
+
+ fn type_deleted(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
+ (self.fun_type_deleted)(archive, id, definition)
+ }
+}
+
+unsafe extern "C" fn cb_type_added<T: TypeArchiveNotificationCallback>(
+ ctxt: *mut ::std::os::raw::c_void,
+ archive: *mut BNTypeArchive,
+ id: *const ::std::os::raw::c_char,
+ definition: *mut BNType,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ ctxt.type_added(
+ unsafe { TypeArchive::ref_from_raw(&archive) },
+ unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
+ &Type { handle: definition },
+ )
+}
+unsafe extern "C" fn cb_type_updated<T: TypeArchiveNotificationCallback>(
+ ctxt: *mut ::std::os::raw::c_void,
+ archive: *mut BNTypeArchive,
+ id: *const ::std::os::raw::c_char,
+ old_definition: *mut BNType,
+ new_definition: *mut BNType,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ ctxt.type_updated(
+ unsafe { TypeArchive::ref_from_raw(&archive) },
+ unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
+ &Type {
+ handle: old_definition,
+ },
+ &Type {
+ handle: new_definition,
+ },
+ )
+}
+unsafe extern "C" fn cb_type_renamed<T: TypeArchiveNotificationCallback>(
+ ctxt: *mut ::std::os::raw::c_void,
+ archive: *mut BNTypeArchive,
+ id: *const ::std::os::raw::c_char,
+ old_name: *const BNQualifiedName,
+ new_name: *const BNQualifiedName,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let old_name = mem::ManuallyDrop::new(QualifiedName(*old_name));
+ let new_name = mem::ManuallyDrop::new(QualifiedName(*new_name));
+ ctxt.type_renamed(
+ unsafe { TypeArchive::ref_from_raw(&archive) },
+ unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
+ &old_name,
+ &new_name,
+ )
+}
+unsafe extern "C" fn cb_type_deleted<T: TypeArchiveNotificationCallback>(
+ ctxt: *mut ::std::os::raw::c_void,
+ archive: *mut BNTypeArchive,
+ id: *const ::std::os::raw::c_char,
+ definition: *mut BNType,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ ctxt.type_deleted(
+ unsafe { TypeArchive::ref_from_raw(&archive) },
+ unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
+ &Type { handle: definition },
+ )
+}