diff options
| author | rbran <git@rubens.io> | 2025-04-23 19:24:45 +0000 |
|---|---|---|
| committer | Mason Reed <35282038+emesare@users.noreply.github.com> | 2025-05-12 17:45:24 -0400 |
| commit | c2d90f2e5a010546d0e1f1deeb1a6d8531ea4d4f (patch) | |
| tree | 20a6c6268943154fdda6a4d6884f2f29d9d4de50 /rust/src/interaction/report.rs | |
| parent | 4a4a8488269922269b20a12a7b71272b925b9f18 (diff) | |
[Rust] Implement custom interactive handler
Diffstat (limited to 'rust/src/interaction/report.rs')
| -rw-r--r-- | rust/src/interaction/report.rs | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/rust/src/interaction/report.rs b/rust/src/interaction/report.rs new file mode 100644 index 00000000..a20d8f1f --- /dev/null +++ b/rust/src/interaction/report.rs @@ -0,0 +1,311 @@ +use std::ffi::c_char; +use std::ptr::NonNull; + +use binaryninjacore_sys::*; + +use crate::binary_view::BinaryView; +use crate::flowgraph::FlowGraph; +use crate::rc::{Ref, RefCountable}; +use crate::string::{BnString, IntoCStr}; + +pub type ReportType = BNReportType; + +#[repr(transparent)] +pub struct ReportCollection { + handle: NonNull<BNReportCollection>, +} + +impl ReportCollection { + pub(crate) unsafe fn from_raw(handle: NonNull<BNReportCollection>) -> Self { + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNReportCollection>) -> Ref<Self> { + unsafe { Ref::new(Self { handle }) } + } + + pub fn new() -> Ref<Self> { + let raw = unsafe { BNCreateReportCollection() }; + unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) } + } + + pub fn show(&self, title: &str) { + let title = title.to_cstr(); + unsafe { BNShowReportCollection(title.as_ptr(), self.handle.as_ptr()) } + } + + pub fn count(&self) -> usize { + unsafe { BNGetReportCollectionCount(self.handle.as_ptr()) } + } + + fn report_type(&self, i: usize) -> ReportType { + unsafe { BNGetReportType(self.handle.as_ptr(), i) } + } + + pub fn get(&self, i: usize) -> Report<'_> { + Report::new(self, i) + } + + fn view(&self, i: usize) -> Ref<BinaryView> { + let raw = unsafe { BNGetReportView(self.handle.as_ptr(), i) }; + unsafe { BinaryView::ref_from_raw(raw) } + } + + fn title(&self, i: usize) -> String { + let raw = unsafe { BNGetReportTitle(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn contents(&self, i: usize) -> String { + let raw = unsafe { BNGetReportContents(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn plain_text(&self, i: usize) -> String { + let raw = unsafe { BNGetReportPlainText(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn flow_graph(&self, i: usize) -> Ref<FlowGraph> { + let raw = unsafe { BNGetReportFlowGraph(self.handle.as_ptr(), i) }; + unsafe { FlowGraph::ref_from_raw(raw) } + } + + pub fn add_text(&self, view: &BinaryView, title: &str, contents: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + unsafe { + BNAddPlainTextReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ref().as_ptr() as *const c_char, + contents.as_ref().as_ptr() as *const c_char, + ) + } + } + + pub fn add_markdown(&self, view: &BinaryView, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNAddMarkdownReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ref().as_ptr() as *const c_char, + contents.as_ref().as_ptr() as *const c_char, + plaintext.as_ref().as_ptr() as *const c_char, + ) + } + } + + pub fn add_html(&self, view: &BinaryView, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNAddHTMLReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ref().as_ptr() as *const c_char, + contents.as_ref().as_ptr() as *const c_char, + plaintext.as_ref().as_ptr() as *const c_char, + ) + } + } + + pub fn add_graph(&self, view: &BinaryView, title: &str, graph: &FlowGraph) { + let title = title.to_cstr(); + unsafe { + BNAddGraphReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ref().as_ptr() as *const c_char, + graph.handle, + ) + } + } + + fn update_report_flow_graph(&self, i: usize, graph: &FlowGraph) { + unsafe { BNUpdateReportFlowGraph(self.handle.as_ptr(), i, graph.handle) } + } + + pub fn iter(&self) -> ReportCollectionIter<'_> { + ReportCollectionIter::new(self) + } +} + +unsafe impl RefCountable for ReportCollection { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + let raw = unsafe { BNNewReportCollectionReference(handle.handle.as_ptr()) }; + unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) } + } + + unsafe fn dec_ref(handle: &Self) { + unsafe { BNFreeReportCollection(handle.handle.as_ptr()) } + } +} + +impl ToOwned for ReportCollection { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +impl<'a> IntoIterator for &'a ReportCollection { + type Item = Report<'a>; + type IntoIter = ReportCollectionIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub enum Report<'a> { + PlainText(ReportPlainText<'a>), + Markdown(ReportMarkdown<'a>), + Html(ReportHtml<'a>), + FlowGraph(ReportFlowGraph<'a>), +} + +impl<'a> Report<'a> { + fn new(collection: &'a ReportCollection, index: usize) -> Self { + let inner = ReportInner { collection, index }; + match inner.type_() { + ReportType::PlainTextReportType => Report::PlainText(ReportPlainText(inner)), + ReportType::MarkdownReportType => Report::Markdown(ReportMarkdown(inner)), + ReportType::HTMLReportType => Report::Html(ReportHtml(inner)), + ReportType::FlowGraphReportType => Report::FlowGraph(ReportFlowGraph(inner)), + } + } + + fn _inner(&self) -> &ReportInner<'a> { + match self { + Report::PlainText(ReportPlainText(x)) + | Report::Markdown(ReportMarkdown(x)) + | Report::Html(ReportHtml(x)) + | Report::FlowGraph(ReportFlowGraph(x)) => x, + } + } + + pub fn view(&self) -> Ref<BinaryView> { + self._inner().view() + } + + pub fn title(&self) -> String { + self._inner().title() + } +} + +pub struct ReportPlainText<'a>(ReportInner<'a>); + +impl ReportPlainText<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } +} + +pub struct ReportMarkdown<'a>(ReportInner<'a>); + +impl ReportMarkdown<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } + + pub fn plaintext(&self) -> String { + self.0.plain_text() + } +} + +pub struct ReportHtml<'a>(ReportInner<'a>); + +impl ReportHtml<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } + + pub fn plaintext(&self) -> String { + self.0.plain_text() + } +} + +pub struct ReportFlowGraph<'a>(ReportInner<'a>); + +impl ReportFlowGraph<'_> { + pub fn flow_graph(&self) -> Ref<FlowGraph> { + self.0.flow_graph() + } + + pub fn update_report_flow_graph(&self, graph: &FlowGraph) { + self.0.update_report_flow_graph(graph) + } +} + +struct ReportInner<'a> { + collection: &'a ReportCollection, + index: usize, +} + +impl ReportInner<'_> { + fn type_(&self) -> ReportType { + self.collection.report_type(self.index) + } + + fn view(&self) -> Ref<BinaryView> { + self.collection.view(self.index) + } + + fn title(&self) -> String { + self.collection.title(self.index) + } + + fn contents(&self) -> String { + self.collection.contents(self.index) + } + + fn plain_text(&self) -> String { + self.collection.plain_text(self.index) + } + + fn flow_graph(&self) -> Ref<FlowGraph> { + self.collection.flow_graph(self.index) + } + + fn update_report_flow_graph(&self, graph: &FlowGraph) { + self.collection.update_report_flow_graph(self.index, graph) + } +} + +pub struct ReportCollectionIter<'a> { + report: &'a ReportCollection, + current_index: usize, + count: usize, +} + +impl<'a> ReportCollectionIter<'a> { + pub fn new(report: &'a ReportCollection) -> Self { + Self { + report, + current_index: 0, + count: report.count(), + } + } + + pub fn collection(&self) -> &ReportCollection { + self.report + } +} + +impl<'a> Iterator for ReportCollectionIter<'a> { + type Item = Report<'a>; + + fn next(&mut self) -> Option<Self::Item> { + (self.current_index < self.count).then(|| { + let result = Report::new(self.report, self.current_index); + self.current_index += 1; + result + }) + } +} |
