imzml 0.1.3

A library for reading the mass spectrometry (imaging) formats mzML and imzML.
Documentation
use std::io::Write;
use std::sync::Arc;

use quick_xml::events::{BytesEnd, BytesStart, Event};
use quick_xml::Result;

use crate::obo::{Ontology, MZML_ONTOLOGY};

use super::cvparam::{Accession, CVParam, CVParamValue};
use super::MzMLTag;

/// Writer for mzML
pub struct Writer<W: Write> {
    writer: quick_xml::Writer<W>,
    ontology: Ontology,
}

impl<W: Write> Writer<W> {
    /// Create a new mzML writer, using the default mzML ontology
    pub fn new(inner: W) -> Result<Self> {
        Self::with_ontology(inner, MZML_ONTOLOGY.clone())
    }

    /// Create a new mzML writer with the supplied ontology
    pub fn with_ontology(inner: W, ontology: Ontology) -> Result<Self> {
        let mut writer = quick_xml::Writer::new_with_indent(inner, b' ', 2);
        let decl_elem = quick_xml::events::BytesDecl::new("1.1", Some("UTF-8"), Some("yes"));
        writer.write_event(quick_xml::events::Event::Decl(decl_elem))?;

        Ok(Self { writer, ontology })
    }

    /// Returns the ontology used by the writer
    pub fn ontology(&self) -> &Ontology {
        &self.ontology
    }

    /// Create a open tag for a list, including the "count" attribute
    // pub fn start_list(&mut self, tag_name: &str, count: usize) -> Result<()> {
    //     self.start_tag_with_attr(tag_name, "count", count)
    // }

    /// Create an opening tag with the given name
    pub fn start_tag(&mut self, tag_name: &str) -> Result<()> {
        let elem = BytesStart::new(tag_name);

        self.writer.write_event(Event::Start(elem))
    }

    /// Create a closing tag with the given name
    pub fn end_tag(&mut self, name: &str) -> Result<()> {
        self.writer.write_event(Event::End(BytesEnd::new(name)))
    }

    /// Write out an empty tag with the given name
    pub fn empty_tag(&mut self, tag_name: &str) -> Result<()> {
        let empty_tag = BytesStart::new(tag_name);
        self.writer.write_event(Event::Empty(empty_tag))
    }

    /// Create a closing tag with one attribute
    pub fn start_tag_with_attr<D: std::fmt::Display>(
        &mut self,
        tag_name: &str,
        attribute_name: &str,
        value: D,
    ) -> Result<()> {
        let mut elem = BytesStart::new(tag_name);
        // encodedLength is required
        elem.push_attribute((attribute_name, format!("{}", value).as_str()));

        self.writer.write_event(Event::Start(elem))
    }

    /// Create a empty tag with one attribute
    pub fn empty_tag_with_attr<D: std::fmt::Display>(
        &mut self,
        tag_name: &str,
        attribute_name: &str,
        value: D,
    ) -> Result<()> {
        let mut empty_tag = BytesStart::new(tag_name);
        // encodedLength is required
        empty_tag.push_attribute((attribute_name, format!("{}", value).as_str()));

        self.writer.write_event(Event::Empty(empty_tag))
    }

    /// Write out the supplied event
    pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
        self.writer.write_event(event)
    }

    /// Write out a list, including a `count` attribute
    pub fn write_list<T: MzMLTag>(&mut self, tag_name: &str, list: &[T]) -> Result<()> {
        self.start_tag_with_attr(tag_name, "count", list.len())?;

        for item in list {
            item.write_xml(self)?;
        }

        self.end_tag(tag_name)
    }

    /// Write out a list of Arc<T> references, including a `count` attribute
    pub fn write_arc_list<T: MzMLTag>(&mut self, tag_name: &str, list: &[Arc<T>]) -> Result<()> {
        self.start_tag_with_attr(tag_name, "count", list.len())?;

        for item in list {
            item.write_xml(self)?;
        }

        self.end_tag(tag_name)
    }

    /// Write out a CV param
    pub fn write_param(&mut self, accession: &str, value: CVParamValue) -> Result<()> {
        let param = CVParam::new(
            Accession::Term(self.ontology.get(accession).unwrap().clone()),
            value,
        );

        param.write_xml(self)
    }
}