binaryninja/collaboration/
merge.rs

1use binaryninjacore_sys::*;
2use std::ptr::NonNull;
3
4use crate::database::{snapshot::Snapshot, Database};
5use crate::file_metadata::FileMetadata;
6use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
7use crate::string::{BnString, IntoCStr};
8
9pub type MergeConflictDataType = BNMergeConflictDataType;
10
11/// Structure representing an individual merge conflict
12#[repr(transparent)]
13pub struct MergeConflict {
14    handle: NonNull<BNAnalysisMergeConflict>,
15}
16
17impl MergeConflict {
18    pub(crate) unsafe fn from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Self {
19        Self { handle }
20    }
21
22    #[allow(unused)]
23    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Ref<Self> {
24        Ref::new(Self { handle })
25    }
26
27    /// Database backing all snapshots in the merge conflict
28    pub fn database(&self) -> Database {
29        let result = unsafe { BNAnalysisMergeConflictGetDatabase(self.handle.as_ptr()) };
30        unsafe { Database::from_raw(NonNull::new(result).unwrap()) }
31    }
32
33    /// Snapshot which is the parent of the two being merged
34    pub fn base_snapshot(&self) -> Option<Snapshot> {
35        let result = unsafe { BNAnalysisMergeConflictGetBaseSnapshot(self.handle.as_ptr()) };
36        NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
37    }
38
39    /// First snapshot being merged
40    pub fn first_snapshot(&self) -> Option<Snapshot> {
41        let result = unsafe { BNAnalysisMergeConflictGetFirstSnapshot(self.handle.as_ptr()) };
42        NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
43    }
44
45    /// Second snapshot being merged
46    pub fn second_snapshot(&self) -> Option<Snapshot> {
47        let result = unsafe { BNAnalysisMergeConflictGetSecondSnapshot(self.handle.as_ptr()) };
48        NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
49    }
50
51    pub fn path_item_string(&self, path: &str) -> Result<BnString, ()> {
52        let path = path.to_cstr();
53        let result = unsafe {
54            BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path.as_ptr())
55        };
56        (!result.is_null())
57            .then(|| unsafe { BnString::from_raw(result) })
58            .ok_or(())
59    }
60
61    /// FileMetadata with contents of file for base snapshot
62    /// This function is slow! Only use it if you really need it.
63    pub fn base_file(&self) -> Option<Ref<FileMetadata>> {
64        let result = unsafe { BNAnalysisMergeConflictGetBaseFile(self.handle.as_ptr()) };
65        (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
66    }
67
68    /// FileMetadata with contents of file for first snapshot
69    /// This function is slow! Only use it if you really need it.
70    pub fn first_file(&self) -> Option<Ref<FileMetadata>> {
71        let result = unsafe { BNAnalysisMergeConflictGetFirstFile(self.handle.as_ptr()) };
72        (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
73    }
74
75    /// FileMetadata with contents of file for second snapshot
76    /// This function is slow! Only use it if you really need it.
77    pub fn second_file(&self) -> Option<Ref<FileMetadata>> {
78        let result = unsafe { BNAnalysisMergeConflictGetSecondFile(self.handle.as_ptr()) };
79        (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
80    }
81
82    /// Json String for conflicting data in the base snapshot
83    pub fn base(&self) -> Option<BnString> {
84        let result = unsafe { BNAnalysisMergeConflictGetBase(self.handle.as_ptr()) };
85        (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
86    }
87
88    /// Json object for conflicting data in the base snapshot
89    pub fn first(&self) -> Option<BnString> {
90        let result = unsafe { BNAnalysisMergeConflictGetFirst(self.handle.as_ptr()) };
91        (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
92    }
93
94    /// Json object for conflicting data in the second snapshot
95    pub fn second(&self) -> Option<BnString> {
96        let result = unsafe { BNAnalysisMergeConflictGetSecond(self.handle.as_ptr()) };
97        (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
98    }
99
100    /// Type of data in the conflict, Text/Json/Binary
101    pub fn data_type(&self) -> MergeConflictDataType {
102        unsafe { BNAnalysisMergeConflictGetDataType(self.handle.as_ptr()) }
103    }
104
105    /// String representing the type name of the data, not the same as data_type.
106    /// This is like "typeName" or "tag" depending on what object the conflict represents.
107    pub fn conflict_type(&self) -> String {
108        let result = unsafe { BNAnalysisMergeConflictGetType(self.handle.as_ptr()) };
109        assert!(!result.is_null());
110        unsafe { BnString::into_string(result) }
111    }
112
113    /// Lookup key for the merge conflict, ideally a tree path that contains the name of the conflict
114    /// and all the recursive children leading up to this conflict.
115    pub fn key(&self) -> String {
116        let result = unsafe { BNAnalysisMergeConflictGetKey(self.handle.as_ptr()) };
117        assert!(!result.is_null());
118        unsafe { BnString::into_string(result) }
119    }
120
121    /// Call this when you've resolved the conflict to save the result
122    pub fn success(&self, value: &str) -> Result<(), ()> {
123        let value = value.to_cstr();
124        let success =
125            unsafe { BNAnalysisMergeConflictSuccess(self.handle.as_ptr(), value.as_ptr()) };
126        success.then_some(()).ok_or(())
127    }
128
129    // TODO: Make a safe version of this that checks the path and if it holds a number
130    pub unsafe fn get_path_item_number(&self, path_key: &str) -> Option<u64> {
131        let path_key = path_key.to_cstr();
132        let value =
133            unsafe { BNAnalysisMergeConflictGetPathItem(self.handle.as_ptr(), path_key.as_ptr()) };
134        match value.is_null() {
135            // SAFETY: The path must be a number.
136            false => Some(value as u64),
137            true => None,
138        }
139    }
140
141    pub unsafe fn get_path_item_string(&self, path_key: &str) -> Option<BnString> {
142        let path_key = path_key.to_cstr();
143        let value = unsafe {
144            BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path_key.as_ptr())
145        };
146        match value.is_null() {
147            false => Some(unsafe { BnString::from_raw(value) }),
148            true => None,
149        }
150    }
151}
152
153impl ToOwned for MergeConflict {
154    type Owned = Ref<Self>;
155
156    fn to_owned(&self) -> Self::Owned {
157        unsafe { RefCountable::inc_ref(self) }
158    }
159}
160
161unsafe impl RefCountable for MergeConflict {
162    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
163        Ref::new(Self {
164            handle: NonNull::new(BNNewAnalysisMergeConflictReference(handle.handle.as_ptr()))
165                .unwrap(),
166        })
167    }
168
169    unsafe fn dec_ref(handle: &Self) {
170        BNFreeAnalysisMergeConflict(handle.handle.as_ptr());
171    }
172}
173
174impl CoreArrayProvider for MergeConflict {
175    type Raw = *mut BNAnalysisMergeConflict;
176    type Context = ();
177    type Wrapped<'a> = Guard<'a, Self>;
178}
179
180unsafe impl CoreArrayProviderInner for MergeConflict {
181    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
182        BNFreeAnalysisMergeConflictList(raw, count)
183    }
184
185    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
186        let raw_ptr = NonNull::new(*raw).unwrap();
187        Guard::new(Self::from_raw(raw_ptr), context)
188    }
189}