binaryninja/types/
archive.rs

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