Skip to content

Repository files navigation

sbol-rs: a Rust implementation of SBOL

CI Documentation Crates.io Rust 1.93+ License: MIT OR Apache-2.0 DOI

sbol-rs is a Rust implementation of the Synthetic Biology Open Language (SBOL), covering both SBOL 2.3.0 and SBOL 3.1.0. SBOL is the community standard for the exchange of synthetic biology designs across registries, design-automation tools, and laboratory automation pipelines. sbol-rs exposes a typed SDK and a CLI for both versions (read, build, rewrite, validate, and losslessly convert SBOL 2 ⇄ SBOL 3), plus validators covering the 109 machine-checkable rules of SBOL 3.1.0 Appendix B and the 239 machine-checkable rules of the SBOL 2.3.0 catalog.

New to the codebase? Start with the crate guide.

Installation

Add the library to your Cargo.toml:

[dependencies]
sbol = "1"

Or with cargo add:

cargo add sbol

The CLI ships as a separate crate. cargo install sbol-cli installs a binary named sbol:

cargo install sbol-cli
sbol validate design.ttl

sbol-rs 1.x follows Cargo's Semantic Versioning compatibility rules for the public Rust APIs. The documented CLI exit codes and JSON v1 validation output schema are also stable throughout 1.x; incompatible changes to those surfaces require a new major release.

Example

use sbol::v3::constants::{EDAM_IUPAC_DNA, SBO_DNA, SO_PROMOTER};
use sbol::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let namespace = "https://example.org/lab";

    let sequence = Sequence::builder(namespace, "j23119_seq")?
        .elements("ttgacagctagctcagtcctaggtataatgctagc")
        .encoding(EDAM_IUPAC_DNA)
        .build()?;

    let component = Component::builder(namespace, "j23119")?
        .types([SBO_DNA])
        .add_component_role(SO_PROMOTER)
        .add_sequence(sequence.identity.clone())
        .name("J23119 constitutive promoter")
        .build()?;

    let document = Document::from_objects(vec![
        SbolObject::Component(component),
        SbolObject::Sequence(sequence),
    ])?;

    document.check()?;
    println!("{}", document.write_turtle()?);
    Ok(())
}

Reading documents, traversing references across documents, expanding combinatorial derivations, and inspecting validation reports are covered in crates/sbol3/examples/. Run any of them with cargo run -p sbol3 --example <name>.

Comparing documents

Document::diff compares two documents by object identity rather than by their serialized text, so serialization order, blank-node labeling, and RDF format are ignored: two serializations of the same design diff clean, and a real edit surfaces as the object whose properties moved.

use sbol::v3::constants::{SBO_DNA, SO_ENGINEERED_REGION, SO_PROMOTER};
use sbol::prelude::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let namespace = "https://example.org/lab";

    let old = Document::from_objects(vec![SbolObject::Component(
        Component::builder(namespace, "j23119")?
            .types([SBO_DNA])
            .add_component_role(SO_PROMOTER)
            .name("J23119")
            .build()?,
    )])?;

    // A revision that renames the promoter and adds a second role.
    let new = Document::from_objects(vec![SbolObject::Component(
        Component::builder(namespace, "j23119")?
            .types([SBO_DNA])
            .add_component_role(SO_PROMOTER)
            .add_component_role(SO_ENGINEERED_REGION)
            .name("J23119 constitutive promoter")
            .build()?,
    )])?;

    let diff = old.diff(&new);
    print!("{diff}");
    // ~ https://example.org/lab/j23119
    //     http://sbols.org/v3#name
    //         + "J23119 constitutive promoter"
    //         - "J23119"
    //     http://sbols.org/v3#role
    //         + https://identifiers.org/SO:0000804
    Ok(())
}

diff returns a structured value — added, removed, and changed objects, with per-predicate term changes on each changed object — so it also drives programmatic checks, not just the printed summary. See crates/sbol3/examples/diff_documents.rs.

