1use crate::data_buffer::DataBuffer;
2use crate::database::kvs::KeyValueStore;
3use crate::database::undo::UndoEntry;
4use crate::database::Database;
5use crate::progress::ProgressCallback;
6use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
7use crate::string::{BnString, IntoCStr};
8use binaryninjacore_sys::{
9 BNCollaborationFreeSnapshotIdList, BNFreeSnapshot, BNFreeSnapshotList, BNGetSnapshotChildren,
10 BNGetSnapshotDatabase, BNGetSnapshotFileContents, BNGetSnapshotFileContentsHash,
11 BNGetSnapshotFirstParent, BNGetSnapshotId, BNGetSnapshotName, BNGetSnapshotParents,
12 BNGetSnapshotUndoData, BNGetSnapshotUndoEntries, BNGetSnapshotUndoEntriesWithProgress,
13 BNIsSnapshotAutoSave, BNNewSnapshotReference, BNReadSnapshotData,
14 BNReadSnapshotDataWithProgress, BNSetSnapshotName, BNSnapshot, BNSnapshotHasAncestor,
15 BNSnapshotHasContents, BNSnapshotHasUndo, BNSnapshotStoreData,
16};
17use std::ffi::c_void;
18use std::fmt;
19use std::fmt::{Debug, Display, Formatter};
20use std::ptr::NonNull;
21
22pub struct Snapshot {
23 pub(crate) handle: NonNull<BNSnapshot>,
24}
25
26impl Snapshot {
27 pub(crate) unsafe fn from_raw(handle: NonNull<BNSnapshot>) -> Self {
28 Self { handle }
29 }
30
31 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNSnapshot>) -> Ref<Self> {
32 Ref::new(Self { handle })
33 }
34
35 pub fn database(&self) -> Database {
37 unsafe {
38 Database::from_raw(NonNull::new(BNGetSnapshotDatabase(self.handle.as_ptr())).unwrap())
39 }
40 }
41
42 pub fn id(&self) -> SnapshotId {
44 SnapshotId(unsafe { BNGetSnapshotId(self.handle.as_ptr()) })
45 }
46
47 pub fn name(&self) -> String {
49 unsafe { BnString::into_string(BNGetSnapshotName(self.handle.as_ptr())) }
50 }
51
52 pub fn set_name(&self, value: &str) {
54 let value_raw = value.to_cstr();
55 let value_ptr = value_raw.as_ptr();
56 unsafe { BNSetSnapshotName(self.handle.as_ptr(), value_ptr) }
57 }
58
59 pub fn is_auto_save(&self) -> bool {
61 unsafe { BNIsSnapshotAutoSave(self.handle.as_ptr()) }
62 }
63
64 pub fn has_contents(&self) -> bool {
66 unsafe { BNSnapshotHasContents(self.handle.as_ptr()) }
67 }
68
69 pub fn has_undo(&self) -> bool {
71 unsafe { BNSnapshotHasUndo(self.handle.as_ptr()) }
72 }
73
74 pub fn first_parent(&self) -> Option<Snapshot> {
76 let result = unsafe { BNGetSnapshotFirstParent(self.handle.as_ptr()) };
77 NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
78 }
79
80 pub fn parents(&self) -> Array<Snapshot> {
82 let mut count = 0;
83 let result = unsafe { BNGetSnapshotParents(self.handle.as_ptr(), &mut count) };
84 assert!(!result.is_null());
85 unsafe { Array::new(result, count, ()) }
86 }
87
88 pub fn children(&self) -> Array<Snapshot> {
90 let mut count = 0;
91 let result = unsafe { BNGetSnapshotChildren(self.handle.as_ptr(), &mut count) };
92 assert!(!result.is_null());
93 unsafe { Array::new(result, count, ()) }
94 }
95
96 pub fn file_contents(&self) -> Option<DataBuffer> {
98 self.has_contents().then(|| unsafe {
99 let result = BNGetSnapshotFileContents(self.handle.as_ptr());
100 assert!(!result.is_null());
101 DataBuffer::from_raw(result)
102 })
103 }
104
105 pub fn file_contents_hash(&self) -> Option<DataBuffer> {
107 self.has_contents().then(|| unsafe {
108 let result = BNGetSnapshotFileContentsHash(self.handle.as_ptr());
109 assert!(!result.is_null());
110 DataBuffer::from_raw(result)
111 })
112 }
113
114 pub fn undo_entries(&self) -> Array<UndoEntry> {
116 assert!(self.has_undo());
117 let mut count = 0;
118 let result = unsafe { BNGetSnapshotUndoEntries(self.handle.as_ptr(), &mut count) };
119 assert!(!result.is_null());
120 unsafe { Array::new(result, count, ()) }
121 }
122
123 pub fn undo_entries_with_progress<P: ProgressCallback>(
124 &self,
125 mut progress: P,
126 ) -> Array<UndoEntry> {
127 assert!(self.has_undo());
128 let mut count = 0;
129
130 let result = unsafe {
131 BNGetSnapshotUndoEntriesWithProgress(
132 self.handle.as_ptr(),
133 &mut progress as *mut P as *mut c_void,
134 Some(P::cb_progress_callback),
135 &mut count,
136 )
137 };
138
139 assert!(!result.is_null());
140 unsafe { Array::new(result, count, ()) }
141 }
142
143 pub fn read_data(&self) -> Ref<KeyValueStore> {
145 let result = unsafe { BNReadSnapshotData(self.handle.as_ptr()) };
146 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
147 }
148
149 pub fn read_data_with_progress<P: ProgressCallback>(
150 &self,
151 mut progress: P,
152 ) -> Ref<KeyValueStore> {
153 let result = unsafe {
154 BNReadSnapshotDataWithProgress(
155 self.handle.as_ptr(),
156 &mut progress as *mut P as *mut c_void,
157 Some(P::cb_progress_callback),
158 )
159 };
160
161 unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
162 }
163
164 pub fn undo_data(&self) -> DataBuffer {
165 let result = unsafe { BNGetSnapshotUndoData(self.handle.as_ptr()) };
166 assert!(!result.is_null());
167 DataBuffer::from_raw(result)
168 }
169
170 pub fn store_data(&self, data: &KeyValueStore) -> bool {
171 unsafe {
172 BNSnapshotStoreData(
173 self.handle.as_ptr(),
174 data.handle.as_ptr(),
175 std::ptr::null_mut(),
176 None,
177 )
178 }
179 }
180
181 pub fn store_data_with_progress<P: ProgressCallback>(
182 &self,
183 data: &KeyValueStore,
184 mut progress: P,
185 ) -> bool {
186 unsafe {
187 BNSnapshotStoreData(
188 self.handle.as_ptr(),
189 data.handle.as_ptr(),
190 &mut progress as *mut P as *mut c_void,
191 Some(P::cb_progress_callback),
192 )
193 }
194 }
195
196 pub fn has_ancestor(self, other: &Snapshot) -> bool {
198 unsafe { BNSnapshotHasAncestor(self.handle.as_ptr(), other.handle.as_ptr()) }
199 }
200}
201
202impl Debug for Snapshot {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 f.debug_struct("Snapshot")
205 .field("id", &self.id())
206 .field("name", &self.name())
207 .field("is_auto_save", &self.is_auto_save())
208 .field("has_contents", &self.has_contents())
209 .field("has_undo", &self.has_undo())
210 .field("children", &self.children().to_vec())
212 .finish()
213 }
214}
215
216impl ToOwned for Snapshot {
217 type Owned = Ref<Self>;
218
219 fn to_owned(&self) -> Self::Owned {
220 unsafe { RefCountable::inc_ref(self) }
221 }
222}
223
224unsafe impl RefCountable for Snapshot {
225 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
226 Ref::new(Self {
227 handle: NonNull::new(BNNewSnapshotReference(handle.handle.as_ptr())).unwrap(),
228 })
229 }
230
231 unsafe fn dec_ref(handle: &Self) {
232 BNFreeSnapshot(handle.handle.as_ptr());
233 }
234}
235
236impl CoreArrayProvider for Snapshot {
237 type Raw = *mut BNSnapshot;
238 type Context = ();
239 type Wrapped<'a> = Guard<'a, Snapshot>;
240}
241
242unsafe impl CoreArrayProviderInner for Snapshot {
243 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
244 BNFreeSnapshotList(raw, count);
245 }
246
247 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
248 let raw_ptr = NonNull::new(*raw).unwrap();
249 Guard::new(Self::from_raw(raw_ptr), context)
250 }
251}
252
253#[repr(transparent)]
254#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
255pub struct SnapshotId(pub i64);
256
257impl Display for SnapshotId {
258 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
259 f.write_fmt(format_args!("{}", self.0))
260 }
261}
262
263impl CoreArrayProvider for SnapshotId {
264 type Raw = i64;
265 type Context = ();
266 type Wrapped<'a> = SnapshotId;
267}
268
269unsafe impl CoreArrayProviderInner for SnapshotId {
270 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
271 BNCollaborationFreeSnapshotIdList(raw, count)
272 }
273
274 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
275 SnapshotId(*raw)
276 }
277}