1use std::{
2 cmp::Ordering,
3 fmt::{self, Display},
4 marker::PhantomData,
5 str::FromStr,
6};
7
8use mzdata_spectrum::IsolationWindowBuilder;
9use num_traits::AsPrimitive;
10use serde::{de::SeqAccess, Deserialize, Deserializer, Serialize};
11
12use crate::{
13 curie,
14 io::usi::USI,
15 params::{ControlledVocabulary, Param, ParamCow, Value, CURIE},
16 prelude::*,
17 spectrum::{
18 ArrayType, BinaryArrayMap, BinaryDataArrayType, DataArray,
19 MultiLayerSpectrum, Precursor, ScanPolarity, SignalContinuity, SpectrumDescription,
20 },
21};
22
23#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
25pub enum PROXIBackend {
26 PeptideAtlas,
27 MassIVE,
28 Pride,
29 Jpost,
30 ProteomeXchange,
31 Custom(String),
35}
36
37impl std::fmt::Display for PROXIBackend {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(
40 f,
41 "{}",
42 match self {
43 Self::PeptideAtlas => "PeptideAtlas",
44 Self::MassIVE => "MassIVE",
45 Self::Pride => "PRIDE",
46 Self::Jpost => "jPOST",
47 Self::ProteomeXchange => "ProteomeXchange",
48 Self::Custom(_) => "Custom",
49 }
50 )
51 }
52}
53
54impl PROXIBackend {
55 const ALL: &[Self] = &[
56 Self::ProteomeXchange,
57 Self::PeptideAtlas,
58 Self::MassIVE,
59 Self::Pride,
60 Self::Jpost,
61 ];
62
63 fn proxi_url(&self, usi: &str) -> String {
65 match self {
66 Self::PeptideAtlas => format!("http://www.peptideatlas.org/api/proxi/v0.1/spectra?resultType=full&usi={usi}") ,
67 Self::MassIVE => format!("http://massive.ucsd.edu/ProteoSAFe/proxi/v0.1/spectra?resultType=full&usi={usi}"),
68 Self::Pride => format!("https://www.ebi.ac.uk/pride/proxi/archive/v0.1/spectra?resultType=full&usi={usi}"),
69 Self::Jpost => format!("https://repository.jpostdb.org/proxi/spectra?resultType=full&usi={usi}"),
70 Self::ProteomeXchange => format!("https://proteomecentral.proteomexchange.org/api/proxi/v0.1/spectra?resultType=full&usi={usi}"),
71 Self::Custom(url) => url.replace("{USI}", usi),
72 }
73 }
74}
75
76impl USI {
77 pub fn download_spectrum_blocking(
97 &self,
98 backend: Option<PROXIBackend>,
99 client: Option<reqwest::blocking::Client>,
100 ) -> Result<(PROXIBackend, Vec<PROXISpectrum>), PROXIError> {
101 backend.map_or_else(
102 || {
103 let client = client.unwrap_or_default();
104 let mut last_error = None;
105 PROXIBackend::ALL
106 .iter()
107 .find_map(|backend| {
108 let response = client
109 .get(backend.proxi_url(&self.to_string()))
110 .send();
111 match response {
112 Ok(resp) => {
113 match resp.text() {
114 Ok(text) => {
115 match serde_json::from_str(&text) {
116 Ok(val) => {
117 transform_response(backend.clone(), Ok(val)).map_err(|err| {
118 last_error = Some(err);
119 }).ok()
120 },
121 Err(e) => {
122 log::error!("Error: {e} on backend {backend:?} while retrieving {self}");
123 log::trace!("PROXI response body: {text}");
124 None
125 }
126 }
127 },
128 Err(e) => {
129 transform_response(backend.clone(), Err(e)).map_err(|err| {
130 last_error = Some(err);
131 }).ok()
132 },
133 }
134 },
135 Err(e) => {
136 transform_response(backend.clone(), Err(e)).map_err(|err| {
137 last_error = Some(err);
138 }).ok()
139 },
140 }
141 })
142 .ok_or(last_error.unwrap_or(PROXIError::NotFound))
143 },
144 |backend| {
145 transform_response(
146 backend.clone(),
147 reqwest::blocking::get(backend.proxi_url(&self.to_string()))
148 .and_then(reqwest::blocking::Response::json),
149 )
150 },
151 )
152 }
153
154 #[cfg(feature = "proxi-async")]
174 pub async fn download_spectrum_async(
175 &self,
176 backend: Option<PROXIBackend>,
177 client: Option<reqwest::Client>,
178 ) -> Result<(PROXIBackend, Vec<PROXISpectrum>), PROXIError> {
179 async fn get_response(
180 client: &reqwest::Client,
181 backend: PROXIBackend,
182 usi: &str,
183 ) -> Result<(PROXIBackend, Vec<PROXISpectrum>), PROXIError> {
184 transform_response(
185 backend.clone(),
186 match client.get(backend.proxi_url(usi)).send().await {
187 Ok(r) => r.json::<PROXIResponse>().await,
188 Err(e) => Err(e),
189 },
190 )
191 }
192
193 let client = client.unwrap_or_default();
194 let usi = self.to_string();
195 if let Some(backend) = backend {
196 get_response(&client, backend, &usi).await
197 } else {
198 use futures::StreamExt;
199
200 let mut requests = futures::stream::FuturesUnordered::new();
201 let mut last_error = None;
202 for backend in PROXIBackend::ALL {
203 requests.push(get_response(&client, backend.clone(), &usi));
204 }
205
206 while let Some(res) = requests.next().await {
207 match res {
208 Ok(s) => return Ok(s),
209 Err(e) => last_error = Some(e),
210 }
211 }
212
213 Err(last_error.unwrap_or(PROXIError::NotFound))
214 }
215 }
216}
217
218fn transform_response(
219 backend: PROXIBackend,
220 response: Result<PROXIResponse, reqwest::Error>,
221) -> Result<(PROXIBackend, Vec<PROXISpectrum>), PROXIError> {
222 match response {
223 Ok(PROXIResponse::Spectra(s))
224 if s.iter()
225 .all(|s| s.status.map(|s| s == Status::Readable).unwrap_or(true)) =>
226 {
227 Ok((backend, s))
228 }
229 Ok(PROXIResponse::Spectra(s)) => Err(PROXIError::PeakUnavailable(backend, s)),
230 Ok(PROXIResponse::Error {
231 detail,
232 status,
233 title,
234 kind,
235 }) => Err(PROXIError::Error {
236 backend,
237 detail,
238 status,
239 title,
240 kind,
241 }),
242 Err(err) => Err(PROXIError::IO(backend, err)),
243 }
244}
245
246#[derive(Debug, Deserialize)]
247#[serde(untagged)]
248enum PROXIResponse {
249 Spectra(Vec<PROXISpectrum>),
250 Error {
251 #[serde(alias = "message", default)]
252 detail: String,
253 #[serde(alias = "code")]
254 status: usize,
255 title: PROXIErrorType,
256 #[serde(rename = "type", default)]
257 kind: String,
258 },
259}
260
261#[derive(Debug)]
263pub enum PROXIError {
264 IO(PROXIBackend, reqwest::Error),
266 Error {
268 backend: PROXIBackend,
270 title: PROXIErrorType,
272 detail: String,
274 status: usize,
276 kind: String,
278 },
279 PeakUnavailable(PROXIBackend, Vec<PROXISpectrum>),
281 NotFound,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub enum PROXIErrorType {
287 #[serde(rename = "DatasetNotHere")]
289 DataSetNotHere,
290 #[serde(rename = "MsRunNotFound")]
292 MsRunNotFound,
293 #[serde(rename = "ScanNotFound")]
295 ScanNotFound,
296 #[serde(rename = "UnrecognizedIdentifierFormat")]
298 UnrecognizedIdentifierFormat,
299 #[serde(rename = "MalformedInterpretation")]
304 MalformedInterpretation,
305 #[serde(rename = "UnrecognizedIndexFlag")]
307 UnrecognizedIndexFlag,
308 #[serde(rename = "MissingPreamble")]
310 MissingPreamble,
311 #[serde(rename = "TooFewFields")]
313 TooFewFields,
314 #[serde(untagged)]
315 Other(String),
316}
317
318impl std::fmt::Display for PROXIErrorType {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 write!(
321 f,
322 "{}",
323 match self {
324 Self::DataSetNotHere => "DataSetNotHere",
325 Self::MsRunNotFound => "MsRunNotFound",
326 Self::ScanNotFound => "ScanNotFound",
327 Self::UnrecognizedIdentifierFormat => "UnrecognizedIdentifierFormat",
328 Self::MalformedInterpretation => "MalformedInterpretation",
329 Self::UnrecognizedIndexFlag => "UnrecognizedIndexFlag",
330 Self::MissingPreamble => "MissingPreamble",
331 Self::TooFewFields => "TooFewFields",
332 Self::Other(o) => o,
333 }
334 )
335 }
336}
337
338#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)]
339pub enum Status {
340 #[serde(rename = "READABLE")]
341 Readable,
342 #[serde(rename = "PEAK UNAVAILABLE")]
343 PeakUnavailable,
344}
345
346#[derive(Debug, Clone, PartialEq, Eq)]
347pub struct PROXIValue(Value);
348
349impl Default for PROXIValue {
350 fn default() -> Self {
351 Self(Value::Empty)
352 }
353}
354
355impl PROXIValue {
356 pub const fn is_empty(&self) -> bool {
357 matches!(self.0, Value::Empty)
358 }
359}
360
361fn proxi_value_serialize<S>(proxi_value: &PROXIValue, serializer: S) -> Result<S::Ok, S::Error>
362where
363 S: serde::Serializer,
364{
365 match &proxi_value.0 {
366 Value::String(v) => serializer.serialize_str(v),
367 Value::Float(v) => serializer.serialize_f64(*v),
368 Value::Int(v) => serializer.serialize_i64(*v),
369 Value::Buffer(v) => serializer.serialize_bytes(v),
370 Value::Boolean(v) => serializer.serialize_bool(*v),
371 Value::Empty => serializer.serialize_unit(),
372 Value::List(v) => serializer.collect_seq(v.iter()),
373 }
374}
375
376fn proxi_value_deserialize<'de, D>(deserializer: D) -> Result<PROXIValue, D::Error>
377where
378 D: serde::Deserializer<'de>,
379{
380 struct PROXIValueVisit {}
381 impl<'de> serde::de::Visitor<'de> for PROXIValueVisit {
382 type Value = PROXIValue;
383
384 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
385 formatter.write_str("PROXIValue string")
386 }
387
388 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
389 match v.parse::<PROXIValue>() {
390 Ok(v) => Ok(v),
391 Err(e) => Err(E::custom(e)),
392 }
393 }
394
395 fn visit_bool<E: serde::de::Error>(self, v: bool) -> Result<Self::Value, E> {
396 Ok(Value::Boolean(v).into())
397 }
398
399 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
400 Ok(Value::Int(v).into())
401 }
402
403 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
404 Ok(Value::Int(v as i64).into())
405 }
406
407 fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<Self::Value, E> {
408 Ok(Value::Float(v).into())
409 }
410
411 fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
412 Ok(Value::Empty.into())
413 }
414
415 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
416 where
417 A: SeqAccess<'de>,
418 {
419 let mut entries = Vec::new();
420 while let Some(val) = SeqAccess::next_element::<Value>(&mut seq)? {
421 entries.push(val);
422 }
423 Ok(Value::List(entries.into_boxed_slice()).into())
424 }
425
426 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
427 Ok(Value::Empty.into())
428 }
429 }
430
431 deserializer.deserialize_any(PROXIValueVisit {})
432}
433
434impl ParamValue for PROXIValue {
435 fn is_empty(&self) -> bool {
436 <Value as ParamValue>::is_empty(&self.0)
437 }
438
439 fn is_i64(&self) -> bool {
440 <Value as ParamValue>::is_i64(&self.0)
441 }
442
443 fn is_f64(&self) -> bool {
444 <Value as ParamValue>::is_f64(&self.0)
445 }
446
447 fn is_buffer(&self) -> bool {
448 <Value as ParamValue>::is_buffer(&self.0)
449 }
450
451 fn is_str(&self) -> bool {
452 <Value as ParamValue>::is_str(&self.0)
453 }
454
455 fn to_f64(&self) -> Result<f64, crate::params::ParamValueParseError> {
456 <Value as ParamValue>::to_f64(&self.0)
457 }
458
459 fn to_i64(&self) -> Result<i64, crate::params::ParamValueParseError> {
460 <Value as ParamValue>::to_i64(&self.0)
461 }
462
463 fn to_str(&self) -> std::borrow::Cow<'_, str> {
464 <Value as ParamValue>::to_str(&self.0)
465 }
466
467 fn to_buffer(&self) -> Result<std::borrow::Cow<'_, [u8]>, crate::params::ParamValueParseError> {
468 <Value as ParamValue>::to_buffer(&self.0)
469 }
470
471 fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
472 <Value as ParamValue>::parse(&self.0)
473 }
474
475 fn as_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
476 <Value as ParamValue>::as_bytes(&self.0)
477 }
478
479 fn as_ref(&self) -> crate::params::ValueRef<'_> {
480 <Value as ParamValue>::as_ref(&self.0)
481 }
482
483 fn data_len(&self) -> usize {
484 <Value as ParamValue>::data_len(&self.0)
485 }
486
487 fn is_boolean(&self) -> bool {
488 <Value as ParamValue>::is_boolean(&self.0)
489 }
490
491 fn to_bool(&self) -> Result<bool, crate::params::ParamValueParseError> {
492 <Value as ParamValue>::to_bool(&self.0)
493 }
494
495 fn is_list(&self) -> bool {
496 <Value as ParamValue>::is_list(&self.0)
497 }
498
499 fn as_slice(&self) -> std::borrow::Cow<'_, [Value]> {
500 <Value as ParamValue>::as_slice(&self.0)
501 }
502}
503
504impl Display for PROXIValue {
505 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506 match &self.0 {
507 Value::String(v) => write!(f, "{v}"),
508 Value::Float(v) => write!(f, "{v}"),
509 Value::Int(v) => write!(f, "{v}"),
510 Value::Buffer(v) => write!(f, "{v:?}"),
511 Value::Boolean(v) => write!(f, "{v}"),
512 Value::Empty => Ok(()),
513 Value::List(_) => write!(f, "{}", self.0),
514 }
515 }
516}
517
518impl FromStr for PROXIValue {
519 type Err = <Value as FromStr>::Err;
520
521 fn from_str(s: &str) -> Result<Self, Self::Err> {
522 let inner = s.parse()?;
523 Ok(Self(inner))
524 }
525}
526
527impl From<Value> for PROXIValue {
528 fn from(value: Value) -> Self {
529 Self(value)
530 }
531}
532
533impl From<PROXIValue> for Value {
534 fn from(value: PROXIValue) -> Self {
535 value.0
536 }
537}
538
539fn curie_serialize<S>(curie: &CURIE, serializer: S) -> Result<S::Ok, S::Error>
540where
541 S: serde::Serializer,
542{
543 serializer.serialize_str(&curie.to_string())
544}
545
546fn curie_deserialize<'de, D>(deserializer: D) -> Result<CURIE, D::Error>
547where
548 D: serde::Deserializer<'de>,
549{
550 struct CURIEVisit {}
551 impl<'de> serde::de::Visitor<'de> for CURIEVisit {
552 type Value = CURIE;
553
554 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
555 formatter.write_str("expected CURIE string")
556 }
557
558 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
559 match v.parse::<CURIE>() {
560 Ok(v) => Ok(v),
561 Err(e) => Err(E::custom(e)),
562 }
563 }
564 }
565
566 deserializer.deserialize_str(CURIEVisit {})
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
570pub struct PROXIParam {
571 pub accession: PROXIAccession,
572 pub name: String,
573 #[serde(
574 serialize_with = "proxi_value_serialize",
575 deserialize_with = "proxi_value_deserialize",
576 skip_serializing_if = "PROXIValue::is_empty",
577 default
578 )]
579 pub value: PROXIValue,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
583pub enum PROXIAccession {
584 #[serde(
585 untagged,
586 serialize_with = "curie_serialize",
587 deserialize_with = "curie_deserialize"
588 )]
589 CURIE(CURIE),
590 #[serde(untagged)]
591 Other(String),
592}
593
594impl PROXIAccession {
595 fn controlled_vocabulary(&self) -> ControlledVocabulary {
596 match self {
597 PROXIAccession::CURIE(c) => c.controlled_vocabulary,
598 PROXIAccession::Other(_) => ControlledVocabulary::Unknown,
599 }
600 }
601}
602
603impl ParamValue for PROXIParam {
604 fn is_empty(&self) -> bool {
605 <PROXIValue as ParamValue>::is_empty(&self.value)
606 }
607
608 fn is_i64(&self) -> bool {
609 <PROXIValue as ParamValue>::is_i64(&self.value)
610 }
611
612 fn is_f64(&self) -> bool {
613 <PROXIValue as ParamValue>::is_f64(&self.value)
614 }
615
616 fn is_buffer(&self) -> bool {
617 <PROXIValue as ParamValue>::is_buffer(&self.value)
618 }
619
620 fn is_str(&self) -> bool {
621 <PROXIValue as ParamValue>::is_str(&self.value)
622 }
623
624 fn to_f64(&self) -> Result<f64, crate::params::ParamValueParseError> {
625 <PROXIValue as ParamValue>::to_f64(&self.value)
626 }
627
628 fn to_i64(&self) -> Result<i64, crate::params::ParamValueParseError> {
629 <PROXIValue as ParamValue>::to_i64(&self.value)
630 }
631
632 fn to_str(&self) -> std::borrow::Cow<'_, str> {
633 <PROXIValue as ParamValue>::to_str(&self.value)
634 }
635
636 fn to_buffer(&self) -> Result<std::borrow::Cow<'_, [u8]>, crate::params::ParamValueParseError> {
637 <PROXIValue as ParamValue>::to_buffer(&self.value)
638 }
639
640 fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
641 <PROXIValue as ParamValue>::parse(&self.value)
642 }
643
644 fn as_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
645 <PROXIValue as ParamValue>::as_bytes(&self.value)
646 }
647
648 fn as_ref(&self) -> crate::params::ValueRef<'_> {
649 <PROXIValue as ParamValue>::as_ref(&self.value)
650 }
651
652 fn data_len(&self) -> usize {
653 <PROXIValue as ParamValue>::data_len(&self.value)
654 }
655
656 fn is_boolean(&self) -> bool {
657 <PROXIValue as ParamValue>::is_boolean(&self.value)
658 }
659
660 fn to_bool(&self) -> Result<bool, crate::params::ParamValueParseError> {
661 <PROXIValue as ParamValue>::to_bool(&self.value)
662 }
663
664 fn is_list(&self) -> bool {
665 <PROXIValue as ParamValue>::is_list(&self.value)
666 }
667
668 fn as_slice(&self) -> std::borrow::Cow<'_, [Value]> {
669 <PROXIValue as ParamValue>::as_slice(&self.value)
670 }
671}
672
673impl PROXIParam {
674 pub fn new<S: ToString, V: Into<PROXIValue>>(accession: CURIE, name: S, value: V) -> Self {
675 Self {
676 accession: PROXIAccession::CURIE(accession),
677 name: name.to_string(),
678 value: value.into(),
679 }
680 }
681}
682
683impl From<Param> for PROXIParam {
684 fn from(value: Param) -> Self {
685 Self {
686 accession: PROXIAccession::CURIE(value.curie().unwrap()),
687 name: value.name,
688 value: value.value.into(),
689 }
690 }
691}
692
693impl<'a> From<ParamCow<'a>> for PROXIParam {
694 fn from(value: ParamCow<'a>) -> Self {
695 Self {
696 accession: PROXIAccession::CURIE(value.curie().unwrap()),
697 name: value.name.to_string(),
698 value: Value::from(value.value).into(),
699 }
700 }
701}
702
703const MS1_SPECTRUM: ParamCow = ControlledVocabulary::MS.const_param_ident("MS1 spectrum", 1000579);
704const MSN_SPECTRUM: ParamCow = ControlledVocabulary::MS.const_param_ident("MSn spectrum", 1000580);
705const NEGATIVE_SCAN: ParamCow =
706 ControlledVocabulary::MS.const_param_ident("negative scan", 1000129);
707const POSITIVE_SCAN: ParamCow =
708 ControlledVocabulary::MS.const_param_ident("positive scan", 1000130);
709const PROFILE_SPECTRUM: ParamCow =
710 ControlledVocabulary::MS.const_param_ident("profile spectrum", 1000128);
711const CENTROID_SPECTRUM: ParamCow =
712 ControlledVocabulary::MS.const_param_ident("centroid spectrum", 1000127);
713
714fn usi_serialize<S>(usi: &Option<USI>, serializer: S) -> Result<S::Ok, S::Error>
715where
716 S: serde::Serializer,
717{
718 match usi {
719 Some(usi) => serializer.serialize_str(&usi.to_string()),
720 None => serializer.serialize_none(),
721 }
722}
723
724fn usi_deserialize<'de, D>(deserializer: D) -> Result<Option<USI>, D::Error>
725where
726 D: serde::Deserializer<'de>,
727{
728 struct USIVisit {}
729 impl<'de> serde::de::Visitor<'de> for USIVisit {
730 type Value = Option<USI>;
731
732 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
733 formatter.write_str("USI string or null")
734 }
735
736 fn visit_none<E>(self) -> Result<Self::Value, E> {
737 Ok(None)
738 }
739
740 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
741 Ok(None)
742 }
743
744 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
745 if v == "null" {
746 Ok(None)
747 } else {
748 match v.parse::<USI>() {
749 Ok(v) => Ok(Some(v)),
750 Err(e) => Err(E::custom(e)),
751 }
752 }
753 }
754 }
755
756 deserializer.deserialize_any(USIVisit {})
757}
758
759use serde::de::{self, Visitor};
760
761#[derive(Debug, Default, Clone, Copy)]
763pub struct Wrapped<T>(T);
764
765impl<'de, T: 'static + Default + Copy + FromStr> serde::Deserialize<'de> for Wrapped<T>
766where
767 T::Err: Display,
768 f64: AsPrimitive<T>,
769 u64: AsPrimitive<T>,
770 i64: AsPrimitive<T>,
771{
772 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
773 where
774 D: de::Deserializer<'de>,
775 {
776 deserializer
777 .deserialize_any(PotentiallyWrappedNumberVisitor::<T>::default())
778 .map(Wrapped)
779 }
780}
781
782impl serde::Serialize for Wrapped<f64> {
783 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
784 where
785 S: serde::Serializer,
786 {
787 serializer.serialize_f64(self.0)
788 }
789}
790
791impl serde::Serialize for Wrapped<f32> {
792 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
793 where
794 S: serde::Serializer,
795 {
796 serializer.serialize_f32(self.0)
797 }
798}
799
800#[derive(Debug, Default)]
801struct PotentiallyWrappedNumberVisitor<T>(PhantomData<T>);
802
803impl<'de, T: 'static + Copy + FromStr> Visitor<'de> for PotentiallyWrappedNumberVisitor<T>
804where
805 T::Err: Display,
806 f64: AsPrimitive<T>,
807 u64: AsPrimitive<T>,
808 i64: AsPrimitive<T>,
809{
810 type Value = T;
811
812 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
813 formatter.write_str("a wrapped number")
814 }
815
816 fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
817 Ok(value.as_())
818 }
819
820 fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
821 Ok(value.as_())
822 }
823
824 fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
825 Ok(value.as_())
826 }
827
828 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
829 value.parse().map_err(|e| de::Error::custom(e))
830 }
831}
832
833fn deserialize_wrapped_series<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
834where
835 T: Deserialize<'de> + Default + Copy + FromStr + 'static,
836 T::Err: Display,
837 f64: AsPrimitive<T>,
838 u64: AsPrimitive<T>,
839 i64: AsPrimitive<T>,
840 D: Deserializer<'de>,
841{
842 struct WrappingVisitor<T>(PhantomData<fn() -> T>);
843
844 impl<'de, T> Visitor<'de> for WrappingVisitor<T>
845 where
846 T: Deserialize<'de> + Default + Copy + FromStr + 'static,
847 T::Err: Display,
848 f64: AsPrimitive<T>,
849 u64: AsPrimitive<T>,
850 i64: AsPrimitive<T>,
851 {
852 type Value = Vec<T>;
853
854 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
855 formatter.write_str("a nonempty sequence of numbers")
856 }
857
858 fn visit_seq<S>(self, mut seq: S) -> Result<Vec<T>, S::Error>
859 where
860 S: SeqAccess<'de>,
861 {
862 let mut buf = Vec::new();
863 while let Some(value) = seq.next_element::<Wrapped<T>>()? {
864 buf.push(value.0);
865 }
866 Ok(buf)
867 }
868 }
869 let visitor = WrappingVisitor::<T>(PhantomData);
870 deserializer.deserialize_seq(visitor)
871}
872
873#[derive(Debug, Default, Clone, Serialize, Deserialize)]
875pub struct PROXISpectrum {
876 #[serde(
878 default,
879 serialize_with = "usi_serialize",
880 deserialize_with = "usi_deserialize"
881 )]
882 pub usi: Option<USI>,
883 pub status: Option<Status>,
885 #[serde(default)]
887 pub attributes: Vec<PROXIParam>,
888 #[serde(default, deserialize_with = "deserialize_wrapped_series")]
889 pub mzs: Vec<f64>,
890 #[serde(default, deserialize_with = "deserialize_wrapped_series")]
891 pub intensities: Vec<f32>,
892 #[serde(skip_serializing_if = "Option::is_none", default)]
893 pub charges: Option<Vec<i32>>,
894}
895
896impl PROXISpectrum {
897 pub fn add_attribute<P: Into<PROXIParam>>(&mut self, param: P) {
898 self.attributes.push(param.into());
899 }
900}
901
902impl From<&PROXISpectrum> for SpectrumDescription {
903 fn from(value: &PROXISpectrum) -> Self {
904 let mut this = Self::default();
905 if let Some(usi) = value.usi.as_ref() {
906 this.id = usi.to_string();
907 if let Some(ident) = usi.identifier.as_ref() {
908 match ident {
909 super::usi::Identifier::Scan(i) => this.index = (i.saturating_sub(1)) as usize,
910 super::usi::Identifier::Index(i) => this.index = *i as usize,
911 super::usi::Identifier::NativeID(_values) => todo!(),
912 }
913 } else {
914 this.index = 0;
915 }
916 }
917
918 let mut has_precursor = false;
919 let mut precursor = Precursor::default();
920
921 for param in &value.attributes {
922 if matches!(
923 param.accession.controlled_vocabulary(),
924 ControlledVocabulary::UO
925 ) {
926 continue;
927 }
928 match param.name.as_str() {
929 "ms level" => {
930 this.ms_level = param.value.to_i32().expect("Failed to parse ms level") as u8;
931 }
932 "scan polarity" => match param.value.as_str().as_ref() {
933 "positive scan" => {
934 this.polarity = ScanPolarity::Positive;
935 }
936 "negative scan" => {
937 this.polarity = ScanPolarity::Negative;
938 }
939 _ => (),
940 },
941 "spectrum representation" => match param.value.as_str().as_ref() {
942 "profile spectrum" => {
943 this.signal_continuity = SignalContinuity::Profile;
944 }
945 "centroid spectrum" => {
946 this.signal_continuity = SignalContinuity::Centroid;
947 }
948 _ => (),
949 },
950 "scan start time" => {
951 if let Some(s) = this.acquisition.first_scan_mut() {
952 s.start_time = param
953 .value
954 .to_f64()
955 .expect("Failed to extract scan start time")
956 / 60.0;
957 }
958 }
959 "ion injection time" => {
960 if let Some(s) = this.acquisition.first_scan_mut() {
961 s.injection_time = param
962 .to_f32()
963 .expect("Failed to extract ion injection time");
964 }
965 }
966 "filter string" => {
967 if let Some(s) = this.acquisition.first_scan_mut() {
968 let mut param =
969 Param::new_key_value("filter string", param.value.to_string());
970 param.controlled_vocabulary = Some(ControlledVocabulary::MS);
971 param.accession = Some(1000512);
972 s.add_param(param);
973 }
974 }
975
976 "selected ion m/z" => {
977 has_precursor = true;
978 precursor.ion_mut().unwrap().mz =
979 param.to_f64().expect("Failed to parse ion m/z");
980 }
981 "peak intensity" => {
982 has_precursor = true;
983 precursor.ion_mut().unwrap().intensity =
984 param.to_f32().expect("Failed to parse peak intensity");
985 }
986 "charge state" => {
987 has_precursor = true;
988 precursor.ion_mut().unwrap().charge =
989 Some(param.to_i32().expect("Failed to parse ion charge"));
990 }
991
992 "isolation window target m/z" => {
993 has_precursor = true;
994 let window = IsolationWindowBuilder(precursor.isolation_window_mut());
995 window.target(param.to_f32().expect("Failed to parse isolation window target"));
996 }
997 "isolation window lower offset" => {
998 has_precursor = true;
999 let lower_bound = param
1000 .to_f32()
1001 .expect("Failed to parse isolation window limit");
1002 let window = IsolationWindowBuilder(precursor.isolation_window_mut());
1003 window.lower_offset(lower_bound);
1004 }
1005 "isolation window upper offset" => {
1006 has_precursor = true;
1007 let upper_bound = param
1008 .to_f32()
1009 .expect("Failed to parse isolation window limit");
1010 let window = IsolationWindowBuilder(precursor.isolation_window_mut());
1011 window.upper_offset(upper_bound);
1012 }
1013 "isolation window lower limit" => {
1014 has_precursor = true;
1015 let window = IsolationWindowBuilder(precursor.isolation_window_mut());
1016 let lower_bound = param
1017 .to_f32()
1018 .expect("Failed to parse isolation window limit");
1019 window.lower_limit(lower_bound);
1020 }
1021 "isolation window upper limit" => {
1022 has_precursor = true;
1023 let upper_bound = param
1024 .to_f32()
1025 .expect("Failed to parse isolation window limit");
1026 let window = IsolationWindowBuilder(precursor.isolation_window_mut());
1027 window.upper_limit(upper_bound);
1028 }
1029 _ => {
1030 let mut p = Param::new_key_value(param.name.clone(), param.value.clone());
1031 if let PROXIAccession::CURIE(c) = param.accession {
1032 p.accession = Some(c.accession);
1033 } else {
1034 log::debug!("{:?} could not be translated to a compact CURIE instance, dropping accession", param.accession)
1035 }
1036 p.controlled_vocabulary = Some(param.accession.controlled_vocabulary());
1037 this.add_param(p);
1038 }
1039 }
1040 }
1041
1042 if has_precursor {
1043 this.precursor = vec![precursor];
1044 }
1045 this
1046 }
1047}
1048
1049impl<
1050 C: CentroidLike + BuildFromArrayMap + BuildArrayMapFrom,
1051 D: DeconvolutedCentroidLike + BuildFromArrayMap + BuildArrayMapFrom,
1052 > From<PROXISpectrum> for MultiLayerSpectrum<C, D>
1053{
1054 fn from(value: PROXISpectrum) -> Self {
1055 let descr: SpectrumDescription = (&value).into();
1056 let mut arrays = BinaryArrayMap::default();
1057
1058 let mut mz_array =
1059 DataArray::from_name_and_type(&ArrayType::MZArray, BinaryDataArrayType::Float64);
1060 mz_array.extend(&value.mzs).unwrap();
1061 arrays.add(mz_array);
1062
1063 let mut intensity_array =
1064 DataArray::from_name_and_type(&ArrayType::IntensityArray, BinaryDataArrayType::Float32);
1065 intensity_array.extend(&value.intensities).unwrap();
1066 arrays.add(intensity_array);
1067
1068 if let Some(charges) = value.charges.as_ref() {
1069 let mut charge_arrays =
1070 DataArray::from_name_and_type(&ArrayType::ChargeArray, BinaryDataArrayType::Int32);
1071 charge_arrays.extend(charges).unwrap();
1072 arrays.add(charge_arrays);
1073 };
1074
1075 Self::from_arrays_and_description(arrays, descr)
1076 }
1077}
1078
1079impl<T> From<&T> for PROXISpectrum
1080where
1081 T: SpectrumLike,
1082{
1083 fn from(value: &T) -> Self {
1084 let mut this = Self {
1085 status: Some(Status::Readable),
1086 ..Default::default()
1087 };
1088
1089 match value.ms_level().cmp(&1) {
1090 Ordering::Equal => this.add_attribute(MS1_SPECTRUM.clone()),
1091 Ordering::Greater => this.add_attribute(MSN_SPECTRUM.clone()),
1092 Ordering::Less => (),
1093 }
1094
1095 for param in value.params().iter().filter(|p| p.is_controlled()) {
1096 if param.curie().unwrap() == curie!(MS:1003063) {
1097 this.usi = Some(param.value.as_str().parse().unwrap());
1098 } else {
1099 this.add_attribute(param.clone());
1100 }
1101 }
1102
1103 this.add_attribute(PROXIParam::new(
1104 curie!(MS:10003057),
1105 "scan number",
1106 Value::Int((value.index() + 1) as i64),
1107 ));
1108
1109 this.add_attribute(PROXIParam {
1110 name: "ms level".to_string(),
1111 value: PROXIValue(Value::Int(value.ms_level() as i64)),
1112 accession: PROXIAccession::CURIE(curie!(MS:1000511)),
1113 });
1114
1115 match value.polarity() {
1116 crate::spectrum::ScanPolarity::Unknown => {}
1117 crate::spectrum::ScanPolarity::Positive => {
1118 this.attributes.push(Param::from(POSITIVE_SCAN).into());
1119 }
1120 crate::spectrum::ScanPolarity::Negative => {
1121 this.attributes.push(Param::from(NEGATIVE_SCAN).into());
1122 }
1123 }
1124
1125 match value.signal_continuity() {
1126 crate::spectrum::SignalContinuity::Unknown => {}
1127 crate::spectrum::SignalContinuity::Centroid => {
1128 this.attributes.push(Param::from(CENTROID_SPECTRUM).into());
1129 }
1130 crate::spectrum::SignalContinuity::Profile => {
1131 this.attributes.push(Param::from(PROFILE_SPECTRUM).into());
1132 }
1133 }
1134
1135 if let Some(precursor) = value.precursor() {
1136 let iw = precursor.isolation_window();
1137 this.add_attribute(ControlledVocabulary::MS.param_val(
1138 "MS:1000827",
1139 "isolation window target m/z",
1140 iw.target,
1141 ));
1142 this.add_attribute(ControlledVocabulary::MS.param_val(
1143 "MS:1000828",
1144 "isolation window lower offset",
1145 iw.target - iw.lower_bound,
1146 ));
1147 this.add_attribute(ControlledVocabulary::MS.param_val(
1148 "MS:1000829",
1149 "isolation window upper offset",
1150 iw.upper_bound - iw.target,
1151 ));
1152
1153 let ion = precursor.ion().unwrap();
1154 this.add_attribute(PROXIParam::new(
1155 curie!(MS:1000744),
1156 "selected ion m/z",
1157 Value::Float(ion.mz),
1158 ));
1159
1160 if let Some(z) = ion.charge {
1161 this.add_attribute(PROXIParam::new(
1162 curie!(MS:1000041),
1163 "charge state",
1164 Value::Int(z as i64),
1165 ));
1166 }
1167 }
1168
1169 for param in value.acquisition().params() {
1170 if param.is_controlled() {
1171 this.add_attribute(param.clone());
1172 }
1173 }
1174
1175 for event in value.acquisition().iter() {
1176 let p = PROXIParam {
1177 name: "scan start time".into(),
1178 accession: PROXIAccession::CURIE(curie!(MS:1000016)),
1179 value: Value::Float(event.start_time * 60.0).into(),
1180 };
1181 this.add_attribute(p);
1182 for param in event.params() {
1183 if param.is_controlled() {
1184 this.add_attribute(param.clone());
1185 }
1186 }
1187 }
1188
1189 match value.peaks() {
1190 crate::spectrum::RefPeakDataLevel::Missing => {}
1191 crate::spectrum::RefPeakDataLevel::RawData(arrays) => {
1192 this.mzs = arrays.mzs().unwrap().iter().copied().collect();
1193 this.intensities = arrays.intensities().unwrap().iter().copied().collect();
1194 if let Ok(arr) = arrays.charges() {
1195 this.charges = Some(arr.to_vec());
1196 }
1197 }
1198 crate::spectrum::RefPeakDataLevel::Centroid(peaks) => {
1199 (this.mzs, this.intensities) =
1200 peaks.iter().map(|p| (p.mz(), p.intensity())).unzip();
1201 }
1202 crate::spectrum::RefPeakDataLevel::Deconvoluted(peaks) => {
1203 (this.mzs, this.intensities) =
1204 peaks.iter().map(|p| (p.mz(), p.intensity())).unzip();
1205 this.charges = Some(
1206 peaks
1207 .iter()
1208 .map(mzpeaks::KnownCharge::charge)
1209 .collect::<Vec<_>>(),
1210 );
1211 }
1212 }
1213
1214 if this.mzs.is_empty() {
1215 this.status = Some(Status::PeakUnavailable);
1216 }
1217
1218 this
1219 }
1220}
1221
1222#[cfg(test)]
1223mod test {
1224 use std::io;
1225
1226 use super::*;
1227 use crate::MZReader;
1228 use serde_json;
1229
1230 #[test]
1231 fn test_convert() -> io::Result<()> {
1232 let mut reader = MZReader::open_path("./test/data/batching_test.mzML")?;
1233
1234 let scan = reader.get_spectrum_by_index(0).unwrap();
1235 let scan_message = PROXISpectrum::from(&scan);
1236
1237 let message = serde_json::to_string(&scan_message)?;
1238 let dup: PROXISpectrum = serde_json::from_str(&message)?;
1239 assert_eq!(dup.usi, scan_message.usi);
1240 assert_eq!(dup.attributes, scan_message.attributes);
1241 Ok(())
1242 }
1243
1244 #[test]
1245 fn get_peptide_atlas() {
1246 let usi: USI =
1247 "mzspec:PXD000561:Adult_Frontalcortex_bRP_Elite_85_f09:scan:17555:VLHPLEGAVVIIFK/2"
1248 .parse()
1249 .unwrap();
1250 let (_, response) = usi
1251 .download_spectrum_blocking(Some(PROXIBackend::PeptideAtlas), None)
1252 .unwrap();
1253 assert!(!response.is_empty());
1254 }
1255
1256 #[ignore]
1257 #[test]
1258 fn get_massive() {
1259 let usi: USI = "mzspec:MSV000078547:120228_nbut_3610_it_it_take2:scan:389"
1260 .parse()
1261 .unwrap();
1262 let (_, response) = usi
1263 .download_spectrum_blocking(Some(PROXIBackend::MassIVE), None)
1264 .unwrap();
1265 assert!(!response.is_empty());
1266 }
1267
1268 #[test]
1269 fn get_pride() {
1270 let usi: USI =
1271 "mzspec:PXD043489:20201103_F1_UM5_Peng0013_SA_139H2_InS_Elastase.raw:scan:11809:VSLFPPSSEQLTSNASVV/2"
1272 .parse()
1273 .unwrap();
1274 let (_, response) = usi
1275 .download_spectrum_blocking(Some(PROXIBackend::Pride), None)
1276 .unwrap();
1277 assert!(!response.is_empty());
1278 }
1279
1280 #[test]
1281 fn get_proteomexchange() {
1282 let usi: USI =
1283 "mzspec:PXD004939:Rice_phos_ABA_3h_20per_F1_R2:scan:2648:DAEKS[UNIMOD:21]PIN[UNIMOD:7]GR/2"
1284 .parse()
1285 .unwrap();
1286 let (_, response) = usi
1287 .download_spectrum_blocking(Some(PROXIBackend::ProteomeXchange), None)
1288 .unwrap();
1289 assert!(!response.is_empty());
1290 }
1291
1292 #[test]
1293 fn get_aggregate() {
1294 for usi in [
1295 "mzspec:PXD000561:Adult_Frontalcortex_bRP_Elite_85_f09:scan:17555:VLHPLEGAVVIIFK/2",
1296 "mzspec:MSV000078547:120228_nbut_3610_it_it_take2:scan:389",
1297 "mzspec:PXD043489:20201103_F1_UM5_Peng0013_SA_139H2_InS_Elastase.raw:scan:11809:VSLFPPSSEQLTSNASVV/2",
1298 "mzspec:PXD004939:Rice_phos_ABA_3h_20per_F1_R2:scan:2648:DAEKS[UNIMOD:21]PIN[UNIMOD:7]GR/2"] {
1299 println!("Trying: {usi}");
1300 let usi: USI = usi.parse().unwrap();
1301 let (_, response) = usi.download_spectrum_blocking(None, None).unwrap();
1302 assert!(!response.is_empty());
1303 }
1304 }
1305
1306 #[test]
1307 fn test_proxi_parse() {
1308 let spec: PROXISpectrum =
1309 serde_json::from_reader(std::fs::File::open("test/data/proxi_test.json").unwrap())
1310 .unwrap();
1311 assert!(!spec.mzs.is_empty());
1312 assert!(!spec.attributes.is_empty());
1313 }
1314}
1315
1316#[cfg(all(feature = "proxi-async", feature = "async_partial"))]
1317#[cfg(test)]
1318mod test_async {
1319 use super::*;
1320
1321 #[tokio::test]
1322 async fn get_aggregate_async() {
1323 for usi in [
1324 "mzspec:PXD000561:Adult_Frontalcortex_bRP_Elite_85_f09:scan:17555:VLHPLEGAVVIIFK/2",
1325 "mzspec:MSV000078547:120228_nbut_3610_it_it_take2:scan:389",
1326 "mzspec:PXD043489:20201103_F1_UM5_Peng0013_SA_139H2_InS_Elastase.raw:scan:11809:VSLFPPSSEQLTSNASVV/2",
1327 "mzspec:PXD004939:Rice_phos_ABA_3h_20per_F1_R2:scan:2648:DAEKS[UNIMOD:21]PIN[UNIMOD:7]GR/2"] {
1328 println!("Trying: {usi}");
1329 let usi: USI = usi.parse().unwrap();
1330 let (_, response) = usi.download_spectrum_async(None, None).await.unwrap();
1331 assert!(!response.is_empty());
1332 }
1333 }
1334}