rdml-qpcr 0.1.1

Read, write, and validate RDML (Real-time PCR Data Markup Language) qPCR data files
Documentation
//! Crate-wide error types.

use crate::validate::ValidationReport;
use crate::version::RdmlVersion;

/// Convenience alias for `Result<T, rdml_qpcr::Error>`.
pub type Result<T> = std::result::Result<T, Error>;

/// The error type for reading, writing, and validating RDML files.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
    /// An underlying I/O operation failed.
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),

    /// The zip archive could not be read or written.
    #[error("zip archive error: {0}")]
    Zip(#[from] zip::result::ZipError),

    /// The XML could not be tokenised at all (malformed markup).
    #[error("malformed XML: {0}")]
    Xml(#[from] quick_xml::Error),

    /// The file is not valid UTF-8. RDML requires UTF-8 encoding.
    #[error("RDML data is not valid UTF-8 (the format requires UTF-8): {0}")]
    NotUtf8(#[from] std::str::Utf8Error),

    /// The input is not an RDML document.
    #[error("not an RDML document: {reason}")]
    NotRdml {
        /// What disqualified the input: a wrong root element name, or no
        /// XML root element at all.
        reason: String,
    },

    /// The root element carries a `version` attribute this crate does not know.
    #[error("unsupported RDML version `{0}` (supported: 1.0, 1.1, 1.2, 1.3, 1.4)")]
    UnsupportedVersion(String),

    /// The root element carries no `version` attribute.
    #[error("RDML root element has no `version` attribute")]
    MissingVersion,

    /// Writing was requested for a version this crate cannot emit.
    ///
    /// Only RDML 1.0 is affected: its `pcrFormat`/`react@id` structure is a
    /// different document shape that the consortium's own tooling treats as
    /// legacy. See the crate docs on versions.
    #[error("cannot write RDML {0}: this crate reads 1.0 but only writes 1.1–1.4")]
    UnsupportedWriteVersion(RdmlVersion),

    /// The XML was well-formed but did not match the RDML structure.
    ///
    /// `path` is a human-readable location such as
    /// `rdml/experiment[id=exp1]/run[id=run1]/react[3]/data[1]/cq`.
    #[error("invalid RDML at {path}: {message}")]
    Invalid {
        /// Location of the offending content within the document.
        path: String,
        /// What was wrong with it.
        message: String,
    },

    /// A value failed the constraints of its type (empty id, bad IUPAC
    /// sequence, malformed date-time, …).
    #[error("invalid value: {0}")]
    InvalidValue(String),

    /// The document failed [`validate`](crate::Rdml::validate) before
    /// writing. Contains every problem found, not just the first.
    #[error("document failed validation with {} error(s); first: {}",
            .0.errors().count(),
            .0.errors().next().map(ToString::to_string).unwrap_or_default())]
    Validation(ValidationReport),

    /// A digital-PCR partition table (TSV sidecar) was malformed.
    #[error("invalid partition table {name}: line {line}: {message}")]
    PartitionTable {
        /// Archive member name of the table.
        name: String,
        /// 1-based line number of the offending row (0 for the header).
        line: usize,
        /// What was wrong.
        message: String,
    },

    /// The requested archive member does not exist.
    #[error("no such archive member: {0}")]
    NoSuchMember(String),
}