The comparison is meaningful within one SBOL version. To compare a design across versions, upgrade the SBOL 2 document to SBOL 3 first (sbol::convert::upgrade_from_sbol2) and diff the two SBOL 3 documents; the two versions otherwise use different identity, type, and predicate IRIs, so a direct comparison reports every object as removed and re-added.

Crates

sbol-rs is a Cargo workspace. Most users depend on the sbol umbrella; the version crates are usable directly.

Crate Role
sbol Complete umbrella facade. Re-exports SBOL 2 as sbol::v2, SBOL 3 as sbol::v3, and conversion as sbol::convert; adds version detection and a version-neutral document handle.
sbol3 SBOL 3.1.0 typed data model, RDF I/O, and validator.
sbol2 SBOL 2.3.0 typed data model, RDF I/O, and validator.
sbol-core Version-neutral machinery both versions build on: field-metadata descriptors, identity newtypes, the RDF-backed document store, and the shared validation reporting and configuration types.
sbol-convert SBOL 2 ⇄ SBOL 3 conversion at the RDF triple level (upgrade_from_sbol2, downgrade).
sbol-cli The sbol command-line tool: validate, convert, and import for both versions.
sbol-rulegen Generates each version's validation rule catalog from its rules.toml.
sbol-fasta / sbol-genbank FASTA and GenBank importers to native SBOL 3.
sbol-ontology / sbol-rdf Bundled ontology snapshots and the RDF serialization layer.

Validation

sbol-rs validates both SBOL versions through one shared ValidationConfig and one diagnostic surface (text / JSON / SARIF output), with configurable scope (offline by default, or resolver-backed for cross-document references) and per-rule severity overrides.

SBOL 3.1.0 Appendix B defines 149 validation rules; 40 are marked as not-to-be-machine-reported, and sbol3 implements an algorithm for each of the remaining 109. The SBOL 2.3.0 catalog carries 268 rules, 239 of them machine-checkable, all implemented by sbol2. docs/validation.md covers what's checked and the trust boundaries; docs/conformance.md and docs/sbol2-conformance.md carry the per-rule status grids.

Ontology Extensions

EDAM, SBO, SO, GO, ChEBI, and Cell Ontology ship bundled. NCIT and lab-specific ontologies install on demand into a local cache; see docs/ontology-extensions.md.

Performance

The benchmark harness compares sbol-rs against the mainstream implementation of each SBOL version. Parse cost (median microseconds across 100 measured iterations, 20 warmup; lower is better) with every implementation in its own pinned Docker image so the rows are apples-to-apples.

SBOL 3, toggle_switch_v2.ttl (~30 KB), rows sorted by rdfxml p50 ascending:

Impl turtle rdfxml jsonld ntriples
sbol-rs 373 387 799 404
libSBOLj3 1.0.5.2 1,908 2,176 4,437 2,062
sboljs 3.0.2 n/a 2,459 n/a n/a
pySBOL3 1.2 7,435 9,807 6,501 6,906

SBOL 2, BBa_F2620 SynBioHub export (~79 KB). SBOL 2 is exchanged as RDF/XML; sbol-rs also reads Turtle, JSON-LD, and N-Triples, and libSBOLj covers RDF/XML:

Impl turtle rdfxml jsonld ntriples
sbol-rs 955 988 2,027 1,029
libSBOLj 2.4.0 n/a 1,688 n/a n/a

Apple M4 Max (12 performance and 4 efficiency cores), 128 GB RAM, macOS 26.6, Docker Desktop 29.4.3. sboljs's underlying rdfoo only emits RDF/XML and its parser stack is too fragile to reach the other format rows; the bench README documents the specific failure modes. The crates/sbol-bench crate runs the cross-implementation comparison end-to-end; see benches/cross-impl/README.md for the full SBOL 2 and SBOL 3 parse, serialize, convert, and validate tables, methodology, and per-row caveats.

About

Rust implementation of the Synthetic Biology Open Language (SBOL 3.1.0 & 2.3.0)

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages