Skip to content

Commit

Permalink
Merge pull request smarco#29 from RagnarGrootKoerkamp/development
Browse files Browse the repository at this point in the history
Add rust bindings
  • Loading branch information
smarco authored Aug 2, 2022
2 parents 98b16f7 + 16558b8 commit 3175dfb
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ cout << "Alignment score " << aligner.getAlignmentScore() << endl;

**IMPORTANT.** Once an alignment object is created, **it is strongly recommended to reuse it to compute multiple alignments**. Creating and destroying the alignment object for every alignment computed can have a significant overhead. Reusing the alignment object allows repurposing internal data structures, minimising the cost of memory allocations, and avoiding multiple alignment setups and precomputations.

### <a name="wfa2.programming.rust"></a> 2.3 Rust bindings

Rust bindings can be generated automatically using `bindgen`, see [bindings/rust/build.rs](bindings/rust/build.rs).
An example of how to use them is [here](./bindings/rust/example.rs).

## <a name="wfa2.features"></a> 3. WFA2-LIB FEATURES

* **Exact alignment** method that computes the optimal **alignment score** and/or **alignment CIGAR**.
Expand Down
65 changes: 65 additions & 0 deletions bindings/rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! WFA bindings for rust using Bindgen.
//!
//! First, add `bindgen` to your dependencies:
//! ```toml
//! [build-dependencies]
//! bindgen = "0.60.1"
//! ```
//!
//! Then save this file as `build.rs` in the root of your crate.
//! Update the paths below to match your WFA installation/repository.
//!
//! The example has the WFA2-lib repository cloned in `../wfa2`.
//! Make sure to run `make lib_wfa` in the WFA repository.
//! The code below will tell cargo to link against `../wfa2/lib/libwfa.a`.
//!
//! The bindings will be writted to a special `OUT_DIR` set by cargo. See
//! `example.rs` for an example of how to include and use the generated
//! bindings.
extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn wfa() {
// 1. Link instructions for Cargo.

// The directory of the WFA libraries, added to the search path.
println!("cargo:rustc-link-search=../wfa2/lib");
// Link the `wfa-lib` library.
println!("cargo:rustc-link-lib=wfa");
// Also link `omp`.
println!("cargo:rustc-link-lib=omp");
// Invalidate the built crate whenever the linked library changes.
println!("cargo:rerun-if-changed=../wfa2/lib/libwfa.a");

// 2. Generate bindings.

let bindings = bindgen::Builder::default()
// Generate bindings for this header file.
.header("../wfa2/wavefront/wavefront_align.h")
// Add this directory to the include path to find included header files.
.clang_arg("-I../wfa2")
// Generate bindings for all functions starting with `wavefront_`.
.allowlist_function("wavefront_.*")
// Generate bindings for all variables starting with `wavefront_`.
.allowlist_var("wavefront_.*")
// Invalidate the built crate whenever any of the included header files
// changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");

// Write the bindings to the $OUT_DIR/bindings_wfa.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings_wfa.rs"))
.expect("Couldn't write bindings!");
}

fn main() {
wfa();
}
46 changes: 46 additions & 0 deletions bindings/rust/example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/// Include the generated bindings into a separate module.
#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(unused)]
mod wfa {
include!(concat!(env!("OUT_DIR"), "/bindings_wfa.rs"));
}

/// Compute the affine alignment score between `a` and `b` with the given substitution,
/// gap-open, and gap-extend penalties.
fn linear_score(a: &[i8], b: &[i8], sub: i32, indel: i32) -> i32 {
unsafe {
let mut attributes = wfa::wavefront_aligner_attr_default;
// Do not use a heuristic (enabled by default).
attributes.heuristic.strategy = wfa::wf_heuristic_strategy_wf_heuristic_none;
// Only compute the score (not a path).
attributes.alignment_scope = wfa::alignment_scope_t_compute_score;

// Set the cost model and parameters.
attributes.distance_metric = wfa::distance_metric_t_gap_affine;
attributes.affine_penalties.mismatch = mismatch as i32;
attributes.affine_penalties.gap_opening = gap_open as i32;
attributes.affine_penalties.gap_extension = gap_extend as i32;

// Initialize the aligner object.
// This should be reused for multiple queries.
let wf_aligner = wfa::wavefront_aligner_new(&mut attributes);

// Do the alignment.
let status = wfa::wavefront_align(
wf_aligner,
a.as_ptr(),
a.len() as i32,
b.as_ptr(),
b.len() as i32,
);
assert_eq!(status, 0);

let score = (*wf_aligner).cigar.score;

// Clean up memory.
wfa::wavefront_aligner_delete(wf_aligner);
score
}
}

0 comments on commit 3175dfb

Please sign in to comment.