binaryninja/collaboration/
merge.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
use binaryninjacore_sys::*;
use std::ffi::c_char;
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::{BnStrCompatible, BnString};

pub type MergeConflictDataType = BNMergeConflictDataType;

/// Structure representing an individual merge conflict
#[repr(transparent)]
pub struct MergeConflict {
    handle: NonNull<BNAnalysisMergeConflict>,
}

impl MergeConflict {
    pub(crate) unsafe fn from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Self {
        Self { handle }
    }

    #[allow(unused)]
    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Ref<Self> {
        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<Snapshot> {
        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<Snapshot> {
        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<Snapshot> {
        let result = unsafe { BNAnalysisMergeConflictGetSecondSnapshot(self.handle.as_ptr()) };
        NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
    }

    pub fn path_item_string<S: BnStrCompatible>(&self, path: S) -> Result<BnString, ()> {
        let path = path.into_bytes_with_nul();
        let result = unsafe {
            BNAnalysisMergeConflictGetPathItemString(
                self.handle.as_ptr(),
                path.as_ref().as_ptr() as *const c_char,
            )
        };
        (!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<Ref<FileMetadata>> {
        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<Ref<FileMetadata>> {
        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<Ref<FileMetadata>> {
        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<BnString> {
        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<BnString> {
        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<BnString> {
        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) -> BnString {
        let result = unsafe { BNAnalysisMergeConflictGetType(self.handle.as_ptr()) };
        assert!(!result.is_null());
        unsafe { BnString::from_raw(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) -> BnString {
        let result = unsafe { BNAnalysisMergeConflictGetKey(self.handle.as_ptr()) };
        assert!(!result.is_null());
        unsafe { BnString::from_raw(result) }
    }

    /// Call this when you've resolved the conflict to save the result
    pub fn success<S: BnStrCompatible>(&self, value: S) -> Result<(), ()> {
        let value = value.into_bytes_with_nul();
        let success = unsafe {
            BNAnalysisMergeConflictSuccess(
                self.handle.as_ptr(),
                value.as_ref().as_ptr() as *const c_char,
            )
        };
        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<S: BnStrCompatible>(&self, path_key: S) -> Option<u64> {
        let path_key = path_key.into_bytes_with_nul();
        let value = unsafe {
            BNAnalysisMergeConflictGetPathItem(
                self.handle.as_ptr(),
                path_key.as_ref().as_ptr() as *const c_char,
            )
        };
        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<S: BnStrCompatible>(&self, path_key: S) -> Option<BnString> {
        let path_key = path_key.into_bytes_with_nul();
        let value = unsafe {
            BNAnalysisMergeConflictGetPathItemString(
                self.handle.as_ptr(),
                path_key.as_ref().as_ptr() as *const c_char,
            )
        };
        match value.is_null() {
            false => Some(unsafe { BnString::from_raw(value) }),
            true => None,
        }
    }
}

impl ToOwned for MergeConflict {
    type Owned = Ref<Self>;

    fn to_owned(&self) -> Self::Owned {
        unsafe { RefCountable::inc_ref(self) }
    }
}

unsafe impl RefCountable for MergeConflict {
    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
        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)
    }
}