Skip to content

Commit 019fd5e

Browse files
committed
feat(polychem): allow more modification conversions
Added more `From` impls for modification kinds and wrappers, and also refactored `SignedCount` -> `Charge`, and added blanket impls for refs and to automatically derive the `Mz` trait.
1 parent 69e493e commit 019fd5e

File tree

10 files changed

+140
-59
lines changed

10 files changed

+140
-59
lines changed

crates/polychem/src/atoms/chemical_composition.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use rust_decimal::Decimal;
66

77
// Local Crate Imports
88
use super::{atomic_database::AtomicDatabase, chemical_composition_parser::chemical_composition};
9-
use crate::{
10-
Charge, Charged, ChemicalComposition, Count, Element, Massive, Mz, Result, SignedCount,
11-
};
9+
use crate::{Charge, Charged, ChemicalComposition, Count, Element, Massive, Result};
1210

1311
// Public API ==========================================================================================================
1412

@@ -36,16 +34,14 @@ impl Charged for ChemicalComposition<'_> {
3634
self.particle_offset
3735
.as_ref()
3836
.map(|(k, c, p)| {
39-
let sign = SignedCount::from(*k);
37+
let sign = Charge::from(*k);
4038
let c = Charge::from(*c);
4139
sign * c * p.charge()
4240
})
4341
.unwrap_or_default()
4442
}
4543
}
4644

47-
impl Mz for ChemicalComposition<'_> {}
48-
4945
// Display and Hash Trait Implementations ==============================================================================
5046

5147
impl Display for ChemicalComposition<'_> {

crates/polychem/src/atoms/offset_kind.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
22

33
use rust_decimal::Decimal;
44

5-
use crate::{OffsetKind, SignedCount};
5+
use crate::{Charge, OffsetKind};
66

