binaryninja/interaction/
report.rs

1use 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}