1use super::{
2 Changeset, MergeConflict, Remote, RemoteFile, RemoteFolder, RemoteProject, RemoteSnapshot,
3};
4use binaryninjacore_sys::*;
5use std::ffi::{c_char, c_void};
6use std::path::{Path, PathBuf};
7use std::ptr::NonNull;
8
9use crate::binary_view::{BinaryView, BinaryViewExt};
10use crate::database::{snapshot::Snapshot, Database};
11use crate::file_metadata::FileMetadata;
12use crate::progress::{NoProgressCallback, ProgressCallback};
13use crate::project::file::ProjectFile;
14use crate::rc::Ref;
15use crate::string::{raw_to_string, BnString, IntoCStr};
16use crate::type_archive::{TypeArchive, TypeArchiveMergeConflict};
17
18pub fn default_project_path(project: &RemoteProject) -> Result<PathBuf, ()> {
21 let result = unsafe { BNCollaborationDefaultProjectPath(project.handle.as_ptr()) };
22 let success = !result.is_null();
23 success
24 .then(|| PathBuf::from(unsafe { BnString::into_string(result) }))
25 .ok_or(())
26}
27
28pub fn default_file_path(file: &RemoteFile) -> Result<PathBuf, ()> {
32 let result = unsafe { BNCollaborationDefaultFilePath(file.handle.as_ptr()) };
33 let success = !result.is_null();
34 success
35 .then(|| PathBuf::from(unsafe { BnString::into_string(result) }))
36 .ok_or(())
37}
38
39pub fn download_file(file: &RemoteFile, db_path: &Path) -> Result<Ref<FileMetadata>, ()> {
45 download_file_with_progress(file, db_path, NoProgressCallback)
46}
47
48pub fn download_file_with_progress<F: ProgressCallback>(
55 file: &RemoteFile,
56 db_path: &Path,
57 mut progress: F,
58) -> Result<Ref<FileMetadata>, ()> {
59 let db_path = db_path.to_cstr();
60 let result = unsafe {
61 BNCollaborationDownloadFile(
62 file.handle.as_ptr(),
63 db_path.as_ptr(),
64 Some(F::cb_progress_callback),
65 &mut progress as *mut F as *mut c_void,
66 )
67 };
68 let success = !result.is_null();
69 success
70 .then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
71 .ok_or(())
72}
73
74pub fn upload_database<N: NameChangeset>(
81 project: &RemoteProject,
82 parent_folder: Option<&RemoteFolder>,
83 metadata: &FileMetadata,
84 name_changeset: N,
85) -> Result<Ref<RemoteFile>, ()> {
86 upload_database_with_progress(
87 project,
88 parent_folder,
89 metadata,
90 name_changeset,
91 NoProgressCallback,
92 )
93}
94
95pub fn upload_database_with_progress<P: ProgressCallback, N: NameChangeset>(
103 project: &RemoteProject,
104 parent_folder: Option<&RemoteFolder>,
105 metadata: &FileMetadata,
106 mut name_changeset: N,
107 mut progress: P,
108) -> Result<Ref<RemoteFile>, ()> {
109 let folder_raw = parent_folder.map_or(std::ptr::null_mut(), |h| h.handle.as_ptr());
110 let result = unsafe {
111 BNCollaborationUploadDatabase(
112 metadata.handle,
113 project.handle.as_ptr(),
114 folder_raw,
115 Some(P::cb_progress_callback),
116 &mut progress as *mut P as *mut c_void,
117 Some(N::cb_name_changeset),
118 &mut name_changeset as *mut N as *mut c_void,
119 )
120 };
121 NonNull::new(result)
122 .map(|raw| unsafe { RemoteFile::ref_from_raw(raw) })
123 .ok_or(())
124}
125
126pub fn is_collaboration_database(database: &Database) -> bool {
128 unsafe { BNCollaborationIsCollaborationDatabase(database.handle.as_ptr()) }
129}
130
131pub fn get_remote_for_local_database(database: &Database) -> Result<Option<Ref<Remote>>, ()> {
133 let mut value = std::ptr::null_mut();
134 let success =
135 unsafe { BNCollaborationGetRemoteForLocalDatabase(database.handle.as_ptr(), &mut value) };
136 success
137 .then(|| NonNull::new(value).map(|handle| unsafe { Remote::ref_from_raw(handle) }))
138 .ok_or(())
139}
140
141pub fn get_remote_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<Remote>>, ()> {
143 let Some(db) = bv.file().database() else {
144 return Ok(None);
145 };
146 get_remote_for_local_database(&db)
147}
148
149pub fn get_remote_project_for_local_database(
152 database: &Database,
153) -> Result<Option<Ref<RemoteProject>>, ()> {
154 let mut value = std::ptr::null_mut();
155 let success = unsafe {
156 BNCollaborationGetRemoteProjectForLocalDatabase(database.handle.as_ptr(), &mut value)
157 };
158 success
159 .then(|| NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
160 .ok_or(())
161}
162
163pub fn get_remote_file_for_local_database(
165 database: &Database,
166) -> Result<Option<Ref<RemoteFile>>, ()> {
167 let mut value = std::ptr::null_mut();
168 let success = unsafe {
169 BNCollaborationGetRemoteFileForLocalDatabase(database.handle.as_ptr(), &mut value)
170 };
171 success
172 .then(|| NonNull::new(value).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) }))
173 .ok_or(())
174}
175
176pub fn assign_snapshot_map(
178 local_snapshot: &Snapshot,
179 remote_snapshot: &RemoteSnapshot,
180) -> Result<(), ()> {
181 let success = unsafe {
182 BNCollaborationAssignSnapshotMap(
183 local_snapshot.handle.as_ptr(),
184 remote_snapshot.handle.as_ptr(),
185 )
186 };
187 success.then_some(()).ok_or(())
188}
189
190pub fn get_remote_snapshot_from_local(snap: &Snapshot) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
192 let mut value = std::ptr::null_mut();
193 let success =
194 unsafe { BNCollaborationGetRemoteSnapshotFromLocal(snap.handle.as_ptr(), &mut value) };
195 success
196 .then(|| NonNull::new(value).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) }))
197 .ok_or(())
198}
199
200pub fn get_local_snapshot_for_remote(
202 snapshot: &RemoteSnapshot,
203 database: &Database,
204) -> Result<Option<Ref<Snapshot>>, ()> {
205 let mut value = std::ptr::null_mut();
206 let success = unsafe {
207 BNCollaborationGetLocalSnapshotFromRemote(
208 snapshot.handle.as_ptr(),
209 database.handle.as_ptr(),
210 &mut value,
211 )
212 };
213 success
214 .then(|| NonNull::new(value).map(|handle| unsafe { Snapshot::ref_from_raw(handle) }))
215 .ok_or(())
216}
217
218pub fn download_database<S>(file: &RemoteFile, location: &Path, force: bool) -> Result<(), ()> {
219 download_database_with_progress(file, location, force, NoProgressCallback)
220}
221
222pub fn download_database_with_progress<PC>(
223 file: &RemoteFile,
224 location: &Path,
225 force: bool,
226 mut progress: PC,
227) -> Result<(), ()>
228where
229 PC: ProgressCallback,
230{
231 let db_path = location.to_cstr();
232 let success = unsafe {
233 BNCollaborationDownloadDatabaseForFile(
234 file.handle.as_ptr(),
235 db_path.as_ptr(),
236 force,
237 Some(PC::cb_progress_callback),
238 &mut progress as *mut PC as *mut c_void,
239 )
240 };
241 success.then_some(()).ok_or(())
242}
243
244pub fn sync_database<C: DatabaseConflictHandler, N: NameChangeset>(
251 database: &Database,
252 file: &RemoteFile,
253 conflict_handler: C,
254 name_changeset: N,
255) -> Result<(), ()> {
256 sync_database_with_progress(
257 database,
258 file,
259 conflict_handler,
260 name_changeset,
261 NoProgressCallback,
262 )
263}
264
265pub fn sync_database_with_progress<
273 C: DatabaseConflictHandler,
274 P: ProgressCallback,
275 N: NameChangeset,
276>(
277 database: &Database,
278 file: &RemoteFile,
279 mut conflict_handler: C,
280 mut name_changeset: N,
281 mut progress: P,
282) -> Result<(), ()> {
283 let success = unsafe {
284 BNCollaborationSyncDatabase(
285 database.handle.as_ptr(),
286 file.handle.as_ptr(),
287 Some(C::cb_handle_conflict),
288 &mut conflict_handler as *mut C as *mut c_void,
289 Some(P::cb_progress_callback),
290 &mut progress as *mut P as *mut c_void,
291 Some(N::cb_name_changeset),
292 &mut name_changeset as *mut N as *mut c_void,
293 )
294 };
295 success.then_some(()).ok_or(())
296}
297
298pub fn pull_database<C: DatabaseConflictHandler, N: NameChangeset>(
306 database: &Database,
307 file: &RemoteFile,
308 conflict_handler: C,
309 name_changeset: N,
310) -> Result<usize, ()> {
311 pull_database_with_progress(
312 database,
313 file,
314 conflict_handler,
315 name_changeset,
316 NoProgressCallback,
317 )
318}
319
320pub fn pull_database_with_progress<
329 C: DatabaseConflictHandler,
330 P: ProgressCallback,
331 N: NameChangeset,
332>(
333 database: &Database,
334 file: &RemoteFile,
335 mut conflict_handler: C,
336 mut name_changeset: N,
337 mut progress: P,
338) -> Result<usize, ()> {
339 let mut count = 0;
340 let success = unsafe {
341 BNCollaborationPullDatabase(
342 database.handle.as_ptr(),
343 file.handle.as_ptr(),
344 &mut count,
345 Some(C::cb_handle_conflict),
346 &mut conflict_handler as *mut C as *mut c_void,
347 Some(P::cb_progress_callback),
348 &mut progress as *mut P as *mut c_void,
349 Some(N::cb_name_changeset),
350 &mut name_changeset as *mut N as *mut c_void,
351 )
352 };
353 success.then_some(count).ok_or(())
354}
355
356pub fn merge_database<C: DatabaseConflictHandler>(
361 database: &Database,
362 conflict_handler: C,
363) -> Result<(), ()> {
364 merge_database_with_progress(database, conflict_handler, NoProgressCallback)
365}
366
367pub fn merge_database_with_progress<C: DatabaseConflictHandler, P: ProgressCallback>(
373 database: &Database,
374 mut conflict_handler: C,
375 mut progress: P,
376) -> Result<(), ()> {
377 let success = unsafe {
378 BNCollaborationMergeDatabase(
379 database.handle.as_ptr(),
380 Some(C::cb_handle_conflict),
381 &mut conflict_handler as *mut C as *mut c_void,
382 Some(P::cb_progress_callback),
383 &mut progress as *mut P as *mut c_void,
384 )
385 };
386 success.then_some(()).ok_or(())
387}
388
389pub fn push_database(database: &Database, file: &RemoteFile) -> Result<usize, ()> {
394 push_database_with_progress(database, file, NoProgressCallback)
395}
396
397pub fn push_database_with_progress<P: ProgressCallback>(
403 database: &Database,
404 file: &RemoteFile,
405 mut progress: P,
406) -> Result<usize, ()> {
407 let mut count = 0;
408 let success = unsafe {
409 BNCollaborationPushDatabase(
410 database.handle.as_ptr(),
411 file.handle.as_ptr(),
412 &mut count,
413 Some(P::cb_progress_callback),
414 &mut progress as *mut P as *mut c_void,
415 )
416 };
417 success.then_some(count).ok_or(())
418}
419
420pub fn dump_database(database: &Database) -> Result<(), ()> {
422 let success = unsafe { BNCollaborationDumpDatabase(database.handle.as_ptr()) };
423 success.then_some(()).ok_or(())
424}
425
426pub fn ignore_snapshot(database: &Database, snapshot: &Snapshot) -> Result<(), ()> {
431 let success = unsafe {
432 BNCollaborationIgnoreSnapshot(database.handle.as_ptr(), snapshot.handle.as_ptr())
433 };
434 success.then_some(()).ok_or(())
435}
436
437pub fn is_snapshot_ignored(database: &Database, snapshot: &Snapshot) -> bool {
442 unsafe { BNCollaborationIsSnapshotIgnored(database.handle.as_ptr(), snapshot.handle.as_ptr()) }
443}
444
445pub fn get_snapshot_author(
450 database: &Database,
451 snapshot: &Snapshot,
452) -> Result<Option<BnString>, ()> {
453 let mut value = std::ptr::null_mut();
454 let success = unsafe {
455 BNCollaborationGetSnapshotAuthor(
456 database.handle.as_ptr(),
457 snapshot.handle.as_ptr(),
458 &mut value,
459 )
460 };
461 success
462 .then(|| (!value.is_null()).then(|| unsafe { BnString::from_raw(value) }))
463 .ok_or(())
464}
465
466pub fn set_snapshot_author(
472 database: &Database,
473 snapshot: &Snapshot,
474 author: &str,
475) -> Result<(), ()> {
476 let author = author.to_cstr();
477 let success = unsafe {
478 BNCollaborationSetSnapshotAuthor(
479 database.handle.as_ptr(),
480 snapshot.handle.as_ptr(),
481 author.as_ptr(),
482 )
483 };
484 success.then_some(()).ok_or(())
485}
486
487pub(crate) fn pull_projects(database: &Database) -> Result<bool, ()> {
489 let Some(remote) = get_remote_for_local_database(database)? else {
490 return Ok(false);
491 };
492 remote.pull_projects()?;
493 Ok(true)
494}
495
496pub(crate) fn pull_files(database: &Database) -> Result<bool, ()> {
498 if !pull_projects(database)? {
499 return Ok(false);
500 }
501 let Some(project) = get_remote_project_for_local_database(database)? else {
502 return Ok(false);
503 };
504 project.pull_files()?;
505 Ok(true)
506}
507
508pub fn sync_type_archive<C: TypeArchiveConflictHandler>(
514 type_archive: &TypeArchive,
515 file: &RemoteFile,
516 conflict_handler: C,
517) -> Result<(), ()> {
518 sync_type_archive_with_progress(type_archive, file, conflict_handler, NoProgressCallback)
519}
520
521pub fn sync_type_archive_with_progress<C: TypeArchiveConflictHandler, P: ProgressCallback>(
528 type_archive: &TypeArchive,
529 file: &RemoteFile,
530 mut conflict_handler: C,
531 mut progress: P,
532) -> Result<(), ()> {
533 let success = unsafe {
534 BNCollaborationSyncTypeArchive(
535 type_archive.handle.as_ptr(),
536 file.handle.as_ptr(),
537 Some(C::cb_handle_conflict),
538 &mut conflict_handler as *mut C as *mut c_void,
539 Some(P::cb_progress_callback),
540 &mut progress as *mut P as *mut c_void,
541 )
542 };
543 success.then_some(()).ok_or(())
544}
545
546pub fn push_type_archive(type_archive: &TypeArchive, file: &RemoteFile) -> Result<usize, ()> {
551 push_type_archive_with_progress(type_archive, file, NoProgressCallback)
552}
553
554pub fn push_type_archive_with_progress<P: ProgressCallback>(
560 type_archive: &TypeArchive,
561 file: &RemoteFile,
562 mut progress: P,
563) -> Result<usize, ()> {
564 let mut count = 0;
565 let success = unsafe {
566 BNCollaborationPushTypeArchive(
567 type_archive.handle.as_ptr(),
568 file.handle.as_ptr(),
569 &mut count,
570 Some(P::cb_progress_callback),
571 &mut progress as *mut P as *mut c_void,
572 )
573 };
574 success.then_some(count).ok_or(())
575}
576
577pub fn pull_type_archive<C: TypeArchiveConflictHandler>(
583 type_archive: &TypeArchive,
584 file: &RemoteFile,
585 conflict_handler: C,
586) -> Result<usize, ()> {
587 pull_type_archive_with_progress(type_archive, file, conflict_handler, NoProgressCallback)
588}
589
590pub fn pull_type_archive_with_progress<C: TypeArchiveConflictHandler, P: ProgressCallback>(
597 type_archive: &TypeArchive,
598 file: &RemoteFile,
599 mut conflict_handler: C,
600 mut progress: P,
601) -> Result<usize, ()> {
602 let mut count = 0;
603 let success = unsafe {
604 BNCollaborationPullTypeArchive(
605 type_archive.handle.as_ptr(),
606 file.handle.as_ptr(),
607 &mut count,
608 Some(C::cb_handle_conflict),
609 &mut conflict_handler as *mut C as *mut c_void,
610 Some(P::cb_progress_callback),
611 &mut progress as *mut P as *mut c_void,
612 )
613 };
614 success.then_some(count).ok_or(())
615}
616
617pub fn is_collaboration_type_archive(type_archive: &TypeArchive) -> bool {
619 unsafe { BNCollaborationIsCollaborationTypeArchive(type_archive.handle.as_ptr()) }
620}
621
622pub fn get_remote_for_local_type_archive(type_archive: &TypeArchive) -> Option<Ref<Remote>> {
624 let value =
625 unsafe { BNCollaborationGetRemoteForLocalTypeArchive(type_archive.handle.as_ptr()) };
626 NonNull::new(value).map(|handle| unsafe { Remote::ref_from_raw(handle) })
627}
628
629pub fn get_remote_project_for_local_type_archive(
631 database: &TypeArchive,
632) -> Option<Ref<RemoteProject>> {
633 let value =
634 unsafe { BNCollaborationGetRemoteProjectForLocalTypeArchive(database.handle.as_ptr()) };
635 NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
636}
637
638pub fn get_remote_file_for_local_type_archive(database: &TypeArchive) -> Option<Ref<RemoteFile>> {
640 let value =
641 unsafe { BNCollaborationGetRemoteFileForLocalTypeArchive(database.handle.as_ptr()) };
642 NonNull::new(value).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })
643}
644
645pub fn get_remote_snapshot_from_local_type_archive(
647 type_archive: &TypeArchive,
648 snapshot_id: &str,
649) -> Option<Ref<RemoteSnapshot>> {
650 let snapshot_id = snapshot_id.to_cstr();
651 let value = unsafe {
652 BNCollaborationGetRemoteSnapshotFromLocalTypeArchive(
653 type_archive.handle.as_ptr(),
654 snapshot_id.as_ptr(),
655 )
656 };
657 NonNull::new(value).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) })
658}
659
660pub fn get_local_snapshot_from_remote_type_archive(
662 snapshot: &RemoteSnapshot,
663 type_archive: &TypeArchive,
664) -> Option<BnString> {
665 let value = unsafe {
666 BNCollaborationGetLocalSnapshotFromRemoteTypeArchive(
667 snapshot.handle.as_ptr(),
668 type_archive.handle.as_ptr(),
669 )
670 };
671 (!value.is_null()).then(|| unsafe { BnString::from_raw(value) })
672}
673
674pub fn is_type_archive_snapshot_ignored(type_archive: &TypeArchive, snapshot_id: &str) -> bool {
676 let snapshot_id = snapshot_id.to_cstr();
677 unsafe {
678 BNCollaborationIsTypeArchiveSnapshotIgnored(
679 type_archive.handle.as_ptr(),
680 snapshot_id.as_ptr(),
681 )
682 }
683}
684
685pub fn download_type_archive(
688 file: &RemoteFile,
689 location: &Path,
690) -> Result<Option<Ref<TypeArchive>>, ()> {
691 download_type_archive_with_progress(file, location, NoProgressCallback)
692}
693
694pub fn download_type_archive_with_progress<PC: ProgressCallback>(
697 file: &RemoteFile,
698 location: &Path,
699 mut progress: PC,
700) -> Result<Option<Ref<TypeArchive>>, ()> {
701 let mut value = std::ptr::null_mut();
702 let db_path = location.to_cstr();
703 let success = unsafe {
704 BNCollaborationDownloadTypeArchive(
705 file.handle.as_ptr(),
706 db_path.as_ptr(),
707 Some(PC::cb_progress_callback),
708 &mut progress as *mut PC as *mut c_void,
709 &mut value,
710 )
711 };
712 success
713 .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }))
714 .ok_or(())
715}
716
717pub fn upload_type_archive(
719 archive: &TypeArchive,
720 project: &RemoteProject,
721 folder: &RemoteFolder,
723 core_file: &ProjectFile,
724) -> Result<Ref<RemoteFile>, ()> {
725 upload_type_archive_with_progress(archive, project, folder, core_file, NoProgressCallback)
726}
727
728pub fn upload_type_archive_with_progress<P: ProgressCallback>(
730 archive: &TypeArchive,
731 project: &RemoteProject,
732 folder: &RemoteFolder,
734 core_file: &ProjectFile,
736 mut progress: P,
737) -> Result<Ref<RemoteFile>, ()> {
738 let mut value = std::ptr::null_mut();
739 let success = unsafe {
740 BNCollaborationUploadTypeArchive(
741 archive.handle.as_ptr(),
742 project.handle.as_ptr(),
743 folder.handle.as_ptr(),
744 Some(P::cb_progress_callback),
745 &mut progress as *const P as *mut c_void,
746 core_file.handle.as_ptr(),
747 &mut value,
748 )
749 };
750 success
751 .then(|| {
752 NonNull::new(value)
753 .map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })
754 .unwrap()
755 })
756 .ok_or(())
757}
758
759pub fn merge_snapshots<C: DatabaseConflictHandler>(
761 first: &Snapshot,
762 second: &Snapshot,
763 conflict_handler: C,
764) -> Result<Snapshot, ()> {
765 merge_snapshots_with_progress(first, second, conflict_handler, NoProgressCallback)
766}
767
768pub fn merge_snapshots_with_progress<C: DatabaseConflictHandler, P: ProgressCallback>(
770 first: &Snapshot,
771 second: &Snapshot,
772 mut conflict_handler: C,
773 mut progress: P,
774) -> Result<Snapshot, ()> {
775 let value = unsafe {
776 BNCollaborationMergeSnapshots(
777 first.handle.as_ptr(),
778 second.handle.as_ptr(),
779 Some(C::cb_handle_conflict),
780 &mut conflict_handler as *mut C as *mut c_void,
781 Some(P::cb_progress_callback),
782 &mut progress as *mut P as *mut c_void,
783 )
784 };
785 NonNull::new(value)
786 .map(|handle| unsafe { Snapshot::from_raw(handle) })
787 .ok_or(())
788}
789
790pub trait NameChangeset: Sized {
791 fn name_changeset(&mut self, changeset: &Changeset) -> bool;
792
793 unsafe extern "C" fn cb_name_changeset(
794 ctxt: *mut ::std::os::raw::c_void,
795 changeset: *mut BNCollaborationChangeset,
796 ) -> bool {
797 let ctxt: &mut Self = &mut *(ctxt as *mut Self);
798 let raw_changeset_ptr = NonNull::new(changeset).unwrap();
799 let changeset = Changeset::from_raw(raw_changeset_ptr);
801 ctxt.name_changeset(&changeset)
802 }
803}
804
805impl<F> NameChangeset for F
806where
807 F: for<'a> FnMut(&'a Changeset) -> bool,
808{
809 fn name_changeset(&mut self, changeset: &Changeset) -> bool {
810 self(changeset)
811 }
812}
813
814pub struct NoNameChangeset;
815
816impl NameChangeset for NoNameChangeset {
817 fn name_changeset(&mut self, _changeset: &Changeset) -> bool {
818 unreachable!()
819 }
820
821 unsafe extern "C" fn cb_name_changeset(
822 _ctxt: *mut std::os::raw::c_void,
823 _changeset: *mut BNCollaborationChangeset,
824 ) -> bool {
825 true
826 }
827}
828
829pub trait DatabaseConflictHandler: Sized {
831 fn handle_conflict(&mut self, keys: &str, conflicts: &MergeConflict) -> bool;
837
838 unsafe extern "C" fn cb_handle_conflict(
839 ctxt: *mut c_void,
840 keys: *mut *const c_char,
841 conflicts: *mut *mut BNAnalysisMergeConflict,
842 conflict_count: usize,
843 ) -> bool {
844 let ctxt: &mut Self = &mut *(ctxt as *mut Self);
845 let keys = core::slice::from_raw_parts(keys, conflict_count);
846 let conflicts = core::slice::from_raw_parts(conflicts, conflict_count);
847 keys.iter().zip(conflicts.iter()).all(|(key, conflict)| {
848 let key = raw_to_string(*key).unwrap();
849 let raw_ptr = NonNull::new(*conflict).unwrap();
851 let conflict = MergeConflict::from_raw(raw_ptr);
852 ctxt.handle_conflict(&key, &conflict)
853 })
854 }
855}
856
857impl<F> DatabaseConflictHandler for F
858where
859 F: for<'a> FnMut(&'a str, &'a MergeConflict) -> bool,
860{
861 fn handle_conflict(&mut self, keys: &str, conflicts: &MergeConflict) -> bool {
862 self(keys, conflicts)
863 }
864}
865
866pub struct DatabaseConflictHandlerFail;
867impl DatabaseConflictHandler for DatabaseConflictHandlerFail {
868 fn handle_conflict(&mut self, _keys: &str, _conflicts: &MergeConflict) -> bool {
869 unreachable!()
870 }
871
872 unsafe extern "C" fn cb_handle_conflict(
873 _ctxt: *mut c_void,
874 _keys: *mut *const c_char,
875 _conflicts: *mut *mut BNAnalysisMergeConflict,
876 conflict_count: usize,
877 ) -> bool {
878 conflict_count > 0
880 }
881}
882
883pub trait TypeArchiveConflictHandler: Sized {
884 fn handle_conflict(&mut self, conflicts: &TypeArchiveMergeConflict) -> bool;
885 unsafe extern "C" fn cb_handle_conflict(
886 ctxt: *mut ::std::os::raw::c_void,
887 conflicts: *mut *mut BNTypeArchiveMergeConflict,
888 conflict_count: usize,
889 ) -> bool {
890 let ctx: &mut Self = &mut *(ctxt as *mut Self);
891 let conflicts_raw = core::slice::from_raw_parts(conflicts, conflict_count);
893 conflicts_raw
894 .iter()
895 .map(|t| NonNull::new_unchecked(*t))
896 .map(|t| TypeArchiveMergeConflict::from_raw(t))
897 .all(|conflict| ctx.handle_conflict(&conflict))
898 }
899}
900
901impl<F> TypeArchiveConflictHandler for F
902where
903 F: for<'a> FnMut(&'a TypeArchiveMergeConflict) -> bool,
904{
905 fn handle_conflict(&mut self, conflicts: &TypeArchiveMergeConflict) -> bool {
906 self(conflicts)
907 }
908}
909
910pub struct TypeArchiveConflictHandlerFail;
911impl TypeArchiveConflictHandler for TypeArchiveConflictHandlerFail {
912 fn handle_conflict(&mut self, _conflicts: &TypeArchiveMergeConflict) -> bool {
913 unreachable!()
914 }
915
916 unsafe extern "C" fn cb_handle_conflict(
917 _ctxt: *mut c_void,
918 _conflicts: *mut *mut BNTypeArchiveMergeConflict,
919 _conflict_count: usize,
920 ) -> bool {
921 false
924 }
925}