summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rust/Cargo.lock8
-rw-r--r--rust/Cargo.toml1
-rw-r--r--rust/examples/workflow/Cargo.toml11
-rw-r--r--rust/examples/workflow/build.rs68
-rw-r--r--rust/examples/workflow/src/lib.rs71
-rw-r--r--rust/src/function.rs9
-rw-r--r--rust/src/workflow.rs377
7 files changed, 390 insertions, 155 deletions
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index a3c62e33..7069dd72 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -1231,6 +1231,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
+name = "workflow"
+version = "0.1.0"
+dependencies = [
+ "binaryninja",
+ "log",
+]
+
+[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 7384893e..04ccb865 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -38,6 +38,7 @@ members = [
"examples/pdb-ng/demo",
"examples/template",
"examples/test_demangler",
+ "examples/workflow"
]
[profile.release]
diff --git a/rust/examples/workflow/Cargo.toml b/rust/examples/workflow/Cargo.toml
new file mode 100644
index 00000000..4c2309ca
--- /dev/null
+++ b/rust/examples/workflow/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "workflow"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+binaryninja = { path="../../" }
+log = "0.4" \ No newline at end of file
diff --git a/rust/examples/workflow/build.rs b/rust/examples/workflow/build.rs
new file mode 100644
index 00000000..5ba9bcde
--- /dev/null
+++ b/rust/examples/workflow/build.rs
@@ -0,0 +1,68 @@
+use std::env;
+use std::fs::File;
+use std::io::BufReader;
+use std::path::PathBuf;
+
+#[cfg(target_os = "macos")]
+static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun");
+
+#[cfg(target_os = "linux")]
+static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");
+
+#[cfg(windows)]
+static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");
+
+// Check last run location for path to BinaryNinja; Otherwise check the default install locations
+fn link_path() -> PathBuf {
+ use std::io::prelude::*;
+
+ let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap());
+ let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1);
+
+ File::open(lastrun)
+ .and_then(|f| {
+ let mut binja_path = String::new();
+ let mut reader = BufReader::new(f);
+
+ reader.read_line(&mut binja_path)?;
+ Ok(PathBuf::from(binja_path.trim()))
+ })
+ .unwrap_or_else(|_| {
+ #[cfg(target_os = "macos")]
+ return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS");
+
+ #[cfg(target_os = "linux")]
+ return home.join("binaryninja");
+
+ #[cfg(windows)]
+ return PathBuf::from(env::var("PROGRAMFILES").unwrap())
+ .join("Vector35\\BinaryNinja\\");
+ })
+}
+
+fn main() {
+ // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults
+ let install_path = env::var("BINARYNINJADIR")
+ .map(PathBuf::from)
+ .unwrap_or_else(|_| link_path());
+
+ #[cfg(target_os = "linux")]
+ println!(
+ "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1",
+ install_path.to_str().unwrap(),
+ install_path.to_str().unwrap(),
+ );
+
+ #[cfg(target_os = "macos")]
+ println!(
+ "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore",
+ install_path.to_str().unwrap(),
+ install_path.to_str().unwrap(),
+ );
+
+ #[cfg(target_os = "windows")]
+ {
+ println!("cargo:rustc-link-lib=binaryninjacore");
+ println!("cargo:rustc-link-search={}", install_path.to_str().unwrap());
+ }
+}
diff --git a/rust/examples/workflow/src/lib.rs b/rust/examples/workflow/src/lib.rs
new file mode 100644
index 00000000..0392b3bf
--- /dev/null
+++ b/rust/examples/workflow/src/lib.rs
@@ -0,0 +1,71 @@
+use binaryninja::architecture::CoreArchitecture;
+use binaryninja::llil::{
+ ExprInfo, LiftedNonSSA, Mutable, NonSSA, RegularNonSSA, VisitorAction, SSA,
+};
+use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
+use log::LevelFilter;
+
+const RUST_ACTIVITY_NAME: &'static str = "analysis.plugins.rustexample";
+// TODO: runOnce needs to be on...
+const RUST_ACTIVITY_CONFIG: &'static str = r#"{
+ "name": "analysis.plugins.rustexample",
+ "title" : "Rust Example",
+ "description": "This analysis step logs out some information about the function...",
+ "eligibility": {
+ "auto": { "default": true },
+ "runOnce": false
+ }
+}"#;
+
+fn example_activity(analysis_context: &AnalysisContext) {
+ let func = analysis_context.function();
+ log::info!(
+ "Activity `{}` called in function {} with workflow {:?}!",
+ RUST_ACTIVITY_NAME,
+ func.start(),
+ func.workflow().map(|wf| wf.name())
+ );
+ // If we have llil available, replace that as well.
+ if let Some(llil) = unsafe { analysis_context.llil_function::<NonSSA<LiftedNonSSA>>() } {
+ for basic_block in &func.basic_blocks() {
+ for instr in basic_block.iter() {
+ if let Some(llil_instr) = llil.instruction_at(instr) {
+ llil_instr.visit_tree(&mut |expr, info| {
+ match info {
+ ExprInfo::Const(op) => {
+ // Replace all consts with 0x1337.
+ log::info!(
+ "Replacing llil expression @ 0x{:x} : {}",
+ instr,
+ expr.index()
+ );
+ unsafe {
+ llil.replace_expression(expr.index(), llil.const_int(4, 0x1337))
+ };
+ }
+ _ => {}
+ }
+ VisitorAction::Descend
+ });
+ }
+ }
+ }
+ analysis_context.set_lifted_il_function(&llil);
+ }
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "C" fn CorePluginInit() -> bool {
+ binaryninja::logger::init(LevelFilter::Debug).unwrap();
+
+ log::info!("Initialized the plugin");
+
+ let meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis");
+ let activity = Activity::new_with_action(RUST_ACTIVITY_CONFIG, example_activity);
+ meta_workflow.register_activity(&activity).unwrap();
+ meta_workflow.insert("core.function.runFunctionRecognizers", [RUST_ACTIVITY_NAME]);
+ // Re-register the meta workflow with our changes.
+ meta_workflow.register().unwrap();
+ true
+}
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 8b38ec54..01b086f2 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -42,6 +42,8 @@ pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
use std::{fmt, mem};
use std::{ffi::c_char, hash::Hash, ops::Range};
+use std::ptr::NonNull;
+use crate::workflow::Workflow;
pub struct Location {
pub arch: Option<CoreArchitecture>,
@@ -163,6 +165,13 @@ impl Function {
}
}
+ pub fn workflow(&self) -> Option<Ref<Workflow>> {
+ unsafe {
+ let workflow = NonNull::new(BNGetWorkflowForFunction(self.handle))?;
+ Some(Workflow::ref_from_raw(workflow))
+ }
+ }
+
pub fn start(&self) -> u64 {
unsafe { BNGetFunctionStart(self.handle) }
}
diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs
index 542fa7db..ae5c8e65 100644
--- a/rust/src/workflow.rs
+++ b/rust/src/workflow.rs
@@ -1,13 +1,13 @@
-use std::{ffi, ptr};
-
use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
use crate::architecture::CoreArchitecture;
use crate::basicblock::BasicBlock;
use crate::flowgraph::FlowGraph;
use crate::function::{Function, NativeBlock};
-use crate::llil::{self, FunctionForm, FunctionMutability};
-use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::llil::{self, FunctionForm, Mutable};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
use crate::{hlil, mlil};
@@ -16,72 +16,99 @@ use crate::{hlil, mlil};
/// analysis for a given function. It allows direct modification of IL and other
/// analysis information.
pub struct AnalysisContext {
- handle: ptr::NonNull<BNAnalysisContext>,
+ handle: NonNull<BNAnalysisContext>,
}
impl AnalysisContext {
- pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNAnalysisContext>) -> Self {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNAnalysisContext>) -> Self {
Self { handle }
}
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNAnalysisContext) -> &Self {
- assert!(!handle.is_null());
- core::mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub fn as_raw(&self) -> &mut BNAnalysisContext {
- unsafe { &mut *self.handle.as_ptr() }
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNAnalysisContext>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
/// Function for the current AnalysisContext
pub fn function(&self) -> Ref<Function> {
- let result = unsafe { BNAnalysisContextGetFunction(self.as_raw()) };
+ let result = unsafe { BNAnalysisContextGetFunction(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { Function::from_raw(result) }
}
/// LowLevelILFunction used to represent Low Level IL
- pub unsafe fn llil_function<M: FunctionMutability, F: FunctionForm>(
+ pub unsafe fn lifted_il_function<F: FunctionForm>(
&self,
- ) -> Ref<llil::Function<CoreArchitecture, M, F>> {
- let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.as_raw()) };
- assert!(!result.is_null());
+ ) -> Option<Ref<llil::Function<CoreArchitecture, Mutable, F>>> {
+ let func = self.function();
+ let result = unsafe { BNGetFunctionLiftedIL(func.handle) };
+ let arch = self.function().arch();
+ unsafe {
+ Some(llil::Function::from_raw(
+ arch,
+ NonNull::new(result)?.as_ptr(),
+ ))
+ }
+ }
+
+ pub fn set_lifted_il_function<F: FunctionForm>(
+ &self,
+ value: &llil::Function<CoreArchitecture, Mutable, F>,
+ ) {
+ unsafe { BNSetLiftedILFunction(self.handle.as_ptr(), value.handle) }
+ }
+
+ /// LowLevelILFunction used to represent Low Level IL
+ pub unsafe fn llil_function<F: FunctionForm>(
+ &self,
+ ) -> Option<Ref<llil::Function<CoreArchitecture, Mutable, F>>> {
+ let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.handle.as_ptr()) };
let arch = self.function().arch();
- unsafe { llil::Function::from_raw(arch, result) }
+ unsafe {
+ Some(llil::Function::from_raw(
+ arch,
+ NonNull::new(result)?.as_ptr(),
+ ))
+ }
}
- pub fn set_llil_function<M: FunctionMutability, F: FunctionForm>(
+ pub fn set_llil_function<F: FunctionForm>(
&self,
- value: &llil::Function<CoreArchitecture, M, F>,
+ value: &llil::Function<CoreArchitecture, Mutable, F>,
) {
- unsafe { BNSetLiftedILFunction(self.as_raw(), value.handle) }
+ unsafe { BNSetLowLevelILFunction(self.handle.as_ptr(), value.handle) }
}
/// MediumLevelILFunction used to represent Medium Level IL
- pub fn mlil_function(&self) -> Ref<mlil::MediumLevelILFunction> {
- let result = unsafe { BNAnalysisContextGetMediumLevelILFunction(self.as_raw()) };
- assert!(!result.is_null());
- unsafe { mlil::MediumLevelILFunction::ref_from_raw(result) }
+ pub fn mlil_function(&self) -> Option<Ref<mlil::MediumLevelILFunction>> {
+ let result = unsafe { BNAnalysisContextGetMediumLevelILFunction(self.handle.as_ptr()) };
+ unsafe {
+ Some(mlil::MediumLevelILFunction::ref_from_raw(
+ NonNull::new(result)?.as_ptr(),
+ ))
+ }
}
pub fn set_mlil_function(&self, value: &mlil::MediumLevelILFunction) {
- unsafe { BNSetMediumLevelILFunction(self.as_raw(), value.handle) }
+ unsafe { BNSetMediumLevelILFunction(self.handle.as_ptr(), value.handle) }
}
/// HighLevelILFunction used to represent High Level IL
- pub fn hlil_function(&self, full_ast: bool) -> Ref<hlil::HighLevelILFunction> {
- let result = unsafe { BNAnalysisContextGetHighLevelILFunction(self.as_raw()) };
- assert!(!result.is_null());
- unsafe { hlil::HighLevelILFunction::ref_from_raw(result, full_ast) }
+ pub fn hlil_function(&self, full_ast: bool) -> Option<Ref<hlil::HighLevelILFunction>> {
+ let result = unsafe { BNAnalysisContextGetHighLevelILFunction(self.handle.as_ptr()) };
+ unsafe {
+ Some(hlil::HighLevelILFunction::ref_from_raw(
+ NonNull::new(result)?.as_ptr(),
+ full_ast,
+ ))
+ }
}
pub fn inform<S: BnStrCompatible>(&self, request: S) -> bool {
let request = request.into_bytes_with_nul();
unsafe {
BNAnalysisContextInform(
- self.as_raw(),
- request.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ request.as_ref().as_ptr() as *const c_char,
)
}
}
@@ -93,50 +120,57 @@ impl AnalysisContext {
let blocks: Vec<_> = blocks.into_iter().map(|block| block).collect();
let mut blocks_raw: Vec<*mut BNBasicBlock> =
blocks.iter().map(|block| block.handle).collect();
- unsafe { BNSetBasicBlockList(self.as_raw(), blocks_raw.as_mut_ptr(), blocks.len()) }
+ unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) }
}
}
-impl Clone for AnalysisContext {
- fn clone(&self) -> Self {
- unsafe {
- Self::from_raw(ptr::NonNull::new(BNNewAnalysisContextReference(self.as_raw())).unwrap())
- }
+impl ToOwned for AnalysisContext {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-impl Drop for AnalysisContext {
- fn drop(&mut self) {
- unsafe { BNFreeAnalysisContext(self.as_raw()) }
+unsafe impl RefCountable for AnalysisContext {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewAnalysisContextReference(handle.handle.as_ptr()))
+ .expect("valid handle"),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeAnalysisContext(handle.handle.as_ptr());
}
}
+// TODO: This needs to be made into a trait similar to that of `Command`.
#[repr(transparent)]
pub struct Activity {
- handle: ptr::NonNull<BNActivity>,
+ handle: NonNull<BNActivity>,
}
impl Activity {
- pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNActivity>) -> Self {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNActivity>) -> Self {
Self { handle }
}
- #[allow(clippy::mut_from_ref)]
- pub fn as_raw(&self) -> &mut BNActivity {
- unsafe { &mut *self.handle.as_ptr() }
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNActivity>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
pub fn new<S: BnStrCompatible>(config: S) -> Self {
- unsafe extern "C" fn cb_action_nop(_: *mut ffi::c_void, _: *mut BNAnalysisContext) {}
+ unsafe extern "C" fn cb_action_nop(_: *mut c_void, _: *mut BNAnalysisContext) {}
let config = config.into_bytes_with_nul();
let result = unsafe {
BNCreateActivity(
- config.as_ref().as_ptr() as *const ffi::c_char,
- ptr::null_mut(),
+ config.as_ref().as_ptr() as *const c_char,
+ std::ptr::null_mut(),
Some(cb_action_nop),
)
};
- unsafe { Activity::from_raw(ptr::NonNull::new(result).unwrap()) }
+ unsafe { Activity::from_raw(NonNull::new(result).unwrap()) }
}
pub fn new_with_action<S, F>(config: S, mut action: F) -> Self
@@ -145,39 +179,50 @@ impl Activity {
F: FnMut(&AnalysisContext),
{
unsafe extern "C" fn cb_action<F: FnMut(&AnalysisContext)>(
- ctxt: *mut ffi::c_void,
+ ctxt: *mut c_void,
analysis: *mut BNAnalysisContext,
) {
let ctxt: &mut F = core::mem::transmute(ctxt);
- ctxt(AnalysisContext::ref_from_raw(&analysis))
+ if let Some(analysis) = NonNull::new(analysis) {
+ ctxt(&AnalysisContext::from_raw(analysis))
+ }
}
let config = config.into_bytes_with_nul();
let result = unsafe {
BNCreateActivity(
- config.as_ref().as_ptr() as *const ffi::c_char,
- &mut action as *mut F as *mut ffi::c_void,
+ config.as_ref().as_ptr() as *const c_char,
+ &mut action as *mut F as *mut c_void,
Some(cb_action::<F>),
)
};
- unsafe { Activity::from_raw(ptr::NonNull::new(result).unwrap()) }
+ unsafe { Activity::from_raw(NonNull::new(result).unwrap()) }
}
pub fn name(&self) -> BnString {
- let result = unsafe { BNActivityGetName(self.as_raw()) };
+ let result = unsafe { BNActivityGetName(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
}
-impl Clone for Activity {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(ptr::NonNull::new(BNNewActivityReference(self.as_raw())).unwrap()) }
+impl ToOwned for Activity {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-impl Drop for Activity {
- fn drop(&mut self) {
- unsafe { BNFreeActivity(self.as_raw()) }
+unsafe impl RefCountable for Activity {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewActivityReference(handle.handle.as_ptr()))
+ .expect("valid handle"),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeActivity(handle.handle.as_ptr());
}
}
@@ -197,64 +242,71 @@ impl<S: BnStrCompatible> IntoActivityName for S {
}
}
+// TODO: We need to hide the JSON here behind a sensible/typed API.
#[repr(transparent)]
pub struct Workflow {
- handle: ptr::NonNull<BNWorkflow>,
+ handle: NonNull<BNWorkflow>,
}
impl Workflow {
- pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNWorkflow>) -> Self {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNWorkflow>) -> Self {
Self { handle }
}
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNWorkflow) -> &Self {
- core::mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub fn as_raw(&self) -> &mut BNWorkflow {
- unsafe { &mut *self.handle.as_ptr() }
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNWorkflow>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
+ /// Create a new unregistered [Workflow] with no activities.
+ ///
+ /// To get a copy of an existing registered [Workflow] use [Workflow::new_from_copy].
pub fn new<S: BnStrCompatible>(name: S) -> Self {
let name = name.into_bytes_with_nul();
- let result = unsafe { BNCreateWorkflow(name.as_ref().as_ptr() as *const ffi::c_char) };
- unsafe { Workflow::from_raw(ptr::NonNull::new(result).unwrap()) }
+ let result = unsafe { BNCreateWorkflow(name.as_ref().as_ptr() as *const c_char) };
+ unsafe { Workflow::from_raw(NonNull::new(result).unwrap()) }
}
- pub fn instance<S: BnStrCompatible>(name: S) -> Workflow {
- let result = unsafe {
- BNWorkflowInstance(name.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char)
- };
- unsafe { Workflow::from_raw(ptr::NonNull::new(result).unwrap()) }
+ /// Make a new unregistered [Workflow], copying all activities and the execution strategy.
+ ///
+ /// * `name` - the name for the new [Workflow]
+ #[must_use]
+ pub fn new_from_copy<S: BnStrCompatible>(name: S) -> Workflow {
+ Self::new_from_copy_with_root(name, "")
}
- /// Make a new Workflow, copying all Activities and the execution strategy.
+ /// Make a new unregistered [Workflow], copying all activities, within `root_activity`, and the execution strategy.
///
- /// * `name` - the name for the new Workflow
- /// * `activity` - if specified, perform the clone operation using
- /// ``activity`` as the root
+ /// * `name` - the name for the new [Workflow]
+ /// * `root_activity` - perform the clone operation with this activity as the root
#[must_use]
- pub fn new_from_copy<S: BnStrCompatible, A: IntoActivityName>(
- &self,
+ pub fn new_from_copy_with_root<S: BnStrCompatible, A: IntoActivityName>(
name: S,
- activity: A,
+ root_activity: A,
) -> Workflow {
let name = name.into_bytes_with_nul();
- let activity = activity.activity_name();
+ let activity = root_activity.activity_name();
+ // I can't think of a single reason as to why we should let users pass a workflow handle into this.
+ let placeholder_workflow = Workflow::instance("");
unsafe {
Self::from_raw(
- ptr::NonNull::new(BNWorkflowClone(
- self.as_raw(),
- name.as_ref().as_ptr() as *const ffi::c_char,
- activity.as_ref().as_ptr() as *const ffi::c_char,
+ NonNull::new(BNWorkflowClone(
+ placeholder_workflow.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ activity.as_ref().as_ptr() as *const c_char,
))
.unwrap(),
)
}
}
- /// List of all Workflows
+ pub fn instance<S: BnStrCompatible>(name: S) -> Workflow {
+ let result = unsafe {
+ BNWorkflowInstance(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char)
+ };
+ unsafe { Workflow::from_raw(NonNull::new(result).unwrap()) }
+ }
+
+ /// List of all registered [Workflow]'s
pub fn list() -> Array<Workflow> {
let mut count = 0;
let result = unsafe { BNGetWorkflowList(&mut count) };
@@ -263,33 +315,44 @@ impl Workflow {
}
pub fn name(&self) -> BnString {
- let result = unsafe { BNGetWorkflowName(self.as_raw()) };
+ let result = unsafe { BNGetWorkflowName(self.handle.as_ptr()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
- /// Register this Workflow, making it immutable and available for use.
+ /// Register this [Workflow], making it immutable and available for use.
+ pub fn register(&self) -> Result<(), ()> {
+ self.register_with_config("")
+ }
+
+ /// Register this [Workflow], making it immutable and available for use.
///
/// * `configuration` - a JSON representation of the workflow configuration
- pub fn register<S: BnStrCompatible>(&self, config: S) -> Result<(), ()> {
+ pub fn register_with_config<S: BnStrCompatible>(&self, config: S) -> Result<(), ()> {
let config = config.into_bytes_with_nul();
- if unsafe {
- BNRegisterWorkflow(
- self.as_raw(),
- config.as_ref().as_ptr() as *const ffi::c_char,
- )
- } {
+ if unsafe { BNRegisterWorkflow(self.handle.as_ptr(), config.as_ref().as_ptr() as *const c_char) } {
Ok(())
} else {
Err(())
}
}
- /// Register an Activity with this Workflow.
+ /// Register an [Activity] with this Workflow.
+ ///
+ /// * `activity` - the [Activity] to register
+ pub fn register_activity(&self, activity: &Activity) -> Result<Activity, ()> {
+ self.register_activity_with_subactivities::<Vec<String>>(activity, vec![])
+ }
+
+ /// Register an [Activity] with this Workflow.
///
- /// * `activity` - the Activity to register
+ /// * `activity` - the [Activity] to register
/// * `subactivities` - the list of Activities to assign
- pub fn register_activity<I>(&self, activity: &Activity, subactivities: I) -> Result<Activity, ()>
+ pub fn register_activity_with_subactivities<I>(
+ &self,
+ activity: &Activity,
+ subactivities: I,
+ ) -> Result<Activity, ()>
where
I: IntoIterator,
I::Item: IntoActivityName,
@@ -302,62 +365,61 @@ impl Workflow {
subactivities_raw.iter().map(|x| x.as_ptr()).collect();
let result = unsafe {
BNWorkflowRegisterActivity(
- self.as_raw(),
- activity.as_raw(),
+ self.handle.as_ptr(),
+ activity.handle.as_ptr(),
subactivities_ptr.as_mut_ptr(),
subactivities_ptr.len(),
)
};
- let activity_ptr = ptr::NonNull::new(result).ok_or(())?;
+ let activity_ptr = NonNull::new(result).ok_or(())?;
unsafe { Ok(Activity::from_raw(activity_ptr)) }
}
- /// Determine if an Activity exists in this Workflow.
+ /// Determine if an Activity exists in this [Workflow].
pub fn contains<A: IntoActivityName>(&self, activity: A) -> bool {
- unsafe { BNWorkflowContains(self.as_raw(), activity.activity_name().as_ptr()) }
+ unsafe { BNWorkflowContains(self.handle.as_ptr(), activity.activity_name().as_ptr()) }
}
/// Retrieve the configuration as an adjacency list in JSON for the
- /// Workflow, or if specified just for the given `activity`.
+ /// [Workflow], or if specified just for the given `activity`.
///
/// `activity` - if specified, return the configuration for the `activity`
pub fn configuration<A: IntoActivityName>(&self, activity: A) -> BnString {
let result =
- unsafe { BNWorkflowGetConfiguration(self.as_raw(), activity.activity_name().as_ptr()) };
+ unsafe { BNWorkflowGetConfiguration(self.handle.as_ptr(), activity.activity_name().as_ptr()) };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
- /// Whether this Workflow is registered or not. A Workflow becomes immutable
- /// once it is registered.
+ /// Whether this [Workflow] is registered or not. A [Workflow] becomes immutable once registered.
pub fn registered(&self) -> bool {
- unsafe { BNWorkflowIsRegistered(self.as_raw()) }
+ unsafe { BNWorkflowIsRegistered(self.handle.as_ptr()) }
}
pub fn size(&self) -> usize {
- unsafe { BNWorkflowSize(self.as_raw()) }
+ unsafe { BNWorkflowSize(self.handle.as_ptr()) }
}
/// Retrieve the Activity object for the specified `name`.
pub fn activity<A: BnStrCompatible>(&self, name: A) -> Option<Activity> {
let name = name.into_bytes_with_nul();
let result = unsafe {
- BNWorkflowGetActivity(self.as_raw(), name.as_ref().as_ptr() as *const ffi::c_char)
+ BNWorkflowGetActivity(self.handle.as_ptr(), name.as_ref().as_ptr() as *const c_char)
};
- ptr::NonNull::new(result).map(|a| unsafe { Activity::from_raw(a) })
+ NonNull::new(result).map(|a| unsafe { Activity::from_raw(a) })
}
- /// Retrieve the list of activity roots for the Workflow, or if
+ /// Retrieve the list of activity roots for the [Workflow], or if
/// specified just for the given `activity`.
///
/// * `activity` - if specified, return the roots for the `activity`
pub fn activity_roots<A: IntoActivityName>(&self, activity: A) -> Array<BnString> {
let mut count = 0;
let result = unsafe {
- BNWorkflowGetActivityRoots(self.as_raw(), activity.activity_name().as_ptr(), &mut count)
+ BNWorkflowGetActivityRoots(self.handle.as_ptr(), activity.activity_name().as_ptr(), &mut count)
};
assert!(!result.is_null());
- unsafe { Array::new(result as *mut *mut ffi::c_char, count, ()) }
+ unsafe { Array::new(result as *mut *mut c_char, count, ()) }
}
/// Retrieve the list of all activities, or optionally a filtered list.
@@ -372,14 +434,14 @@ impl Workflow {
let mut count = 0;
let result = unsafe {
BNWorkflowGetSubactivities(
- self.as_raw(),
+ self.handle.as_ptr(),
activity.activity_name().as_ptr(),
immediate,
&mut count,
)
};
assert!(!result.is_null());
- unsafe { Array::new(result as *mut *mut _, count, ()) }
+ unsafe { Array::new(result as *mut *mut c_char, count, ()) }
}
/// Assign the list of `activities` as the new set of children for the specified `activity`.
@@ -396,10 +458,10 @@ impl Workflow {
activities.into_iter().map(|a| a.activity_name()).collect();
// SAFETY: this works because BnString and *mut ffi::c_char are
// transmutable
- let input_list_ptr = input_list.as_mut_ptr() as *mut *const ffi::c_char;
+ let input_list_ptr = input_list.as_mut_ptr() as *mut *const c_char;
unsafe {
BNWorkflowAssignSubactivities(
- self.as_raw(),
+ self.handle.as_ptr(),
activity.activity_name().as_ptr(),
input_list_ptr,
input_list.len(),
@@ -407,9 +469,9 @@ impl Workflow {
}
}
- /// Remove all Activity nodes from this Workflow.
+ /// Remove all Activity nodes from this [Workflow].
pub fn clear(&self) -> bool {
- unsafe { BNWorkflowClear(self.as_raw()) }
+ unsafe { BNWorkflowClear(self.handle.as_ptr()) }
}
/// Insert the list of `activities` before the specified `activity` and at the same level.
@@ -426,10 +488,10 @@ impl Workflow {
activities.into_iter().map(|a| a.activity_name()).collect();
// SAFETY: this works because BnString and *mut ffi::c_char are
// transmutable
- let input_list_ptr = input_list.as_mut_ptr() as *mut *const ffi::c_char;
+ let input_list_ptr = input_list.as_mut_ptr() as *mut *const c_char;
unsafe {
BNWorkflowInsert(
- self.as_raw(),
+ self.handle.as_ptr(),
activity.activity_name().as_ptr(),
input_list_ptr,
input_list.len(),
@@ -438,8 +500,8 @@ impl Workflow {
}
/// Remove the specified `activity`
- pub fn remove<A: IntoActivityName>(self, activity: A) -> bool {
- unsafe { BNWorkflowRemove(self.as_raw(), activity.activity_name().as_ptr()) }
+ pub fn remove<A: IntoActivityName>(&self, activity: A) -> bool {
+ unsafe { BNWorkflowRemove(self.handle.as_ptr(), activity.activity_name().as_ptr()) }
}
/// Replace the specified `activity`.
@@ -447,32 +509,32 @@ impl Workflow {
/// * `activity` - the Activity to replace
/// * `new_activity` - the replacement Activity
pub fn replace<A: IntoActivityName, N: IntoActivityName>(
- self,
+ &self,
activity: A,
new_activity: N,
) -> bool {
unsafe {
BNWorkflowReplace(
- self.as_raw(),
+ self.handle.as_ptr(),
activity.activity_name().as_ptr(),
new_activity.activity_name().as_ptr(),
)
}
}
- /// Generate a FlowGraph object for the current Workflow and optionally show it in the UI.
+ /// Generate a FlowGraph object for the current [Workflow] and optionally show it in the UI.
///
/// * `activity` - if specified, generate the Flowgraph using `activity` as the root
/// * `sequential` - whether to generate a **Composite** or **Sequential** style graph
pub fn graph<A: IntoActivityName>(
- self,
+ &self,
activity: A,
sequential: Option<bool>,
) -> Option<FlowGraph> {
let sequential = sequential.unwrap_or(false);
let activity_name = activity.activity_name();
let graph =
- unsafe { BNWorkflowGetGraph(self.as_raw(), activity_name.as_ptr(), sequential) };
+ unsafe { BNWorkflowGetGraph(self.handle.as_ptr(), activity_name.as_ptr(), sequential) };
if graph.is_null() {
return None;
}
@@ -481,43 +543,45 @@ impl Workflow {
/// Not yet implemented.
pub fn show_metrics(&self) {
- unsafe {
- BNWorkflowShowReport(self.as_raw(), b"metrics\x00".as_ptr() as *const ffi::c_char)
- }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"metrics\x00".as_ptr() as *const c_char) }
}
/// Show the Workflow topology in the UI.
pub fn show_topology(&self) {
- unsafe {
- BNWorkflowShowReport(
- self.as_raw(),
- b"topology\x00".as_ptr() as *const ffi::c_char,
- )
- }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"topology\x00".as_ptr() as *const c_char) }
}
/// Not yet implemented.
pub fn show_trace(&self) {
- unsafe { BNWorkflowShowReport(self.as_raw(), b"trace\x00".as_ptr() as *const ffi::c_char) }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"trace\x00".as_ptr() as *const c_char) }
}
}
-impl Clone for Workflow {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(ptr::NonNull::new(BNNewWorkflowReference(self.as_raw())).unwrap()) }
+impl ToOwned for Workflow {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-impl Drop for Workflow {
- fn drop(&mut self) {
- unsafe { BNFreeWorkflow(self.as_raw()) }
+unsafe impl RefCountable for Workflow {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewWorkflowReference(handle.handle.as_ptr()))
+ .expect("valid handle"),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeWorkflow(handle.handle.as_ptr());
}
}
impl CoreArrayProvider for Workflow {
type Raw = *mut BNWorkflow;
type Context = ();
- type Wrapped<'a> = &'a Self;
+ type Wrapped<'a> = Guard<'a, Workflow>;
}
unsafe impl CoreArrayProviderInner for Workflow {
@@ -525,7 +589,10 @@ unsafe impl CoreArrayProviderInner for Workflow {
BNFreeWorkflowList(raw, count)
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Workflow::ref_from_raw(raw)
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(
+ Workflow::from_raw(NonNull::new(*raw).expect("valid handle")),
+ context,
+ )
}
}