diff --git a/README.md b/README.md index 44530a6..b41e400 100644 --- a/README.md +++ b/README.md @@ -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. +### 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). + ## 3. WFA2-LIB FEATURES * **Exact alignment** method that computes the optimal **alignment score** and/or **alignment CIGAR**. diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs new file mode 100644 index 0000000..fcb41a2 --- /dev/null +++ b/bindings/rust/build.rs @@ -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(); +} diff --git a/bindings/rust/example.rs b/bindings/rust/example.rs new file mode 100644 index 0000000..0ac3cc4 --- /dev/null +++ b/bindings/rust/example.rs @@ -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 + } +}