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