use std::ffi::CString;
use std::marker::PhantomData;
use crate::ffi::bookmarks::{
idalib_bookmarks_t_erase, idalib_bookmarks_t_find_index, idalib_bookmarks_t_get,
idalib_bookmarks_t_get_desc, idalib_bookmarks_t_mark, idalib_bookmarks_t_size,
};
use crate::ffi::BADADDR;
use crate::idb::IDB;
use crate::{Address, IDAError};
pub type BookmarkIndex = u32;
const BOOKMARKS_BAD_INDEX: BookmarkIndex = 0xffffffff; pub struct Bookmarks<'a> {
_marker: PhantomData<&'a IDB>,
}
impl<'a> Bookmarks<'a> {
pub(crate) fn new(_: &'a IDB) -> Self {
Self {
_marker: PhantomData,
}
}
pub fn mark(&self, ea: Address, desc: impl AsRef<str>) -> Result<BookmarkIndex, IDAError> {
self.mark_with(ea, self.len(), desc)
}
pub fn mark_with(
&self,
ea: Address,
idx: BookmarkIndex,
desc: impl AsRef<str>,
) -> Result<BookmarkIndex, IDAError> {
let desc = CString::new(desc.as_ref()).map_err(IDAError::ffi)?;
let slot = unsafe { idalib_bookmarks_t_mark(ea.into(), idx.into(), desc.as_ptr()) };
if slot != BOOKMARKS_BAD_INDEX {
Ok(slot)
} else {
Err(IDAError::ffi_with(format!(
"failed to set bookmark at address {ea:#x}, index {idx}"
)))
}
}
pub fn get_description(&self, ea: Address) -> Option<String> {
self.get_description_by_index(self.find_index(ea)?)
}
pub fn get_address(&self, idx: BookmarkIndex) -> Option<Address> {
let addr = unsafe { idalib_bookmarks_t_get(idx.into()) };
if addr == BADADDR {
None
} else {
Some(addr.into())
}
}
pub fn get_description_by_index(&self, idx: BookmarkIndex) -> Option<String> {
let s = unsafe { idalib_bookmarks_t_get_desc(idx.into()) };
if s.is_empty() {
None
} else {
Some(s)
}
}
pub fn erase(&self, ea: Address) -> Result<(), IDAError> {
self.erase_by_index(self.find_index(ea).ok_or(IDAError::ffi_with(format!(
"failed to find bookmark index for address {ea:#x}"
)))?)
}
pub fn erase_by_index(&self, idx: BookmarkIndex) -> Result<(), IDAError> {
if idx >= self.len() {
return Err(IDAError::ffi_with(format!(
"failed to erase bookmark at index {idx}"
)));
}
if unsafe { idalib_bookmarks_t_erase(idx.into()) } {
Ok(())
} else {
Err(IDAError::ffi_with(format!(
"failed to erase bookmark at index {idx}"
)))
}
}
pub fn find_index(&self, ea: Address) -> Option<BookmarkIndex> {
let index = unsafe { idalib_bookmarks_t_find_index(ea.into()) };
if index != BOOKMARKS_BAD_INDEX {
Some(index)
} else {
None
}
}
pub fn len(&self) -> BookmarkIndex {
unsafe { idalib_bookmarks_t_size() }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}