use std::{
io::{self, prelude::*},
marker::PhantomData,
};
use mzdata::{mzpeaks::peak_set::PeakSetIter, params::Value, prelude::*};
use crate::{
mzspeclib::{Attribute, AttributeValue, Attributes, EntryType, Id, LibraryHeader},
prelude::ToMzPAF,
term,
};
use itertools::Itertools;
#[derive(Debug)]
pub struct MzSpecLibTextWriter<Writer: Write, State> {
writer: Writer,
header: LibraryHeader,
state: PhantomData<State>,
}
impl<Writer: Write, State> MzSpecLibTextWriter<Writer, State> {
pub const fn header(&self) -> &LibraryHeader {
&self.header
}
}
impl<Writer: Write> MzSpecLibTextWriter<Writer, Initial> {
pub fn new(writer: Writer) -> Self {
let header: LibraryHeader = LibraryHeader::default();
Self {
writer,
header,
state: PhantomData,
}
}
pub const fn header_mut(&mut self) -> &mut LibraryHeader {
&mut self.header
}
pub fn write_header(mut self) -> io::Result<MzSpecLibTextWriter<Writer, HeaderWritten>> {
let version = Attribute::new(
term!(MS:1003186|library format version),
AttributeValue::Scalar(Value::String(self.header.format_version.clone())),
);
writeln!(&mut self.writer, "<mzSpecLib>\n{version}")?;
for (id, group) in self.header.attributes.iter().enumerate() {
for attr in group {
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
for t in [
&EntryType::Spectrum,
&EntryType::Analyte,
&EntryType::Interpretation,
&EntryType::Cluster,
] {
for group in self
.header
.attribute_classes
.get(t)
.iter()
.flat_map(|s| s.iter())
{
writeln!(&mut self.writer, "<AttributeSet {t}={}>", group.id)?;
for (id, group) in group.attributes.iter().enumerate() {
for attr in group {
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
}
}
Ok(MzSpecLibTextWriter::<Writer, HeaderWritten> {
writer: self.writer,
header: self.header,
state: PhantomData,
})
}
}
impl<Writer: Write> MzSpecLibTextWriter<Writer, HeaderWritten> {
pub fn write_spectrum<Spectrum: MzSpecLibEncode>(
&mut self,
spectrum: &Spectrum,
) -> io::Result<()> {
writeln!(&mut self.writer, "<Spectrum={}>", spectrum.key())?;
for (id, group) in spectrum.spectrum().iter().enumerate() {
for attr in group {
if !self
.header
.is_already_defined(attr, EntryType::Spectrum, &["all"])
{
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
}
for (id, attributes) in spectrum.analytes() {
writeln!(&mut self.writer, "<Analyte={id}>")?;
for (id, group) in attributes.iter().enumerate() {
for attr in group {
if !self
.header
.is_already_defined(attr, EntryType::Analyte, &["all"])
{
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
}
}
for (id, attributes, members) in spectrum.interpretations() {
writeln!(&mut self.writer, "<Interpretation={id}>")?;
for (id, group) in attributes.iter().enumerate() {
for attr in group {
if !self
.header
.is_already_defined(attr, EntryType::Interpretation, &["all"])
{
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
}
for (key, attributes) in members {
writeln!(&mut self.writer, "<InterpretationMember={key}>",)?;
for (id, group) in attributes.iter().enumerate() {
for attr in group {
if let Some(id) = id.checked_sub(1) {
writeln!(&mut self.writer, "[{id}]{attr}")?;
} else {
writeln!(&mut self.writer, "{attr}")?;
}
}
}
}
}
writeln!(&mut self.writer, "<Peaks>")?;
let mut buffer = String::new();
for p in spectrum.peaks() {
write!(&mut self.writer, "{}\t{}", p.mz(), p.intensity())?;
if p.annotations().next().is_some() {
write!(&mut self.writer, "\t")?;
for (i, a) in p.annotations().enumerate() {
if i == 0 {
} else {
write!(&mut self.writer, ",")?;
}
buffer.clear();
a.to_mz_paf(&mut buffer).map_err(io::Error::other)?;
self.writer.write_all(buffer.as_bytes())?;
}
}
if p.aggregations().next().is_some() {
if p.annotations().next().is_none() {
write!(&mut self.writer, "\t")?;
}
write!(&mut self.writer, "\t")?;
write!(&mut self.writer, "{}", p.aggregations().join(","))?;
}
writeln!(&mut self.writer)?;
}
Ok(())
}
pub fn write_spectra<'a, Spectrum: MzSpecLibEncode + 'a>(
&mut self,
spectra: impl IntoIterator<Item = &'a Spectrum>,
) -> io::Result<()> {
for spectrum in spectra {
self.write_spectrum(spectrum)?;
}
Ok(())
}
}
#[allow(missing_debug_implementations, missing_copy_implementations)] pub struct Initial;
#[allow(missing_debug_implementations, missing_copy_implementations)] pub struct HeaderWritten;
pub trait MzSpecLibEncode {
type Peak: MzSpecLibPeakEncode;
fn key(&self) -> Id;
fn spectrum(&self) -> Attributes;
fn analytes(&self) -> impl Iterator<Item = (Id, Attributes)>;
type InterpretationMemberIter: IntoIterator<Item = (Id, Attributes)>;
fn interpretations(
&self,
) -> impl Iterator<Item = (Id, Attributes, Self::InterpretationMemberIter)>;
fn peaks(&self) -> PeakSetIter<'_, Self::Peak>;
}
pub trait MzSpecLibPeakEncode: CentroidLike {
type A: ToMzPAF;
fn annotations(&self) -> impl Iterator<Item = &Self::A>;
fn aggregations(&self) -> impl Iterator<Item = &str>;
}