use binaryninjacore_sys::*; use std::ptr::NonNull; use crate::database::{snapshot::Snapshot, Database}; use crate::file_metadata::FileMetadata; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnString, IntoCStr}; pub type MergeConflictDataType = BNMergeConflictDataType; /// Structure representing an individual merge conflict #[repr(transparent)] pub struct MergeConflict { handle: NonNull, } impl MergeConflict { pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { Self { handle } } #[allow(unused)] pub(crate) unsafe fn ref_from_raw(handle: NonNull) -> Ref { Ref::new(Self { handle }) } /// Database backing all snapshots in the merge conflict pub fn database(&self) -> Database { let result = unsafe { BNAnalysisMergeConflictGetDatabase(self.handle.as_ptr()) }; unsafe { Database::from_raw(NonNull::new(result).unwrap()) } } /// Snapshot which is the parent of the two being merged pub fn base_snapshot(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetBaseSnapshot(self.handle.as_ptr()) }; NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } /// First snapshot being merged pub fn first_snapshot(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetFirstSnapshot(self.handle.as_ptr()) }; NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } /// Second snapshot being merged pub fn second_snapshot(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetSecondSnapshot(self.handle.as_ptr()) }; NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } pub fn path_item_string(&self, path: &str) -> Result { let path = path.to_cstr(); let result = unsafe { BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path.as_ptr()) }; (!result.is_null()) .then(|| unsafe { BnString::from_raw(result) }) .ok_or(()) } /// FileMetadata with contents of file for base snapshot /// This function is slow! Only use it if you really need it. pub fn base_file(&self) -> Option> { let result = unsafe { BNAnalysisMergeConflictGetBaseFile(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) }) } /// FileMetadata with contents of file for first snapshot /// This function is slow! Only use it if you really need it. pub fn first_file(&self) -> Option> { let result = unsafe { BNAnalysisMergeConflictGetFirstFile(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) }) } /// FileMetadata with contents of file for second snapshot /// This function is slow! Only use it if you really need it. pub fn second_file(&self) -> Option> { let result = unsafe { BNAnalysisMergeConflictGetSecondFile(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) }) } /// Json String for conflicting data in the base snapshot pub fn base(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetBase(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) } /// Json object for conflicting data in the base snapshot pub fn first(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetFirst(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) } /// Json object for conflicting data in the second snapshot pub fn second(&self) -> Option { let result = unsafe { BNAnalysisMergeConflictGetSecond(self.handle.as_ptr()) }; (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) } /// Type of data in the conflict, Text/Json/Binary pub fn data_type(&self) -> MergeConflictDataType { unsafe { BNAnalysisMergeConflictGetDataType(self.handle.as_ptr()) } } /// String representing the type name of the data, not the same as data_type. /// This is like "typeName" or "tag" depending on what object the conflict represents. pub fn conflict_type(&self) -> String { let result = unsafe { BNAnalysisMergeConflictGetType(self.handle.as_ptr()) }; assert!(!result.is_null()); unsafe { BnString::into_string(result) } } /// Lookup key for the merge conflict, ideally a tree path that contains the name of the conflict /// and all the recursive children leading up to this conflict. pub fn key(&self) -> String { let result = unsafe { BNAnalysisMergeConflictGetKey(self.handle.as_ptr()) }; assert!(!result.is_null()); unsafe { BnString::into_string(result) } } /// Call this when you've resolved the conflict to save the result pub fn success(&self, value: &str) -> Result<(), ()> { let value = value.to_cstr(); let success = unsafe { BNAnalysisMergeConflictSuccess(self.handle.as_ptr(), value.as_ptr()) }; success.then_some(()).ok_or(()) } // TODO: Make a safe version of this that checks the path and if it holds a number pub unsafe fn get_path_item_number(&self, path_key: &str) -> Option { let path_key = path_key.to_cstr(); let value = unsafe { BNAnalysisMergeConflictGetPathItem(self.handle.as_ptr(), path_key.as_ptr()) }; match value.is_null() { // SAFETY: The path must be a number. false => Some(value as u64), true => None, } } pub unsafe fn get_path_item_string(&self, path_key: &str) -> Option { let path_key = path_key.to_cstr(); let value = unsafe { BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path_key.as_ptr()) }; match value.is_null() { false => Some(unsafe { BnString::from_raw(value) }), true => None, } } } impl ToOwned for MergeConflict { type Owned = Ref; fn to_owned(&self) -> Self::Owned { unsafe { RefCountable::inc_ref(self) } } } unsafe impl RefCountable for MergeConflict { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { handle: NonNull::new(BNNewAnalysisMergeConflictReference(handle.handle.as_ptr())) .unwrap(), }) } unsafe fn dec_ref(handle: &Self) { BNFreeAnalysisMergeConflict(handle.handle.as_ptr()); } } impl CoreArrayProvider for MergeConflict { type Raw = *mut BNAnalysisMergeConflict; type Context = (); type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for MergeConflict { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeAnalysisMergeConflictList(raw, count) } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { let raw_ptr = NonNull::new(*raw).unwrap(); Guard::new(Self::from_raw(raw_ptr), context) } }