summaryrefslogtreecommitdiff
path: root/rust/src/interaction/report.rs
diff options
context:
space:
mode:
authorrbran <git@rubens.io>2025-04-23 19:24:45 +0000
committerMason Reed <35282038+emesare@users.noreply.github.com>2025-05-12 17:45:24 -0400
commitc2d90f2e5a010546d0e1f1deeb1a6d8531ea4d4f (patch)
tree20a6c6268943154fdda6a4d6884f2f29d9d4de50 /rust/src/interaction/report.rs
parent4a4a8488269922269b20a12a7b71272b925b9f18 (diff)
[Rust] Implement custom interactive handler
Diffstat (limited to 'rust/src/interaction/report.rs')
-rw-r--r--rust/src/interaction/report.rs311
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
+ })
+ }
+}