77
impl Display for OffsetKind {
88
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@@ -19,11 +19,11 @@ impl Display for OffsetKind {
1919

2020
impl From<OffsetKind> for Decimal {
2121
fn from(value: OffsetKind) -> Self {
22-
SignedCount::from(value).into()
22+
Charge::from(value).into()
2323
}
2424
}
2525

26-
impl From<OffsetKind> for SignedCount {
26+
impl From<OffsetKind> for Charge {
2727
fn from(value: OffsetKind) -> Self {
2828
match value {
2929
OffsetKind::Add => 1,
@@ -47,11 +47,11 @@ mod tests {
4747
}
4848

4949
#[test]
50-
fn into_signed_count() {
50+
fn into_charge() {
5151
let add = OffsetKind::Add;
52-
assert_eq!(SignedCount::from(add), 1);
52+
assert_eq!(Charge::from(add), 1);
5353
let remove = OffsetKind::Remove;
54-
assert_eq!(SignedCount::from(remove), -1);
54+
assert_eq!(Charge::from(remove), -1);
5555
}
5656

5757
#[test]

crates/polychem/src/atoms/particle.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
22

33
use rust_decimal::Decimal;
44

5-
use crate::{Charge, Charged, Massive, Mz, Particle};
5+
use crate::{Charge, Charged, Massive, Particle};
66

77
use super::{
88
atomic_database::{AtomicDatabase, ParticleDescription},
@@ -51,8 +51,6 @@ impl Charged for Particle<'_> {
5151
}
5252
}
5353

54-
impl Mz for Particle<'_> {}
55-
5654
#[cfg(test)]
5755
mod tests {
5856
use insta::assert_debug_snapshot;

crates/polychem/src/lib.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ pub struct Modification<K> {
6262
pub kind: K,
6363
}
6464

65-
type SignedCount = i64;
66-
6765
// ---------------------------------------------------------------------------------------------------------------------
6866

6967
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
@@ -162,16 +160,52 @@ pub trait Charged {
162160

163161
pub trait Mz: Massive + Charged {
164162
fn monoisotopic_mz(&self) -> Option<Decimal> {
165-
let charge = self.charge().abs();
166-
(charge != 0).then(|| self.monoisotopic_mass() / Decimal::from(charge))
163+
let charge = Decimal::from(self.charge()).abs();
164+
(!charge.is_zero()).then(|| self.monoisotopic_mass() / charge)
167165
}
168166

169167
fn average_mz(&self) -> Option<Decimal> {
170-
let charge = self.charge().abs();
171-
(charge != 0).then(|| self.average_mass() / Decimal::from(charge))
168+
let charge = Decimal::from(self.charge()).abs();
169+
(!charge.is_zero()).then(|| self.average_mass() / charge)
172170
}
173171
}
174172

173+
// Blanket impls
174+
175+
macro_rules! massive_ref_impls {
176+
($($ref_type:ty),+ $(,)?) => {
177+
$(
178+
impl<T: Massive> Massive for $ref_type {
179+
fn monoisotopic_mass(&self) -> Decimal {
180+
(**self).monoisotopic_mass()
181+
}
182+
183+
fn average_mass(&self) -> Decimal {
184+
(**self).average_mass()
185+
}
186+
}
187+
)+
188+
};
189+
}
190+
191+
massive_ref_impls!(&T, &mut T, Box<T>);
192+
193+
macro_rules! charged_ref_impls {
194+
($($ref_type:ty),+ $(,)?) => {
195+
$(
196+
impl<T: Charged> Charged for $ref_type {
197+
fn charge(&self) -> Charge {
198+
(**self).charge()
199+
}
200+
}
201+
)+
202+
};
203+
}
204+
205+
charged_ref_impls!(&T, &mut T, Box<T>);
206+
207+
impl<T: Massive + Charged> Mz for T {}
208+
175209
pub type Result<T, E = Box<PolychemError>> = std::result::Result<T, E>;
176210

177211
// FIXME: Maybe there are too many layers of things being wrapped here!

crates/polychem/src/polymers/any_mod.rs

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rust_decimal::Decimal;
22

33
use crate::{
4-
atoms::atomic_database::AtomicDatabase, AnyMod, Charge, Charged, Massive, Mz, NamedMod,
5-
OffsetKind, OffsetMod, Result,
4+
atoms::atomic_database::AtomicDatabase, AnyMod, AnyModification, Charge, Charged, Massive,
5+
Modification, NamedMod, OffsetKind, OffsetMod, Result,
66
};
77

88
use super::polymer_database::PolymerDatabase;
@@ -21,6 +21,24 @@ impl<'a, 'p> AnyMod<'a, 'p> {
2121
}
2222
}
2323

24+
impl<'a, 'p> From<NamedMod<'a, 'p>> for AnyMod<'a, 'p> {
25+
fn from(value: NamedMod<'a, 'p>) -> Self {
26+
Self::Named(value)
27+
}
28+
}
29+
30+
impl<'a, 'p> From<OffsetMod<'a>> for AnyMod<'a, 'p> {
31+
fn from(value: OffsetMod<'a>) -> Self {
32+
Self::Offset(value)
33+
}
34+
}
35+
36+
impl<'a, 'p, K: Into<AnyMod<'a, 'p>>> From<K> for AnyModification<'a, 'p> {
37+
fn from(value: K) -> Self {
38+
Modification::new(1, value.into())
39+
}
40+
}
41+
2442
// NOTE: There are crates for automating more of this code generation, but odds are that I'll only need to do this sort
2543
// of enum dispatch for AnyMod — it doesn't seem worth a dependency and cluttering lib.rs with attributes
2644
macro_rules! dispatch {
@@ -48,26 +66,12 @@ impl Charged for AnyMod<'_, '_> {
4866
}
4967
}
5068

51-
impl Mz for AnyMod<'_, '_> {}
52-
53-
impl<'a, 'p> From<NamedMod<'a, 'p>> for AnyMod<'a, 'p> {
54-
fn from(value: NamedMod<'a, 'p>) -> Self {
55-
Self::Named(value)
56-
}
57-
}
58-
59-
impl<'a, 'p> From<OffsetMod<'a>> for AnyMod<'a, 'p> {
60-
fn from(value: OffsetMod<'a>) -> Self {
61-
Self::Offset(value)
62-
}
63-
}
64-
6569
#[cfg(test)]
6670
mod tests {
6771
use once_cell::sync::Lazy;
6872
use rust_decimal_macros::dec;
6973

70-
use crate::testing_tools::assert_miette_snapshot;
74+
use crate::{testing_tools::assert_miette_snapshot, Mz};
7175

7276
use super::*;
7377

@@ -94,6 +98,43 @@ mod tests {
9498
assert_miette_snapshot!(water_lost);
9599
}
96100

101+
#[test]
102+
fn from_impls() {
103+
let named_mod = NamedMod::new(&POLYMER_DB, "Am").unwrap();
104+
let named_any_mod: AnyMod = named_mod.into();
105+
let named_any_modification: AnyModification = named_mod.into();
106+
let named_any_any_modification: AnyModification = named_any_mod.clone().into();
107+
assert_eq!(
108+
named_mod.monoisotopic_mass(),
109+
named_any_mod.monoisotopic_mass()
110+
);
111+
assert_eq!(
112+
named_mod.monoisotopic_mass(),
113+
named_any_modification.monoisotopic_mass()
114+
);
115+
assert_eq!(
116+
named_mod.monoisotopic_mass(),
117+
named_any_any_modification.monoisotopic_mass()
118+
);
119+
120+
let offset_mod = OffsetMod::new(&ATOMIC_DB, OffsetKind::Add, "H2O").unwrap();
121+
let offset_any_mod: AnyMod = offset_mod.clone().into();
122+
let offset_any_modification: AnyModification = offset_mod.clone().into();
123+
let offset_any_any_modification: AnyModification = offset_any_mod.clone().into();
124+
assert_eq!(
125+
offset_mod.monoisotopic_mass(),
126+
offset_any_mod.monoisotopic_mass()
127+
);
128+
assert_eq!(
129+
offset_mod.monoisotopic_mass(),
130+
offset_any_modification.monoisotopic_mass()
131+
);
132+
assert_eq!(
133+
offset_mod.monoisotopic_mass(),
134+
offset_any_any_modification.monoisotopic_mass()
135+
);
136+
}
137+
97138
#[test]
98139
fn monoisotopic_mass() {
99140
// Masses checked against https://www.unimod.org/modifications_list.php

crates/polychem/src/polymers/bond.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rust_decimal::Decimal;
22

3-
use crate::{Bond, BondTarget, Charge, Charged, Massive, Mz, PolychemError, Result};
3+
use crate::{Bond, BondTarget, Charge, Charged, Massive, PolychemError, Result};
44

55
use super::polymer_database::{BondDescription, PolymerDatabase};
66

@@ -45,14 +45,12 @@ impl Charged for Bond<'_, '_> {
4545
}
4646
}
4747

48-
impl Mz for Bond<'_, '_> {}
49-
5048
#[cfg(test)]
5149
mod tests {
5250
use once_cell::sync::Lazy;
5351
use rust_decimal_macros::dec;
5452

55-
use crate::{testing_tools::assert_miette_snapshot, AtomicDatabase, FunctionalGroup};
53+
use crate::{testing_tools::assert_miette_snapshot, AtomicDatabase, FunctionalGroup, Mz};
5654

5755
use super::*;
5856

crates/polychem/src/polymers/modification.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rust_decimal::prelude::Decimal;
22

3-
use crate::{Charge, Charged, Count, Massive, Modification, Mz, SignedCount};
3+
use crate::{Charge, Charged, Count, Massive, Modification};
44

55
impl<K> Modification<K> {
66
// FIXME: Maybe make this `new_with_multiplier`, and make `new(k)` = `new_with_multiplier(1, k)` — depends on if
@@ -30,20 +30,18 @@ impl<K: Massive> Massive for Modification<K> {
3030

3131
impl<K: Charged> Charged for Modification<K> {
3232
fn charge(&self) -> Charge {
33-
SignedCount::from(self.multiplier) * self.kind.charge()
33+
Charge::from(self.multiplier) * self.kind.charge()
3434
}
3535
}
3636

37-
impl<K: Mz> Mz for Modification<K> {}
38-
3937
#[cfg(test)]
4038
mod tests {
4139
use once_cell::sync::Lazy;
4240
use rust_decimal_macros::dec;
4341

4442
use crate::{
4543
atoms::atomic_database::AtomicDatabase, polymers::polymer_database::PolymerDatabase,
46-
AnyMod, NamedMod, OffsetKind, OffsetMod,
44+
AnyMod, Mz, NamedMod, OffsetKind, OffsetMod,
4745
};
4846

4947
use super::*;

crates/polychem/src/polymers/named_mod.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rust_decimal::Decimal;
22

3-
use crate::{Charge, Charged, Massive, Mz, NamedMod, PolychemError, Result};
3+
use crate::{Charge, Charged, Massive, Modification, NamedMod, PolychemError, Result};
44

55
use super::polymer_database::{ModificationDescription, PolymerDatabase};
66

@@ -36,6 +36,12 @@ impl<'a, 'p> NamedMod<'a, 'p> {
3636
}
3737
}
3838

39+
impl<'a, 'p> From<NamedMod<'a, 'p>> for Modification<NamedMod<'a, 'p>> {
40+
fn from(value: NamedMod<'a, 'p>) -> Self {
41+
Modification::new(1, value)
42+
}
43+
}
44+
3945
impl Massive for NamedMod<'_, '_> {
4046
fn monoisotopic_mass(&self) -> Decimal {
4147
self.gained.monoisotopic_mass() - self.lost.monoisotopic_mass()
@@ -52,14 +58,12 @@ impl Charged for NamedMod<'_, '_> {
5258
}
5359
}
5460

55-
impl Mz for NamedMod<'_, '_> {}
56-
5761
#[cfg(test)]
5862
mod tests {
5963
use once_cell::sync::Lazy;
6064
use rust_decimal_macros::dec;
6165

62-
use crate::{testing_tools::assert_miette_snapshot, AtomicDatabase};
66+
use crate::{testing_tools::assert_miette_snapshot, AtomicDatabase, Mz};
6367

6468
use super::*;
6569

@@ -82,6 +86,16 @@ mod tests {
8286
assert_miette_snapshot!(potassium);
8387
}
8488

89+
#[test]
90+
fn from_impls() {
91+
let named_mod = NamedMod::new(&POLYMER_DB, "Am").unwrap();
92+
let named_modification: Modification<NamedMod> = named_mod.into();
93+
assert_eq!(
94+
named_mod.monoisotopic_mass(),
95+
named_modification.monoisotopic_mass()
96+
);
97+
}
98+
8599
#[test]
86100
fn monoisotopic_mass() {
87101
// Masses checked against https://www.unimod.org/modifications_list.php

0 commit comments

Comments
 (0)