binaryninja/interaction/
report.rs1use std::fmt::Debug;
2use std::ptr::NonNull;
3
4use binaryninjacore_sys::*;
5
6use crate::binary_view::BinaryView;
7use crate::flowgraph::FlowGraph;
8use crate::rc::{Ref, RefCountable};
9use crate::string::{BnString, IntoCStr};
10
11pub type ReportType = BNReportType;
12
13#[repr(transparent)]
14pub struct ReportCollection {
15 handle: NonNull<BNReportCollection>,
16}
17
18impl ReportCollection {
19 pub(crate) unsafe fn from_raw(handle: NonNull<BNReportCollection>) -> Self {
20 Self { handle }
21 }
22
23 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNReportCollection>) -> Ref<Self> {
24 unsafe { Ref::new(Self { handle }) }
25 }
26
27 pub fn new() -> Ref<Self> {
28 let raw = unsafe { BNCreateReportCollection() };
29 unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) }
30 }
31
32 pub fn show(&self, title: &str) {
33 let title = title.to_cstr();
34 unsafe { BNShowReportCollection(title.as_ptr(), self.handle.as_ptr()) }
35 }
36
37 pub fn count(&self) -> usize {
38 unsafe { BNGetReportCollectionCount(self.handle.as_ptr()) }
39 }
40
41 fn report_type(&self, i: usize) -> ReportType {
42 unsafe { BNGetReportType(self.handle.as_ptr(), i) }
43 }
44
45 pub fn get(&self, i: usize) -> Report<'_> {
46 Report::new(self, i)
47 }
48
49 fn view(&self, i: usize) -> Option<Ref<BinaryView>> {
50 let raw = unsafe { BNGetReportView(self.handle.as_ptr(), i) };
51 if raw.is_null() {
52 return None;
53 }
54 Some(unsafe { BinaryView::ref_from_raw(raw) })
55 }
56
57 fn title(&self, i: usize) -> String {
58 let raw = unsafe { BNGetReportTitle(self.handle.as_ptr(), i) };
59 unsafe { BnString::into_string(raw) }
60 }
61
62 fn contents(&self, i: usize) -> String {
63 let raw = unsafe { BNGetReportContents(self.handle.as_ptr(), i) };
64 unsafe { BnString::into_string(raw) }
65 }
66
67 fn plain_text(&self, i: usize) -> String {
68 let raw = unsafe { BNGetReportPlainText(self.handle.as_ptr(), i) };
69 unsafe { BnString::into_string(raw) }
70 }
71
72 fn flow_graph(&self, i: usize) -> Option<Ref<FlowGraph>> {
73 let raw = unsafe { BNGetReportFlowGraph(self.handle.as_ptr(), i) };
74 match raw.is_null() {
75 false => Some(unsafe { FlowGraph::ref_from_raw(raw) }),
76 true => None,
77 }
78 }
79
80 pub fn add_text(&self, view: Option<&BinaryView>, title: &str, contents: &str) {
81 let title = title.to_cstr();
82 let contents = contents.to_cstr();
83 unsafe {
84 BNAddPlainTextReportToCollection(
85 self.handle.as_ptr(),
86 view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
87 title.as_ptr(),
88 contents.as_ptr(),
89 )
90 }
91 }
92
93 pub fn add_markdown(
94 &self,
95 view: Option<&BinaryView>,
96 title: &str,
97 contents: &str,
98 plaintext: &str,
99 ) {
100 let title = title.to_cstr();
101 let contents = contents.to_cstr();
102 let plaintext = plaintext.to_cstr();
103 unsafe {
104 BNAddMarkdownReportToCollection(
105 self.handle.as_ptr(),
106 view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
107 title.as_ptr(),
108 contents.as_ptr(),
109 plaintext.as_ptr(),
110 )
111 }
112 }
113
114 pub fn add_html(
115 &self,
116 view: Option<&BinaryView>,
117 title: &str,
118 contents: &str,
119 plaintext: &str,
120 ) {
121 let title = title.to_cstr();
122 let contents = contents.to_cstr();
123 let plaintext = plaintext.to_cstr();
124 unsafe {
125 BNAddHTMLReportToCollection(
126 self.handle.as_ptr(),
127 view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
128 title.as_ptr(),
129 contents.as_ptr(),
130 plaintext.as_ptr(),
131 )
132 }
133 }
134
135 pub fn add_graph(&self, view: Option<&BinaryView>, title: &str, graph: &FlowGraph) {
136 let title = title.to_cstr();
137 unsafe {
138 BNAddGraphReportToCollection(
139 self.handle.as_ptr(),
140 view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
141 title.as_ptr(),
142 graph.handle,
143 )
144 }
145 }
146
147 fn update_report_flow_graph(&self, i: usize, graph: &FlowGraph) {
148 unsafe { BNUpdateReportFlowGraph(self.handle.as_ptr(), i, graph.handle) }
149 }
150
151 pub fn iter(&self) -> ReportCollectionIter<'_> {
152 ReportCollectionIter::new(self)
153 }
154}
155
156impl Debug for ReportCollection {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 f.debug_struct("ReportCollection")
159 .field("count", &self.count())
160 .finish()
161 }
162}
163
164unsafe impl RefCountable for ReportCollection {
165 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
166 let raw = unsafe { BNNewReportCollectionReference(handle.handle.as_ptr()) };
167 unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) }
168 }
169
170 unsafe fn dec_ref(handle: &Self) {
171 unsafe { BNFreeReportCollection(handle.handle.as_ptr()) }
172 }
173}
174
175impl ToOwned for ReportCollection {
176 type Owned = Ref<Self>;
177
178 fn to_owned(&self) -> Self::Owned {
179 unsafe { RefCountable::inc_ref(self) }
180 }
181}
182
183impl<'a> IntoIterator for &'a ReportCollection {
184 type Item = Report<'a>;
185 type IntoIter = ReportCollectionIter<'a>;
186
187 fn into_iter(self) -> Self::IntoIter {
188 self.iter()
189 }
190}
191
192pub enum Report<'a> {
193 PlainText(ReportPlainText<'a>),
194 Markdown(ReportMarkdown<'a>),
195 Html(ReportHtml<'a>),
196 FlowGraph(ReportFlowGraph<'a>),
197}
198
199impl<'a> Report<'a> {
200 fn new(collection: &'a ReportCollection, index: usize) -> Self {
201 let inner = ReportInner { collection, index };
202 match inner.type_() {
203 ReportType::PlainTextReportType => Report::PlainText(ReportPlainText(inner)),
204 ReportType::MarkdownReportType => Report::Markdown(ReportMarkdown(inner)),
205 ReportType::HTMLReportType => Report::Html(ReportHtml(inner)),
206 ReportType::FlowGraphReportType => Report::FlowGraph(ReportFlowGraph(inner)),
207 }
208 }
209
210 fn _inner(&self) -> &ReportInner<'a> {
211 match self {
212 Report::PlainText(ReportPlainText(x))
213 | Report::Markdown(ReportMarkdown(x))
214 | Report::Html(ReportHtml(x))
215 | Report::FlowGraph(ReportFlowGraph(x)) => x,
216 }
217 }
218
219 pub fn view(&self) -> Option<Ref<BinaryView>> {
220 self._inner().view()
221 }
222
223 pub fn title(&self) -> String {
224 self._inner().title()
225 }
226}
227
228pub struct ReportPlainText<'a>(ReportInner<'a>);
229
230impl ReportPlainText<'_> {
231 pub fn contents(&self) -> String {
232 self.0.contents()
233 }
234}
235
236pub struct ReportMarkdown<'a>(ReportInner<'a>);
237
238impl ReportMarkdown<'_> {
239 pub fn contents(&self) -> String {
240 self.0.contents()
241 }
242
243 pub fn plaintext(&self) -> String {
244 self.0.plain_text()
245 }
246}
247
248pub struct ReportHtml<'a>(ReportInner<'a>);
249
250impl ReportHtml<'_> {
251 pub fn contents(&self) -> String {
252 self.0.contents()
253 }
254
255 pub fn plaintext(&self) -> String {
256 self.0.plain_text()
257 }
258}
259
260pub struct ReportFlowGraph<'a>(ReportInner<'a>);
261
262impl ReportFlowGraph<'_> {
263 pub fn flow_graph(&self) -> Ref<FlowGraph> {
264 self.0
265 .flow_graph()
266 .expect("Flow graph not available for flow graph report!")
267 }
268
269 pub fn update_report_flow_graph(&self, graph: &FlowGraph) {
270 self.0.update_report_flow_graph(graph)
271 }
272}
273
274struct ReportInner<'a> {
275 collection: &'a ReportCollection,
276 index: usize,
277}
278
279impl ReportInner<'_> {
280 fn type_(&self) -> ReportType {
281 self.collection.report_type(self.index)
282 }
283
284 fn view(&self) -> Option<Ref<BinaryView>> {
285 self.collection.view(self.index)
286 }
287
288 fn title(&self) -> String {
289 self.collection.title(self.index)
290 }
291
292 fn contents(&self) -> String {
293 self.collection.contents(self.index)
294 }
295
296 fn plain_text(&self) -> String {
297 self.collection.plain_text(self.index)
298 }
299
300 fn flow_graph(&self) -> Option<Ref<FlowGraph>> {
301 self.collection.flow_graph(self.index)
302 }
303
304 fn update_report_flow_graph(&self, graph: &FlowGraph) {
305 self.collection.update_report_flow_graph(self.index, graph)
306 }
307}
308
309pub struct ReportCollectionIter<'a> {
310 report: &'a ReportCollection,
311 current_index: usize,
312 count: usize,
313}
314
315impl<'a> ReportCollectionIter<'a> {
316 pub fn new(report: &'a ReportCollection) -> Self {
317 Self {
318 report,
319 current_index: 0,
320 count: report.count(),
321 }
322 }
323
324 pub fn collection(&self) -> &ReportCollection {
325 self.report
326 }
327}
328
329impl<'a> Iterator for ReportCollectionIter<'a> {
330 type Item = Report<'a>;
331
332 fn next(&mut self) -> Option<Self::Item> {
333 (self.current_index < self.count).then(|| {
334 let result = Report::new(self.report, self.current_index);
335 self.current_index += 1;
336 result
337 })
338 }
339}