binaryninja/collaboration/
snapshot.rs1use std::ffi::c_void;
2use std::ptr::NonNull;
3use std::time::SystemTime;
4
5use super::{sync, Remote, RemoteFile, RemoteProject};
6use crate::binary_view::{BinaryView, BinaryViewExt};
7use crate::collaboration::undo::{RemoteUndoEntry, RemoteUndoEntryId};
8use crate::database::snapshot::Snapshot;
9use crate::progress::{NoProgressCallback, ProgressCallback};
10use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
11use crate::string::{BnString, IntoCStr};
12use binaryninjacore_sys::*;
13
14#[repr(transparent)]
17pub struct RemoteSnapshot {
18 pub(crate) handle: NonNull<BNCollaborationSnapshot>,
19}
20
21impl RemoteSnapshot {
22 pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Self {
23 Self { handle }
24 }
25
26 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Ref<Self> {
27 Ref::new(Self { handle })
28 }
29
30 pub fn get_for_local_snapshot(snapshot: &Snapshot) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
32 sync::get_remote_snapshot_from_local(snapshot)
33 }
34
35 pub fn file(&self) -> Result<Ref<RemoteFile>, ()> {
37 let result = unsafe { BNCollaborationSnapshotGetFile(self.handle.as_ptr()) };
38 let raw = NonNull::new(result).ok_or(())?;
39 Ok(unsafe { RemoteFile::ref_from_raw(raw) })
40 }
41
42 pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
44 let result = unsafe { BNCollaborationSnapshotGetProject(self.handle.as_ptr()) };
45 let raw = NonNull::new(result).ok_or(())?;
46 Ok(unsafe { RemoteProject::ref_from_raw(raw) })
47 }
48
49 pub fn remote(&self) -> Result<Ref<Remote>, ()> {
51 let result = unsafe { BNCollaborationSnapshotGetRemote(self.handle.as_ptr()) };
52 let raw = NonNull::new(result).ok_or(())?;
53 Ok(unsafe { Remote::ref_from_raw(raw) })
54 }
55
56 pub fn url(&self) -> String {
58 let value = unsafe { BNCollaborationSnapshotGetUrl(self.handle.as_ptr()) };
59 assert!(!value.is_null());
60 unsafe { BnString::into_string(value) }
61 }
62
63 pub fn id(&self) -> String {
65 let value = unsafe { BNCollaborationSnapshotGetId(self.handle.as_ptr()) };
66 assert!(!value.is_null());
67 unsafe { BnString::into_string(value) }
68 }
69
70 pub fn name(&self) -> String {
72 let value = unsafe { BNCollaborationSnapshotGetName(self.handle.as_ptr()) };
73 assert!(!value.is_null());
74 unsafe { BnString::into_string(value) }
75 }
76
77 pub fn title(&self) -> String {
79 let value = unsafe { BNCollaborationSnapshotGetTitle(self.handle.as_ptr()) };
80 assert!(!value.is_null());
81 unsafe { BnString::into_string(value) }
82 }
83
84 pub fn description(&self) -> String {
86 let value = unsafe { BNCollaborationSnapshotGetDescription(self.handle.as_ptr()) };
87 assert!(!value.is_null());
88 unsafe { BnString::into_string(value) }
89 }
90
91 pub fn author(&self) -> String {
93 let value = unsafe { BNCollaborationSnapshotGetAuthor(self.handle.as_ptr()) };
94 assert!(!value.is_null());
95 unsafe { BnString::into_string(value) }
96 }
97
98 pub fn author_username(&self) -> String {
100 let value = unsafe { BNCollaborationSnapshotGetAuthorUsername(self.handle.as_ptr()) };
101 assert!(!value.is_null());
102 unsafe { BnString::into_string(value) }
103 }
104
105 pub fn created(&self) -> SystemTime {
107 let timestamp = unsafe { BNCollaborationSnapshotGetCreated(self.handle.as_ptr()) };
108 crate::ffi::time_from_bn(timestamp.try_into().unwrap())
109 }
110
111 pub fn last_modified(&self) -> SystemTime {
113 let timestamp = unsafe { BNCollaborationSnapshotGetLastModified(self.handle.as_ptr()) };
114 crate::ffi::time_from_bn(timestamp.try_into().unwrap())
115 }
116
117 pub fn hash(&self) -> String {
120 let value = unsafe { BNCollaborationSnapshotGetHash(self.handle.as_ptr()) };
121 assert!(!value.is_null());
122 unsafe { BnString::into_string(value) }
123 }
124
125 pub fn snapshot_file_hash(&self) -> String {
128 let value = unsafe { BNCollaborationSnapshotGetSnapshotFileHash(self.handle.as_ptr()) };
129 assert!(!value.is_null());
130 unsafe { BnString::into_string(value) }
131 }
132
133 pub fn has_pulled_undo_entries(&self) -> bool {
135 unsafe { BNCollaborationSnapshotHasPulledUndoEntries(self.handle.as_ptr()) }
136 }
137
138 pub fn is_finalized(&self) -> bool {
140 unsafe { BNCollaborationSnapshotIsFinalized(self.handle.as_ptr()) }
141 }
142
143 pub fn parent_ids(&self) -> Result<Array<BnString>, ()> {
145 let mut count = 0;
146 let raw = unsafe { BNCollaborationSnapshotGetParentIds(self.handle.as_ptr(), &mut count) };
147 (!raw.is_null())
148 .then(|| unsafe { Array::new(raw, count, ()) })
149 .ok_or(())
150 }
151
152 pub fn child_ids(&self) -> Result<Array<BnString>, ()> {
154 let mut count = 0;
155 let raw = unsafe { BNCollaborationSnapshotGetChildIds(self.handle.as_ptr(), &mut count) };
156 (!raw.is_null())
157 .then(|| unsafe { Array::new(raw, count, ()) })
158 .ok_or(())
159 }
160
161 pub fn parents(&self) -> Result<Array<RemoteSnapshot>, ()> {
163 let mut count = 0;
164 let raw = unsafe { BNCollaborationSnapshotGetParents(self.handle.as_ptr(), &mut count) };
165 (!raw.is_null())
166 .then(|| unsafe { Array::new(raw, count, ()) })
167 .ok_or(())
168 }
169
170 pub fn children(&self) -> Result<Array<RemoteSnapshot>, ()> {
172 let mut count = 0;
173 let raw = unsafe { BNCollaborationSnapshotGetChildren(self.handle.as_ptr(), &mut count) };
174 (!raw.is_null())
175 .then(|| unsafe { Array::new(raw, count, ()) })
176 .ok_or(())
177 }
178
179 pub fn undo_entries(&self) -> Result<Array<RemoteUndoEntry>, ()> {
183 if !self.has_pulled_undo_entries() {
184 self.pull_undo_entries()?;
185 }
186 let mut count = 0;
187 let raw =
188 unsafe { BNCollaborationSnapshotGetUndoEntries(self.handle.as_ptr(), &mut count) };
189 (!raw.is_null())
190 .then(|| unsafe { Array::new(raw, count, ()) })
191 .ok_or(())
192 }
193
194 pub fn get_undo_entry_by_id(
198 &self,
199 id: RemoteUndoEntryId,
200 ) -> Result<Option<Ref<RemoteUndoEntry>>, ()> {
201 if !self.has_pulled_undo_entries() {
202 self.pull_undo_entries()?;
203 }
204 let raw = unsafe { BNCollaborationSnapshotGetUndoEntryById(self.handle.as_ptr(), id.0) };
205 Ok(NonNull::new(raw).map(|handle| unsafe { RemoteUndoEntry::ref_from_raw(handle) }))
206 }
207
208 pub fn pull_undo_entries(&self) -> Result<(), ()> {
210 self.pull_undo_entries_with_progress(NoProgressCallback)
211 }
212
213 pub fn pull_undo_entries_with_progress<P: ProgressCallback>(
215 &self,
216 mut progress: P,
217 ) -> Result<(), ()> {
218 let success = unsafe {
219 BNCollaborationSnapshotPullUndoEntries(
220 self.handle.as_ptr(),
221 Some(P::cb_progress_callback),
222 &mut progress as *mut P as *mut c_void,
223 )
224 };
225 success.then_some(()).ok_or(())
226 }
227
228 pub fn create_undo_entry(
230 &self,
231 parent: Option<u64>,
232 data: &str,
233 ) -> Result<Ref<RemoteUndoEntry>, ()> {
234 let data = data.to_cstr();
235 let value = unsafe {
236 BNCollaborationSnapshotCreateUndoEntry(
237 self.handle.as_ptr(),
238 parent.is_some(),
239 parent.unwrap_or(0),
240 data.as_ptr(),
241 )
242 };
243 let handle = NonNull::new(value).ok_or(())?;
244 Ok(unsafe { RemoteUndoEntry::ref_from_raw(handle) })
245 }
246
247 pub fn finalize(&self) -> Result<(), ()> {
250 let success = unsafe { BNCollaborationSnapshotFinalize(self.handle.as_ptr()) };
251 success.then_some(()).ok_or(())
252 }
253
254 pub fn get_local_snapshot(&self, bv: &BinaryView) -> Result<Option<Ref<Snapshot>>, ()> {
314 let Some(db) = bv.file().database() else {
315 return Ok(None);
316 };
317 sync::get_local_snapshot_for_remote(self, &db)
318 }
319
320 pub fn analysis_cache_build_id(&self) -> u64 {
321 unsafe { BNCollaborationSnapshotGetAnalysisCacheBuildId(self.handle.as_ptr()) }
322 }
323}
324
325impl PartialEq for RemoteSnapshot {
326 fn eq(&self, other: &Self) -> bool {
327 self.id() == other.id()
328 }
329}
330impl Eq for RemoteSnapshot {}
331
332impl ToOwned for RemoteSnapshot {
333 type Owned = Ref<Self>;
334
335 fn to_owned(&self) -> Self::Owned {
336 unsafe { RefCountable::inc_ref(self) }
337 }
338}
339
340unsafe impl RefCountable for RemoteSnapshot {
341 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
342 Ref::new(Self {
343 handle: NonNull::new(BNNewCollaborationSnapshotReference(handle.handle.as_ptr()))
344 .unwrap(),
345 })
346 }
347
348 unsafe fn dec_ref(handle: &Self) {
349 BNFreeCollaborationSnapshot(handle.handle.as_ptr());
350 }
351}
352
353impl CoreArrayProvider for RemoteSnapshot {
354 type Raw = *mut BNCollaborationSnapshot;
355 type Context = ();
356 type Wrapped<'a> = Guard<'a, RemoteSnapshot>;
357}
358
359unsafe impl CoreArrayProviderInner for RemoteSnapshot {
360 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
361 BNFreeCollaborationSnapshotList(raw, count)
362 }
363
364 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
365 let raw_ptr = NonNull::new(*raw).unwrap();
366 Guard::new(Self::from_raw(raw_ptr), context)
367 }
368}