Workspace overview¶
rust-MAFFT is a single Cargo workspace with 12 crates. 10 are publishable (crates.io); 2 stay internal.
The crates¶
crates/
├── mafft-c-bindings/ ← INTERNAL : FFI shim to MAFFT C source (cross-validation only)
├── mafft-sys/ ← published : reserved-name stub (0.0.1) for future real FFI bindings
├── mafft-types/ ← published : Sequence, SequenceSet, scoring models, segments
├── mafft-io/ ← published : FASTA, hat2, localhom, Clustal, PHYLIP I/O
├── mafft-scoring/ ← published : BLOSUM, JTT, TM matrices, gap penalties
├── mafft-fft/ ← published : hand-ported Cooley-Tukey FFT, bit-for-bit C-compat
├── mafft-align/ ← published : pairwise + profile DP (NW, SW, generalized affine)
├── mafft-tree/ ← published : NJ, UPGMA, PartTree, memsavetree
├── mafft-core/ ← published : MafftEngine, progressive alignment, refinement, --add
├── mafft/ ← published : ergonomic top-level — `cargo add mafft`
├── mafft-bin/ ← published : the `mafft-rs` CLI (`cargo install mafft-rs`)
└── pymafft/ ← INTERNAL : Python bindings via PyO3 (shipped as a wheel)
Dependency graph¶
flowchart TD
types[mafft-types]
scoring[mafft-scoring] --> types
io[mafft-io] --> types & scoring
fft[mafft-fft] --> types & scoring
align[mafft-align] --> types & scoring & fft
tree[mafft-tree] --> types & scoring
core[mafft-core] --> types & scoring & align & tree & fft & io
mafft[mafft] --> core & types & io
mafft -. feature cli .-> bin
bin[mafft-bin / mafft-rs] --> core & io & types & tree & scoring & align
pymafft[pymafft] --> core & io & types & bin
mafft-types carries the floating-point contraction policy
(mafft_types::fp::{CONTRACTS_FMA, fmadd}, features fp-contract-fma /
fp-contract-none) that every crate with FP arithmetic forwards.
mafft-io depends on mafft-scoring for the C seqcheck alphabets used by
find_illegal_residue, so there is exactly one copy of them. mafft's
edge to mafft-bin is the optional cli feature; pymafft depends on the
mafft-rs library target unconditionally (it runs every alignment through
run_from_seqs). Dev-dependencies on the C shim (mafft-c-bindings,
aliased mafft-sys) are not drawn.
Why split into so many crates¶
- Compile time. Most callers only need the engine; pulling
mafft-fft's FFT machinery for an L-INS-i workflow is wasted work. Splitting givescargoprecise rebuild boundaries. - API surface. Each crate is small enough to read in one sitting.
mafft-scoringis pure: matrices in, scores out.mafft-fftis the numerically-fussy bit, isolated. - Independent versioning. Once we hit 1.0, scoring matrices can
evolve at their own pace. The top-level
mafftre-export shields callers from sub-crate version churn. - Publishability.
pymafftships through PyPI,mafft-binthrough crates.io.mafft-c-bindings(which compiles MAFFT's C source from the git submodule) can't be published as-is and is correctly markedpublish = false.
Three ergonomic entry points¶
cargo add mafft— most users.mafftre-exports the engine, types, and I/O.use mafft::*is enough for the majority of work.cargo install mafft-rs— for the CLI binary on$PATH.cargo add mafft-rs(ormafftwithfeatures = ["cli"]) — to embed the command line's behaviour in a program. See below.
For finer control, depend on individual sub-crates (mafft-core,
mafft-fft, etc.) directly.
Embedding the CLI's flag layer¶
MafftEngine::align takes an AlignmentMode. The command line does more
than call it: --auto chooses the mode from sequence count and length,
--adjustdirection runs strand detection first, --nuc / --amino force
the type and the residue case fold, --reorder changes the output order.
That flag → behaviour layer lives in mafft-bin (crates/mafft-bin/src/lib.rs),
and a program that re-derived any of it would quietly drift from C MAFFT.
The mafft-rs library target therefore exposes it directly, argv in:
| Entry point | Input | Output |
|---|---|---|
run_from(argv, &mut out) |
INPUT file / stdin, as on the command line |
formatted text (--format) into out |
run_from_with_progress(argv, &mut out, &sink) |
same | same, progress lines to sink |
run_from_seqs(argv, &SequenceSet, &sink) |
sequences already in memory | MultipleAlignment (names + rows by value) |
Mafft::new()…run() / .run_to_vec() / .run_seqs(&set) |
typed builder over the above | as above |
All of them parse argv with the same clap definition and run the same
stages — parse_argv → preflight → (read_input | prepare_in_memory) →
align_prepared → write_alignment — so run_from_seqs returns exactly the
rows run_from would print, including --reorder's order and
--adjustdirection's _R_ name prefixes. Every failure is a MafftError
carrying the CLI's exit code and message.
use mafft_rs::{run_from_seqs, SilentProgress, Sequence, SequenceSet, SeqType};
let input = SequenceSet {
sequences: vec![
Sequence { name: "a".into(), data: b"atggctagcttggacc".to_vec() },
Sequence { name: "b".into(), data: b"atggctagcttgcacc".to_vec() },
],
seq_type: SeqType::Dna,
};
// The same flags `mafft --auto --adjustdirection --thread 1 --nuc FILE`
// would take, minus FILE: the sequences are already here.
let msa = run_from_seqs(
["mafft", "--auto", "--adjustdirection", "--thread", "1", "--nuc"],
&input,
&SilentProgress,
)?;
for (name, row) in msa.names.iter().zip(&msa.sequences) {
println!(">{name}\n{}", std::str::from_utf8(row).unwrap());
}
# Ok::<(), mafft_rs::MafftError>(())
Copies on this path: the input is borrowed and handed to the engine as-is
when it is already canonical (the reader's residue filter, the case
convention for its type, a declared seq_type); otherwise one normalised
copy is made. Flags that rewrite the input (--adjustdirection, --seed,
--anysymbol) copy once more, exactly as on the file path. The returned
rows are moved out of the engine's result, not copied.
Internal crates¶
mafft-c-bindings (publish = false)¶
Compiles MAFFT's C source (mafft-upstream/core/*.c) into a static lib
and exposes raw unsafe extern "C" bindings. Used by the
cross-validation tests — never at runtime. Can't
be published because the C source lives in a git submodule outside the
crate directory.
pymafft (publish = false on crates.io, published as a wheel on PyPI)¶
The PyO3 binding crate. Cargo doesn't publish it; maturin packages
it as a Python wheel. The wheel also embeds the mafft-rs binary so
pip install pymafft puts the CLI on $PATH.
Release flow¶
A single GitHub release fans out to four channels (see the Contributing guide for the button-press procedure):