rdml-qpcr 0.1.0

Read, write, and validate RDML (Real-time PCR Data Markup Language) qPCR data files
docs.rs failed to build rdml-qpcr-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: rdml-qpcr-0.1.1

rdml-qpcr

Rust implementation of RDML (Real-time PCR Data Markup Language), the RDML consortium's XML interchange format for qPCR and digital PCR data.

  • Complete model of RDML 1.3 (REC) and 1.4 (CR): every element, attribute, and enumeration, including melting curves, digital-PCR partitions with their TSV sidecar tables, all protocol step kinds, commercial assays, and oligo sequences.
  • Reads all published versions (1.0–1.4) into one model; writes 1.1–1.4. Version migrations are explicit and reported, in both directions.
  • Reads .rdml/.rdm zip containers and bare .xml (detected by content). Unknown archive members — vendor sidecar files — are preserved byte-for-byte on round-trip.
  • validate() checks the schema's identity constraints (reference resolution, id and per-element uniqueness), which XSD tooling does not, and reports all findings with document paths. Writing validates first.
  • The whole model derives serde Serialize/Deserialize with clean field names, for conversion to JSON, CBOR, etc.
  • The format's out-of-schema rules are encoded in the types: the -1.0 "not available" sentinel maps to Option, excl/note presence semantics make <excl>false</excl> unrepresentable, and ;-joined reason lists are a dedicated type.

This crate stores results computed elsewhere; it does no Cq calling, baselining, or curve fitting.

Writing a file

use rdml_qpcr::*;
use std::num::NonZeroU32;

fn main() -> rdml_qpcr::Result<()> {
    let mut doc = Rdml::new();
    doc.dyes.push(Dye::new(Id::new("SYBR")?));
    doc.samples.push(Sample::new(Id::new("liver 1")?));
    doc.targets.push(Target::new(
        Id::new("GAPDH")?,
        TargetType::Reference,
        DyeRef::new("SYBR")?,
    ));

    let mut run = Run::new(Id::new("plate 1")?, PcrFormat::plate96());
    run.run_date = Some(DateTime::new("2026-08-14T09:30:00Z")?);

    let mut react = React::new(NonZeroU32::new(1).unwrap(), SampleRef::new("liver 1")?);
    let mut data = Data::new(TargetRef::new("GAPDH")?);
    data.cq = Some(21.4); // None = not available; -1.0 is never written
    data.adps = (1..=40)
        .map(|cyc| AmpPoint::new(cyc as f64, 0.02 * cyc as f64))
        .collect();
    react.data.push(data);
    run.reacts.push(react);

    let mut experiment = Experiment::new(Id::new("pilot")?);
    experiment.runs.push(run);
    doc.experiments.push(experiment);

    // Validates first; fails on dangling references or duplicate ids.
    let report = RdmlFile::new(doc).save("pilot.rdml")?;
    assert!(report.is_lossless());
    Ok(())
}

Reading a file

fn main() -> rdml_qpcr::Result<()> {
    // Any version 1.0–1.4, zip container or bare XML.
    let file = rdml_qpcr::RdmlFile::open("pilot.rdml")?;

    for note in file.read_notes() {
        eprintln!("note: {note}"); // legacy migrations, skipped unknowns
    }

    for ctx in file.document.data_entries() {
        let well = ctx.run.pcr_format.well_name(ctx.react.id);
        let sample = &ctx.react.sample;
        match ctx.data.cq {
            Some(cq) => println!("{well}  {sample}  {}  Cq {cq:.2}", ctx.data.tar),
            None => println!("{well}  {sample}  {}", ctx.data.tar),
        }
    }
    Ok(())
}

Versions

RDML status read write
1.0 REC yes (migrated, with notes) no
1.1 REC yes yes
1.2 REC yes yes
1.3 REC yes yes (default)
1.4 CR (may still change) yes yes

Writing an older version than the document uses returns a WriteReport listing every element the target version cannot express; nothing is dropped silently. RDML 1.0 is read-only: its enumerated plate formats and well-label reaction ids are a different document shape, which reading migrates (positions mapped from labels, dye records synthesized from free-text names) with a note for each non-trivial step.

Cargo features

  • time (off by default): conversions between rdml_qpcr::DateTime and the time crate's types.

Serde support is not feature-gated.

MSRV: Rust 1.88.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

The vendored RDML schema files under schema/ are © the RDML consortium, MIT-licensed (see schema/LICENSE-RDML-consortium). Conformance test files under tests/corpus/ come from the consortium's RDMLpython and the rdml R package, both MIT; see tests/corpus/README.md for provenance.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.