binaryninja/
type_archive.rs

1use crate::progress::{NoProgressCallback, ProgressCallback};
2use binaryninjacore_sys::*;
3use std::ffi::{c_char, c_void, CStr};
4use std::fmt::{Debug, Display, Formatter};
5use std::hash::Hash;
6use std::path::{Path, PathBuf};
7use std::ptr::NonNull;
8
9use crate::data_buffer::DataBuffer;
10use crate::metadata::Metadata;
11use crate::platform::Platform;
12use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
13use crate::string::{raw_to_string, BnString, IntoCStr};
14use crate::type_container::TypeContainer;
15use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type};
16
17#[repr(transparent)]
18#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct TypeArchiveSnapshotId(pub String);
20
21impl TypeArchiveSnapshotId {
22    pub fn unset() -> Self {
23        Self("".to_string())
24    }
25}
26
27impl Display for TypeArchiveSnapshotId {
28    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
29        f.write_fmt(format_args!("{}", self.0))
30    }
31}
32
33impl CoreArrayProvider for TypeArchiveSnapshotId {
34    type Raw = *mut c_char;
35    type Context = ();
36    type Wrapped<'a> = TypeArchiveSnapshotId;
37}
38
39unsafe impl CoreArrayProviderInner for TypeArchiveSnapshotId {
40    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
41        BNFreeStringList(raw, count)
42    }
43
44    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
45        let str = CStr::from_ptr(*raw).to_str().unwrap().to_string();
46        TypeArchiveSnapshotId(str)
47    }
48}
49
50/// Type Archives are a collection of types which can be shared between different analysis
51/// sessions and are backed by a database file on disk. Their types can be modified, and
52/// a history of previous versions of types is stored in snapshots in the archive.
53pub struct TypeArchive {
54    pub(crate) handle: NonNull<BNTypeArchive>,
55}
56
57impl TypeArchive {
58    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeArchive>) -> Self {
59        Self { handle }
60    }
61
62    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNTypeArchive>) -> Ref<Self> {
63        Ref::new(Self { handle })
64    }
65
66    /// Open the Type Archive at the given path, if it exists.
67    pub fn open(path: impl AsRef<Path>) -> Option<Ref<TypeArchive>> {
68        let raw_path = path.as_ref().to_cstr();
69        let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr()) };
70        NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
71    }
72
73    /// Create a Type Archive at the given path, returning `None` if it could not be created.
74    ///
75    /// If the file has already been created and is not a valid type archive this will return `None`.
76    pub fn create(path: impl AsRef<Path>, platform: &Platform) -> Option<Ref<TypeArchive>> {
77        let raw_path = path.as_ref().to_cstr();
78        let handle = unsafe { BNCreateTypeArchive(raw_path.as_ptr(), platform.handle) };
79        NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
80    }
81
82    /// Create a Type Archive at the given path and id, returning None if it could not be created.
83    ///
84    /// If the file has already been created and is not a valid type archive this will return `None`.
85    pub fn create_with_id(
86        path: impl AsRef<Path>,
87        id: &str,
88        platform: &Platform,
89    ) -> Option<Ref<TypeArchive>> {
90        let raw_path = path.as_ref().to_cstr();
91        let id = id.to_cstr();
92        let handle =
93            unsafe { BNCreateTypeArchiveWithId(raw_path.as_ptr(), platform.handle, id.as_ptr()) };
94        NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
95    }
96
97    /// Get a reference to the Type Archive with the known id, if one exists.
98    pub fn lookup_by_id(id: &str) -> Option<Ref<TypeArchive>> {
99        let id = id.to_cstr();
100        let handle = unsafe { BNLookupTypeArchiveById(id.as_ptr()) };
101        NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
102    }
103
104    /// Get the path to the Type Archive's file
105    pub fn path(&self) -> Option<PathBuf> {
106        let result = unsafe { BNGetTypeArchivePath(self.handle.as_ptr()) };
107        assert!(!result.is_null());
108        let path_str = unsafe { BnString::into_string(result) };
109        Some(PathBuf::from(path_str))
110    }
111
112    /// Get the guid for a Type Archive
113    pub fn id(&self) -> BnString {
114        let result = unsafe { BNGetTypeArchiveId(self.handle.as_ptr()) };
115        assert!(!result.is_null());
116        unsafe { BnString::from_raw(result) }
117    }
118
119    /// Get the associated Platform for a Type Archive
120    pub fn platform(&self) -> Ref<Platform> {
121        let result = unsafe { BNGetTypeArchivePlatform(self.handle.as_ptr()) };
122        assert!(!result.is_null());
123        unsafe { Platform::ref_from_raw(result) }
124    }
125
126    /// Get the id of the current snapshot in the type archive
127    pub fn current_snapshot_id(&self) -> TypeArchiveSnapshotId {
128        let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.handle.as_ptr()) };
129        assert!(!result.is_null());
130        let id = unsafe { BnString::into_string(result) };
131        TypeArchiveSnapshotId(id)
132    }
133
134    /// Revert the type archive's current snapshot to the given snapshot
135    pub fn set_current_snapshot_id(&self, id: &TypeArchiveSnapshotId) {
136        let snapshot = id.clone().to_cstr();
137        unsafe { BNSetTypeArchiveCurrentSnapshot(self.handle.as_ptr(), snapshot.as_ptr()) }
138    }
139
140    /// Get a list of every snapshot's id
141    pub fn all_snapshot_ids(&self) -> Array<TypeArchiveSnapshotId> {
142        let mut count = 0;
143        let result = unsafe { BNGetTypeArchiveAllSnapshotIds(self.handle.as_ptr(), &mut count) };
144        assert!(!result.is_null());
145        unsafe { Array::new(result, count, ()) }
146    }
147
148    /// Get the ids of the parents to the given snapshot
149    pub fn get_snapshot_parent_ids(
150        &self,
151        snapshot: &TypeArchiveSnapshotId,
152    ) -> Option<Array<BnString>> {
153        let mut count = 0;
154        let snapshot = snapshot.clone().to_cstr();
155        let result = unsafe {
156            BNGetTypeArchiveSnapshotParentIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count)
157        };
158        (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
159    }
160
161    /// Get the ids of the children to the given snapshot
162    pub fn get_snapshot_child_ids(
163        &self,
164        snapshot: &TypeArchiveSnapshotId,
165    ) -> Option<Array<BnString>> {
166        let mut count = 0;
167        let snapshot = snapshot.clone().to_cstr();
168        let result = unsafe {
169            BNGetTypeArchiveSnapshotChildIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count)
170        };
171        (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
172    }
173
174    /// Add a named type to the type archive. Type must have all dependant named types added
175    /// prior to being added, or this function will fail.
176    /// If the type already exists, it will be overwritten.
177    ///
178    /// * `named_type` - Named type to add
179    pub fn add_type(&self, named_type: QualifiedNameAndType) -> bool {
180        self.add_types(vec![named_type])
181    }
182
183    /// Add named types to the type archive. Types must have all dependant named
184    /// types prior to being added, or included in the list, or this function will fail.
185    /// Types already existing with any added names will be overwritten.
186    ///
187    /// * `named_types` - Names and definitions of new types
188    pub fn add_types(&self, named_types: Vec<QualifiedNameAndType>) -> bool {
189        let new_types_raw: Vec<_> = named_types
190            .into_iter()
191            .map(QualifiedNameAndType::into_raw)
192            .collect();
193        let result = unsafe {
194            BNAddTypeArchiveTypes(
195                self.handle.as_ptr(),
196                new_types_raw.as_ptr(),
197                new_types_raw.len(),
198            )
199        };
200        for new_type in new_types_raw {
201            QualifiedNameAndType::free_raw(new_type);
202        }
203        result
204    }
205
206    /// Change the name of an existing type in the type archive. Returns false if failed.
207    ///
208    /// * `old_name` - Old type name in archive
209    /// * `new_name` - New type name
210    pub fn rename_type(&self, old_name: QualifiedName, new_name: QualifiedName) -> bool {
211        if let Some(id) = self.get_type_id(old_name) {
212            self.rename_type_by_id(&id, new_name)
213        } else {
214            false
215        }
216    }
217
218    /// Change the name of an existing type in the type archive. Returns false if failed.
219    ///
220    /// * `id` - Old id of type in archive
221    /// * `new_name` - New type name
222    pub fn rename_type_by_id(&self, id: &str, new_name: QualifiedName) -> bool {
223        let id = id.to_cstr();
224        let raw_name = QualifiedName::into_raw(new_name);
225        let result =
226            unsafe { BNRenameTypeArchiveType(self.handle.as_ptr(), id.as_ptr(), &raw_name) };
227        QualifiedName::free_raw(raw_name);
228        result
229    }
230
231    /// Delete an existing type in the type archive.
232    pub fn delete_type(&self, name: QualifiedName) -> bool {
233        if let Some(type_id) = self.get_type_id(name) {
234            self.delete_type_by_id(&type_id)
235        } else {
236            false
237        }
238    }
239
240    /// Delete an existing type in the type archive.
241    pub fn delete_type_by_id(&self, id: &str) -> bool {
242        let id = id.to_cstr();
243        unsafe { BNDeleteTypeArchiveType(self.handle.as_ptr(), id.as_ptr()) }
244    }
245
246    /// Retrieve a stored type in the archive
247    ///
248    /// * `name` - Type name
249    pub fn get_type_by_name(&self, name: QualifiedName) -> Option<Ref<Type>> {
250        self.get_type_by_name_from_snapshot(name, &TypeArchiveSnapshotId::unset())
251    }
252
253    /// Retrieve a stored type in the archive
254    ///
255    /// * `name` - Type name
256    /// * `snapshot` - Snapshot id to search for types
257    pub fn get_type_by_name_from_snapshot(
258        &self,
259        name: QualifiedName,
260        snapshot: &TypeArchiveSnapshotId,
261    ) -> Option<Ref<Type>> {
262        let raw_name = QualifiedName::into_raw(name);
263        let snapshot = snapshot.clone().to_cstr();
264        let result = unsafe {
265            BNGetTypeArchiveTypeByName(self.handle.as_ptr(), &raw_name, snapshot.as_ptr())
266        };
267        QualifiedName::free_raw(raw_name);
268        (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
269    }
270
271    /// Retrieve a stored type in the archive by id
272    ///
273    /// * `id` - Type id
274    pub fn get_type_by_id(&self, id: &str) -> Option<Ref<Type>> {
275        self.get_type_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset())
276    }
277
278    /// Retrieve a stored type in the archive by id
279    ///
280    /// * `id` - Type id
281    /// * `snapshot` - Snapshot id to search for types
282    pub fn get_type_by_id_from_snapshot(
283        &self,
284        id: &str,
285        snapshot: &TypeArchiveSnapshotId,
286    ) -> Option<Ref<Type>> {
287        let id = id.to_cstr();
288        let snapshot = snapshot.clone().to_cstr();
289        let result = unsafe {
290            BNGetTypeArchiveTypeById(self.handle.as_ptr(), id.as_ptr(), snapshot.as_ptr())
291        };
292        (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
293    }
294
295    /// Retrieve a type's name by its id
296    ///
297    /// * `id` - Type id
298    pub fn get_type_name_by_id(&self, id: &str) -> QualifiedName {
299        self.get_type_name_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset())
300    }
301
302    /// Retrieve a type's name by its id
303    ///
304    /// * `id` - Type id
305    /// * `snapshot` - Snapshot id to search for types
306    pub fn get_type_name_by_id_from_snapshot(
307        &self,
308        id: &str,
309        snapshot: &TypeArchiveSnapshotId,
310    ) -> QualifiedName {
311        let id = id.to_cstr();
312        let snapshot = snapshot.clone().to_cstr();
313        let result = unsafe {
314            BNGetTypeArchiveTypeName(self.handle.as_ptr(), id.as_ptr(), snapshot.as_ptr())
315        };
316        QualifiedName::from_owned_raw(result)
317    }
318
319    /// Retrieve a type's id by its name
320    ///
321    /// * `name` - Type name
322    pub fn get_type_id(&self, name: QualifiedName) -> Option<String> {
323        self.get_type_id_from_snapshot(name, &TypeArchiveSnapshotId::unset())
324    }
325
326    /// Retrieve a type's id by its name
327    ///
328    /// * `name` - Type name
329    /// * `snapshot` - Snapshot id to search for types
330    pub fn get_type_id_from_snapshot(
331        &self,
332        name: QualifiedName,
333        snapshot: &TypeArchiveSnapshotId,
334    ) -> Option<String> {
335        let raw_name = QualifiedName::into_raw(name);
336        let snapshot = snapshot.clone().to_cstr();
337        let result =
338            unsafe { BNGetTypeArchiveTypeId(self.handle.as_ptr(), &raw_name, snapshot.as_ptr()) };
339        QualifiedName::free_raw(raw_name);
340        (!result.is_null()).then(|| unsafe { BnString::into_string(result) })
341    }
342
343    /// Retrieve all stored types in the archive at a snapshot
344    pub fn get_types_and_ids(&self) -> Array<QualifiedNameTypeAndId> {
345        self.get_types_and_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
346    }
347
348    /// Retrieve all stored types in the archive at a snapshot
349    ///
350    /// * `snapshot` - Snapshot id to search for types
351    pub fn get_types_and_ids_from_snapshot(
352        &self,
353        snapshot: &TypeArchiveSnapshotId,
354    ) -> Array<QualifiedNameTypeAndId> {
355        let mut count = 0;
356        let snapshot = snapshot.clone().to_cstr();
357        let result =
358            unsafe { BNGetTypeArchiveTypes(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) };
359        assert!(!result.is_null());
360        unsafe { Array::new(result, count, ()) }
361    }
362
363    /// Get a list of all types' ids in the archive at a snapshot
364    pub fn get_type_ids(&self) -> Array<BnString> {
365        self.get_type_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
366    }
367
368    /// Get a list of all types' ids in the archive at a snapshot
369    ///
370    /// * `snapshot` - Snapshot id to search for types
371    pub fn get_type_ids_from_snapshot(&self, snapshot: &TypeArchiveSnapshotId) -> Array<BnString> {
372        let mut count = 0;
373        let snapshot = snapshot.clone().to_cstr();
374        let result =
375            unsafe { BNGetTypeArchiveTypeIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) };
376        assert!(!result.is_null());
377        unsafe { Array::new(result, count, ()) }
378    }
379
380    /// Get a list of all types' names in the archive at a snapshot
381    pub fn get_type_names(&self) -> Array<QualifiedName> {
382        self.get_type_names_from_snapshot(&TypeArchiveSnapshotId::unset())
383    }
384
385    /// Get a list of all types' names in the archive at a snapshot
386    ///
387    /// * `snapshot` - Snapshot id to search for types
388    pub fn get_type_names_from_snapshot(
389        &self,
390        snapshot: &TypeArchiveSnapshotId,
391    ) -> Array<QualifiedName> {
392        let mut count = 0;
393        let snapshot = snapshot.clone().to_cstr();
394        let result = unsafe {
395            BNGetTypeArchiveTypeNames(self.handle.as_ptr(), snapshot.as_ptr(), &mut count)
396        };
397        assert!(!result.is_null());
398        unsafe { Array::new(result, count, ()) }
399    }
400
401    /// Get a list of all types' names and ids in the archive at the latest snapshot
402    pub fn get_type_names_and_ids(&self) -> (Array<QualifiedName>, Array<BnString>) {
403        self.get_type_names_and_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
404    }
405
406    /// Get a list of all types' names and ids in the archive at a specific snapshot
407    ///
408    /// * `snapshot` - Snapshot id to search for types
409    pub fn get_type_names_and_ids_from_snapshot(
410        &self,
411        snapshot: &TypeArchiveSnapshotId,
412    ) -> (Array<QualifiedName>, Array<BnString>) {
413        let mut count = 0;
414        let snapshot = snapshot.clone().to_cstr();
415        let mut names = std::ptr::null_mut();
416        let mut ids = std::ptr::null_mut();
417        let result = unsafe {
418            BNGetTypeArchiveTypeNamesAndIds(
419                self.handle.as_ptr(),
420                snapshot.as_ptr(),
421                &mut names,
422                &mut ids,
423                &mut count,
424            )
425        };
426        assert!(result);
427        (unsafe { Array::new(names, count, ()) }, unsafe {
428            Array::new(ids, count, ())
429        })
430    }
431
432    /// Get all types a given type references directly
433    ///
434    /// * `id` - Source type id
435    pub fn get_outgoing_direct_references(&self, id: &str) -> Array<BnString> {
436        self.get_outgoing_direct_references_from_snapshot(id, &TypeArchiveSnapshotId::unset())
437    }
438
439    /// Get all types a given type references directly
440    ///
441    /// * `id` - Source type id
442    /// * `snapshot` - Snapshot id to search for types
443    pub fn get_outgoing_direct_references_from_snapshot(
444        &self,
445        id: &str,
446        snapshot: &TypeArchiveSnapshotId,
447    ) -> Array<BnString> {
448        let id = id.to_cstr();
449        let snapshot = snapshot.clone().to_cstr();
450        let mut count = 0;
451        let result = unsafe {
452            BNGetTypeArchiveOutgoingDirectTypeReferences(
453                self.handle.as_ptr(),
454                id.as_ptr(),
455                snapshot.as_ptr(),
456                &mut count,
457            )
458        };
459        assert!(!result.is_null());
460        unsafe { Array::new(result, count, ()) }
461    }
462
463    /// Get all types a given type references, and any types that the referenced types reference
464    ///
465    /// * `id` - Source type id
466    pub fn get_outgoing_recursive_references(&self, id: &str) -> Array<BnString> {
467        self.get_outgoing_recursive_references_from_snapshot(id, &TypeArchiveSnapshotId::unset())
468    }
469
470    /// Get all types a given type references, and any types that the referenced types reference
471    ///
472    /// * `id` - Source type id
473    /// * `snapshot` - Snapshot id to search for types
474    pub fn get_outgoing_recursive_references_from_snapshot(
475        &self,
476        id: &str,
477        snapshot: &TypeArchiveSnapshotId,
478    ) -> Array<BnString> {
479        let id = id.to_cstr();
480        let snapshot = snapshot.clone().to_cstr();
481        let mut count = 0;
482        let result = unsafe {
483            BNGetTypeArchiveOutgoingRecursiveTypeReferences(
484                self.handle.as_ptr(),
485                id.as_ptr(),
486                snapshot.as_ptr(),
487                &mut count,
488            )
489        };
490        assert!(!result.is_null());
491        unsafe { Array::new(result, count, ()) }
492    }
493
494    /// Get all types that reference a given type
495    ///
496    /// * `id` - Target type id
497    pub fn get_incoming_direct_references(&self, id: &str) -> Array<BnString> {
498        self.get_incoming_direct_references_with_snapshot(id, &TypeArchiveSnapshotId::unset())
499    }
500
501    /// Get all types that reference a given type
502    ///
503    /// * `id` - Target type id
504    /// * `snapshot` - Snapshot id to search for types
505    pub fn get_incoming_direct_references_with_snapshot(
506        &self,
507        id: &str,
508        snapshot: &TypeArchiveSnapshotId,
509    ) -> Array<BnString> {
510        let id = id.to_cstr();
511        let snapshot = snapshot.clone().to_cstr();
512        let mut count = 0;
513        let result = unsafe {
514            BNGetTypeArchiveIncomingDirectTypeReferences(
515                self.handle.as_ptr(),
516                id.as_ptr(),
517                snapshot.as_ptr(),
518                &mut count,
519            )
520        };
521        assert!(!result.is_null());
522        unsafe { Array::new(result, count, ()) }
523    }
524
525    /// Get all types that reference a given type, and all types that reference them, recursively
526    ///
527    /// * `id` - Target type id
528    pub fn get_incoming_recursive_references(&self, id: &str) -> Array<BnString> {
529        self.get_incoming_recursive_references_with_snapshot(id, &TypeArchiveSnapshotId::unset())
530    }
531
532    /// Get all types that reference a given type, and all types that reference them, recursively
533    ///
534    /// * `id` - Target type id
535    /// * `snapshot` - Snapshot id to search for types, or empty string to search the latest snapshot
536    pub fn get_incoming_recursive_references_with_snapshot(
537        &self,
538        id: &str,
539        snapshot: &TypeArchiveSnapshotId,
540    ) -> Array<BnString> {
541        let id = id.to_cstr();
542        let snapshot = snapshot.clone().to_cstr();
543        let mut count = 0;
544        let result = unsafe {
545            BNGetTypeArchiveIncomingRecursiveTypeReferences(
546                self.handle.as_ptr(),
547                id.as_ptr(),
548                snapshot.as_ptr(),
549                &mut count,
550            )
551        };
552        assert!(!result.is_null());
553        unsafe { Array::new(result, count, ()) }
554    }
555
556    /// Look up a metadata entry in the archive
557    pub fn query_metadata(&self, key: &str) -> Option<Ref<Metadata>> {
558        let key = key.to_cstr();
559        let result = unsafe { BNTypeArchiveQueryMetadata(self.handle.as_ptr(), key.as_ptr()) };
560        (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) })
561    }
562
563    /// Store a key/value pair in the archive's metadata storage
564    ///
565    /// * `key` - key value to associate the Metadata object with
566    /// * `md` - object to store.
567    pub fn store_metadata(&self, key: &str, md: &Metadata) {
568        let key = key.to_cstr();
569        let result =
570            unsafe { BNTypeArchiveStoreMetadata(self.handle.as_ptr(), key.as_ptr(), md.handle) };
571        assert!(result);
572    }
573
574    /// Delete a given metadata entry in the archive from the `key`
575    pub fn remove_metadata(&self, key: &str) -> bool {
576        let key = key.to_cstr();
577        unsafe { BNTypeArchiveRemoveMetadata(self.handle.as_ptr(), key.as_ptr()) }
578    }
579
580    /// Turn a given `snapshot` id into a data stream
581    pub fn serialize_snapshot(&self, snapshot: &TypeArchiveSnapshotId) -> DataBuffer {
582        let snapshot = snapshot.clone().to_cstr();
583        let result =
584            unsafe { BNTypeArchiveSerializeSnapshot(self.handle.as_ptr(), snapshot.as_ptr()) };
585        assert!(!result.is_null());
586        DataBuffer::from_raw(result)
587    }
588
589    /// Take a serialized snapshot `data` stream and create a new snapshot from it
590    pub fn deserialize_snapshot(&self, data: &DataBuffer) -> TypeArchiveSnapshotId {
591        let result =
592            unsafe { BNTypeArchiveDeserializeSnapshot(self.handle.as_ptr(), data.as_raw()) };
593        assert!(!result.is_null());
594        let id = unsafe { BnString::into_string(result) };
595        TypeArchiveSnapshotId(id)
596    }
597
598    /// Register a notification listener
599    pub fn register_notification_callback<T: TypeArchiveNotificationCallback>(
600        &self,
601        callback: T,
602    ) -> TypeArchiveCallbackHandle<T> {
603        // SAFETY free on [TypeArchiveCallbackHandle::Drop]
604        let callback = Box::leak(Box::new(callback));
605        let mut notification = BNTypeArchiveNotification {
606            context: callback as *mut T as *mut c_void,
607            typeAdded: Some(cb_type_added::<T>),
608            typeUpdated: Some(cb_type_updated::<T>),
609            typeRenamed: Some(cb_type_renamed::<T>),
610            typeDeleted: Some(cb_type_deleted::<T>),
611        };
612        unsafe { BNRegisterTypeArchiveNotification(self.handle.as_ptr(), &mut notification) }
613        TypeArchiveCallbackHandle {
614            callback,
615            type_archive: self.to_owned(),
616        }
617    }
618
619    // NOTE NotificationClosure is left private, there is no need for the user
620    // to know or use it.
621    #[allow(private_interfaces)]
622    pub fn register_notification_closure<A, U, R, D>(
623        &self,
624        type_added: A,
625        type_updated: U,
626        type_renamed: R,
627        type_deleted: D,
628    ) -> TypeArchiveCallbackHandle<NotificationClosure<A, U, R, D>>
629    where
630        A: FnMut(&TypeArchive, &str, &Type),
631        U: FnMut(&TypeArchive, &str, &Type, &Type),
632        R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
633        D: FnMut(&TypeArchive, &str, &Type),
634    {
635        self.register_notification_callback(NotificationClosure {
636            fun_type_added: type_added,
637            fun_type_updated: type_updated,
638            fun_type_renamed: type_renamed,
639            fun_type_deleted: type_deleted,
640        })
641    }
642
643    /// Close a type archive, disconnecting it from any active views and closing
644    /// any open file handles
645    pub fn close(&self) {
646        unsafe { BNCloseTypeArchive(self.handle.as_ptr()) }
647    }
648
649    /// Determine if `file` is a Type Archive
650    pub fn is_type_archive(file: &Path) -> bool {
651        let file = file.to_cstr();
652        unsafe { BNIsTypeArchive(file.as_ptr()) }
653    }
654
655    ///// Get the TypeContainer interface for this Type Archive, presenting types
656    ///// at the current snapshot in the archive.
657    pub fn type_container(&self) -> TypeContainer {
658        let result = unsafe { BNGetTypeArchiveTypeContainer(self.handle.as_ptr()) };
659        unsafe { TypeContainer::from_raw(NonNull::new(result).unwrap()) }
660    }
661
662    /// Do some function in a transaction making a new snapshot whose id is passed to func. If func throws,
663    /// the transaction will be rolled back and the snapshot will not be created.
664    ///
665    /// * `func` - Function to call
666    /// * `parents` - Parent snapshot ids
667    ///
668    /// Returns Created snapshot id
669    pub fn new_snapshot_transaction<F>(
670        &self,
671        mut function: F,
672        parents: &[TypeArchiveSnapshotId],
673    ) -> TypeArchiveSnapshotId
674    where
675        F: FnMut(&TypeArchiveSnapshotId) -> bool,
676    {
677        unsafe extern "C" fn cb_callback<F: FnMut(&TypeArchiveSnapshotId) -> bool>(
678            ctxt: *mut c_void,
679            id: *const c_char,
680        ) -> bool {
681            let fun: &mut F = &mut *(ctxt as *mut F);
682            let id_str = raw_to_string(id).unwrap();
683            fun(&TypeArchiveSnapshotId(id_str))
684        }
685
686        let parents_cstr: Vec<_> = parents.iter().map(|p| p.clone().to_cstr()).collect();
687        let parents_raw: Vec<_> = parents_cstr.iter().map(|p| p.as_ptr()).collect();
688        let result = unsafe {
689            BNTypeArchiveNewSnapshotTransaction(
690                self.handle.as_ptr(),
691                Some(cb_callback::<F>),
692                &mut function as *mut F as *mut c_void,
693                parents_raw.as_ptr(),
694                parents.len(),
695            )
696        };
697        assert!(!result.is_null());
698        let id_str = unsafe { BnString::into_string(result) };
699        TypeArchiveSnapshotId(id_str)
700    }
701
702    /// Merge two snapshots in the archive to produce a new snapshot
703    ///
704    /// * `base_snapshot` - Common ancestor of snapshots
705    /// * `first_snapshot` - First snapshot to merge
706    /// * `second_snapshot` - Second snapshot to merge
707    /// * `merge_conflicts` - List of all conflicting types, id <-> target snapshot
708    /// * `progress` - Function to call for progress updates
709    ///
710    /// Returns Snapshot id, if merge was successful, otherwise the List of
711    /// conflicting type ids
712    pub fn merge_snapshots<M>(
713        &self,
714        base_snapshot: &str,
715        first_snapshot: &str,
716        second_snapshot: &str,
717        merge_conflicts: M,
718    ) -> Result<BnString, Array<BnString>>
719    where
720        M: IntoIterator<Item = (String, String)>,
721    {
722        self.merge_snapshots_with_progress(
723            base_snapshot,
724            first_snapshot,
725            second_snapshot,
726            merge_conflicts,
727            NoProgressCallback,
728        )
729    }
730
731    /// Merge two snapshots in the archive to produce a new snapshot
732    ///
733    /// * `base_snapshot` - Common ancestor of snapshots
734    /// * `first_snapshot` - First snapshot to merge
735    /// * `second_snapshot` - Second snapshot to merge
736    /// * `merge_conflicts` - List of all conflicting types, id <-> target snapshot
737    /// * `progress` - Function to call for progress updates
738    ///
739    /// Returns Snapshot id, if merge was successful, otherwise the List of
740    /// conflicting type ids
741    pub fn merge_snapshots_with_progress<M, PC>(
742        &self,
743        base_snapshot: &str,
744        first_snapshot: &str,
745        second_snapshot: &str,
746        merge_conflicts: M,
747        mut progress: PC,
748    ) -> Result<BnString, Array<BnString>>
749    where
750        M: IntoIterator<Item = (String, String)>,
751        PC: ProgressCallback,
752    {
753        let base_snapshot = base_snapshot.to_cstr();
754        let first_snapshot = first_snapshot.to_cstr();
755        let second_snapshot = second_snapshot.to_cstr();
756        let (merge_keys, merge_values): (Vec<BnString>, Vec<BnString>) = merge_conflicts
757            .into_iter()
758            .map(|(k, v)| (BnString::new(k), BnString::new(v)))
759            .unzip();
760        // SAFETY BnString and `*const c_char` are transparent
761        let merge_keys_raw = merge_keys.as_ptr() as *const *const c_char;
762        let merge_values_raw = merge_values.as_ptr() as *const *const c_char;
763
764        let mut conflicts_errors = std::ptr::null_mut();
765        let mut conflicts_errors_count = 0;
766
767        let mut result = std::ptr::null_mut();
768
769        let success = unsafe {
770            BNTypeArchiveMergeSnapshots(
771                self.handle.as_ptr(),
772                base_snapshot.as_ptr(),
773                first_snapshot.as_ptr(),
774                second_snapshot.as_ptr(),
775                merge_keys_raw,
776                merge_values_raw,
777                merge_keys.len(),
778                &mut conflicts_errors,
779                &mut conflicts_errors_count,
780                &mut result,
781                Some(PC::cb_progress_callback),
782                &mut progress as *mut PC as *mut c_void,
783            )
784        };
785
786        if success {
787            assert!(!result.is_null());
788            Ok(unsafe { BnString::from_raw(result) })
789        } else {
790            assert!(!conflicts_errors.is_null());
791            Err(unsafe { Array::new(conflicts_errors, conflicts_errors_count, ()) })
792        }
793    }
794}
795
796impl ToOwned for TypeArchive {
797    type Owned = Ref<Self>;
798
799    fn to_owned(&self) -> Self::Owned {
800        unsafe { RefCountable::inc_ref(self) }
801    }
802}
803
804unsafe impl RefCountable for TypeArchive {
805    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
806        Ref::new(Self {
807            handle: NonNull::new(BNNewTypeArchiveReference(handle.handle.as_ptr())).unwrap(),
808        })
809    }
810
811    unsafe fn dec_ref(handle: &Self) {
812        BNFreeTypeArchiveReference(handle.handle.as_ptr());
813    }
814}
815
816impl PartialEq for TypeArchive {
817    fn eq(&self, other: &Self) -> bool {
818        self.id() == other.id()
819    }
820}
821impl Eq for TypeArchive {}
822
823impl Hash for TypeArchive {
824    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
825        self.id().hash(state);
826    }
827}
828
829impl Debug for TypeArchive {
830    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
831        f.debug_struct("TypeArchive")
832            .field("id", &self.id())
833            .field("path", &self.path())
834            .field("current_snapshot_id", &self.current_snapshot_id())
835            .field("platform", &self.platform())
836            .finish()
837    }
838}
839
840impl CoreArrayProvider for TypeArchive {
841    type Raw = *mut BNTypeArchive;
842    type Context = ();
843    type Wrapped<'a> = Guard<'a, TypeArchive>;
844}
845
846unsafe impl CoreArrayProviderInner for TypeArchive {
847    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
848        BNFreeTypeArchiveList(raw, count)
849    }
850
851    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
852        let raw_ptr = NonNull::new(*raw).unwrap();
853        Guard::new(Self::from_raw(raw_ptr), context)
854    }
855}
856
857pub struct TypeArchiveCallbackHandle<T: TypeArchiveNotificationCallback> {
858    callback: *mut T,
859    type_archive: Ref<TypeArchive>,
860}
861
862impl<T: TypeArchiveNotificationCallback> Drop for TypeArchiveCallbackHandle<T> {
863    fn drop(&mut self) {
864        let mut notification = BNTypeArchiveNotification {
865            context: self.callback as *mut c_void,
866            typeAdded: Some(cb_type_added::<T>),
867            typeUpdated: Some(cb_type_updated::<T>),
868            typeRenamed: Some(cb_type_renamed::<T>),
869            typeDeleted: Some(cb_type_deleted::<T>),
870        };
871        // unregister the notification callback
872        unsafe {
873            BNUnregisterTypeArchiveNotification(
874                self.type_archive.handle.as_ptr(),
875                &mut notification,
876            )
877        }
878        // free the context created at [TypeArchive::register_notification_callback]
879        drop(unsafe { Box::from_raw(self.callback) });
880    }
881}
882
883pub trait TypeArchiveNotificationCallback {
884    /// Called when a type is added to the archive
885    ///
886    /// * `archive` - Source Type archive
887    /// * `id` - Id of type added
888    /// * `definition` - Definition of type
889    fn type_added(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
890
891    /// Called when a type in the archive is updated to a new definition
892    ///
893    /// * `archive` - Source Type archive
894    /// * `id` - Id of type
895    /// * `old_definition` - Previous definition
896    /// * `new_definition` - Current definition
897    fn type_updated(
898        &mut self,
899        _archive: &TypeArchive,
900        _id: &str,
901        _old_definition: &Type,
902        _new_definition: &Type,
903    ) {
904    }
905
906    /// Called when a type in the archive is renamed
907    ///
908    /// * `archive` - Source Type archive
909    /// * `id` - Type id
910    /// * `old_name` - Previous name
911    /// * `new_name` - Current name
912    fn type_renamed(
913        &mut self,
914        _archive: &TypeArchive,
915        _id: &str,
916        _old_name: &QualifiedName,
917        _new_name: &QualifiedName,
918    ) {
919    }
920
921    /// Called when a type in the archive is deleted from the archive
922    ///
923    /// * `archive` - Source Type archive
924    /// * `id` - Id of type deleted
925    /// * `definition` - Definition of type deleted
926    fn type_deleted(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
927}
928
929struct NotificationClosure<A, U, R, D>
930where
931    A: FnMut(&TypeArchive, &str, &Type),
932    U: FnMut(&TypeArchive, &str, &Type, &Type),
933    R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
934    D: FnMut(&TypeArchive, &str, &Type),
935{
936    fun_type_added: A,
937    fun_type_updated: U,
938    fun_type_renamed: R,
939    fun_type_deleted: D,
940}
941
942impl<A, U, R, D> TypeArchiveNotificationCallback for NotificationClosure<A, U, R, D>
943where
944    A: FnMut(&TypeArchive, &str, &Type),
945    U: FnMut(&TypeArchive, &str, &Type, &Type),
946    R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
947    D: FnMut(&TypeArchive, &str, &Type),
948{
949    fn type_added(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
950        (self.fun_type_added)(archive, id, definition)
951    }
952
953    fn type_updated(
954        &mut self,
955        archive: &TypeArchive,
956        id: &str,
957        old_definition: &Type,
958        new_definition: &Type,
959    ) {
960        (self.fun_type_updated)(archive, id, old_definition, new_definition)
961    }
962
963    fn type_renamed(
964        &mut self,
965        archive: &TypeArchive,
966        id: &str,
967        old_name: &QualifiedName,
968        new_name: &QualifiedName,
969    ) {
970        (self.fun_type_renamed)(archive, id, old_name, new_name)
971    }
972
973    fn type_deleted(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
974        (self.fun_type_deleted)(archive, id, definition)
975    }
976}
977
978unsafe extern "C" fn cb_type_added<T: TypeArchiveNotificationCallback>(
979    ctxt: *mut ::std::os::raw::c_void,
980    archive: *mut BNTypeArchive,
981    id: *const ::std::os::raw::c_char,
982    definition: *mut BNType,
983) {
984    let ctxt: &mut T = &mut *(ctxt as *mut T);
985    // `archive` is owned by the caller.
986    let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
987    ctxt.type_added(
988        &archive,
989        unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() },
990        &Type { handle: definition },
991    )
992}
993unsafe extern "C" fn cb_type_updated<T: TypeArchiveNotificationCallback>(
994    ctxt: *mut ::std::os::raw::c_void,
995    archive: *mut BNTypeArchive,
996    id: *const ::std::os::raw::c_char,
997    old_definition: *mut BNType,
998    new_definition: *mut BNType,
999) {
1000    let ctxt: &mut T = &mut *(ctxt as *mut T);
1001    // `archive` is owned by the caller.
1002    let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
1003    ctxt.type_updated(
1004        &archive,
1005        unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() },
1006        &Type {
1007            handle: old_definition,
1008        },
1009        &Type {
1010            handle: new_definition,
1011        },
1012    )
1013}
1014unsafe extern "C" fn cb_type_renamed<T: TypeArchiveNotificationCallback>(
1015    ctxt: *mut ::std::os::raw::c_void,
1016    archive: *mut BNTypeArchive,
1017    id: *const ::std::os::raw::c_char,
1018    old_name: *const BNQualifiedName,
1019    new_name: *const BNQualifiedName,
1020) {
1021    let ctxt: &mut T = &mut *(ctxt as *mut T);
1022    // `old_name` is freed by the caller
1023    let old_name = QualifiedName::from_raw(&*old_name);
1024    // `new_name` is freed by the caller
1025    let new_name = QualifiedName::from_raw(&*new_name);
1026    // `archive` is owned by the caller.
1027    let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
1028    ctxt.type_renamed(
1029        &archive,
1030        unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() },
1031        &old_name,
1032        &new_name,
1033    )
1034}
1035unsafe extern "C" fn cb_type_deleted<T: TypeArchiveNotificationCallback>(
1036    ctxt: *mut ::std::os::raw::c_void,
1037    archive: *mut BNTypeArchive,
1038    id: *const ::std::os::raw::c_char,
1039    definition: *mut BNType,
1040) {
1041    let ctxt: &mut T = &mut *(ctxt as *mut T);
1042    // `archive` is owned by the caller.
1043    let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
1044    ctxt.type_deleted(
1045        &archive,
1046        unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() },
1047        &Type { handle: definition },
1048    )
1049}
1050
1051#[repr(transparent)]
1052pub struct TypeArchiveMergeConflict {
1053    handle: NonNull<BNTypeArchiveMergeConflict>,
1054}
1055
1056impl TypeArchiveMergeConflict {
1057    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeArchiveMergeConflict>) -> Self {
1058        Self { handle }
1059    }
1060
1061    #[allow(unused)]
1062    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNTypeArchiveMergeConflict>) -> Ref<Self> {
1063        Ref::new(Self { handle })
1064    }
1065
1066    pub fn get_type_archive(&self) -> Option<Ref<TypeArchive>> {
1067        let value = unsafe { BNTypeArchiveMergeConflictGetTypeArchive(self.handle.as_ptr()) };
1068        NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
1069    }
1070
1071    pub fn type_id(&self) -> String {
1072        let value = unsafe { BNTypeArchiveMergeConflictGetTypeId(self.handle.as_ptr()) };
1073        assert!(!value.is_null());
1074        unsafe { BnString::into_string(value) }
1075    }
1076
1077    pub fn base_snapshot_id(&self) -> TypeArchiveSnapshotId {
1078        let value = unsafe { BNTypeArchiveMergeConflictGetBaseSnapshotId(self.handle.as_ptr()) };
1079        assert!(!value.is_null());
1080        let id = unsafe { BnString::into_string(value) };
1081        TypeArchiveSnapshotId(id)
1082    }
1083
1084    pub fn first_snapshot_id(&self) -> TypeArchiveSnapshotId {
1085        let value = unsafe { BNTypeArchiveMergeConflictGetFirstSnapshotId(self.handle.as_ptr()) };
1086        assert!(!value.is_null());
1087        let id = unsafe { BnString::into_string(value) };
1088        TypeArchiveSnapshotId(id)
1089    }
1090
1091    pub fn second_snapshot_id(&self) -> TypeArchiveSnapshotId {
1092        let value = unsafe { BNTypeArchiveMergeConflictGetSecondSnapshotId(self.handle.as_ptr()) };
1093        assert!(!value.is_null());
1094        let id = unsafe { BnString::into_string(value) };
1095        TypeArchiveSnapshotId(id)
1096    }
1097
1098    /// Call this when you've resolved the conflict to save the result.
1099    pub fn success(&self, result: &str) -> bool {
1100        let result = result.to_cstr();
1101        unsafe { BNTypeArchiveMergeConflictSuccess(self.handle.as_ptr(), result.as_ptr()) }
1102    }
1103}
1104
1105impl Debug for TypeArchiveMergeConflict {
1106    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1107        f.debug_struct("TypeArchiveMergeConflict")
1108            .field("type_id", &self.type_id())
1109            .field("base_snapshot_id", &self.base_snapshot_id())
1110            .field("first_snapshot_id", &self.first_snapshot_id())
1111            .field("second_snapshot_id", &self.second_snapshot_id())
1112            .finish()
1113    }
1114}
1115
1116impl ToOwned for TypeArchiveMergeConflict {
1117    type Owned = Ref<Self>;
1118
1119    fn to_owned(&self) -> Self::Owned {
1120        unsafe { RefCountable::inc_ref(self) }
1121    }
1122}
1123
1124unsafe impl RefCountable for TypeArchiveMergeConflict {
1125    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
1126        Ref::new(Self {
1127            handle: NonNull::new(BNNewTypeArchiveMergeConflictReference(
1128                handle.handle.as_ptr(),
1129            ))
1130            .unwrap(),
1131        })
1132    }
1133
1134    unsafe fn dec_ref(handle: &Self) {
1135        BNFreeTypeArchiveMergeConflict(handle.handle.as_ptr());
1136    }
1137}
1138
1139impl CoreArrayProvider for TypeArchiveMergeConflict {
1140    type Raw = *mut BNTypeArchiveMergeConflict;
1141    type Context = ();
1142    type Wrapped<'a> = Guard<'a, Self>;
1143}
1144
1145unsafe impl CoreArrayProviderInner for TypeArchiveMergeConflict {
1146    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
1147        BNFreeTypeArchiveMergeConflictList(raw, count)
1148    }
1149
1150    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
1151        let raw_ptr = NonNull::new(*raw).unwrap();
1152        Guard::new(Self::from_raw(raw_ptr), context)
1153    }
1154}