Skip to main content

mzdata_param/
param.rs

1use std::borrow::Cow;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::str::{self, FromStr};
5
6
7use crate::{
8    AccessionIntCode, CURIE, ControlledVocabulary, ParamCow, ParamLike, ParamValue, ParamValueParseError, Unit, Value, ValueRef
9};
10
11
12/// A controlled vocabulary or user parameter
13#[derive(Debug, Clone, Default, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct Param {
16    pub name: String,
17    pub value: Value,
18    pub accession: Option<AccessionIntCode>,
19    pub controlled_vocabulary: Option<ControlledVocabulary>,
20    pub unit: Unit,
21}
22
23impl AsRef<Value> for Param {
24    fn as_ref(&self) -> &Value {
25        &self.value
26    }
27}
28
29impl ParamValue for Param {
30    fn is_empty(&self) -> bool {
31        <Value as ParamValue>::is_empty(&self.value)
32    }
33
34    fn is_i64(&self) -> bool {
35        <Value as ParamValue>::is_i64(&self.value)
36    }
37
38    fn is_f64(&self) -> bool {
39        <Value as ParamValue>::is_f64(&self.value)
40    }
41
42    fn is_buffer(&self) -> bool {
43        <Value as ParamValue>::is_buffer(&self.value)
44    }
45
46    fn is_str(&self) -> bool {
47        <Value as ParamValue>::is_str(&self.value)
48    }
49
50    fn to_f64(&self) -> Result<f64, ParamValueParseError> {
51        <Value as ParamValue>::to_f64(&self.value)
52    }
53
54    fn to_i64(&self) -> Result<i64, ParamValueParseError> {
55        <Value as ParamValue>::to_i64(&self.value)
56    }
57
58    fn to_str(&self) -> Cow<'_, str> {
59        <Value as ParamValue>::to_str(&self.value)
60    }
61
62    fn to_buffer(&self) -> Result<Cow<'_, [u8]>, ParamValueParseError> {
63        <Value as ParamValue>::to_buffer(&self.value)
64    }
65
66    fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
67        <Value as ParamValue>::parse(&self.value)
68    }
69
70    fn as_bytes(&self) -> Cow<'_, [u8]> {
71        <Value as ParamValue>::as_bytes(&self.value)
72    }
73
74    fn as_ref(&self) -> ValueRef<'_> {
75        <Value as ParamValue>::as_ref(&self.value)
76    }
77
78    fn data_len(&self) -> usize {
79        <Value as ParamValue>::data_len(&self.value)
80    }
81
82    fn is_boolean(&self) -> bool {
83        <Value as ParamValue>::is_boolean(&self.value)
84    }
85
86    fn to_bool(&self) -> Result<bool, ParamValueParseError> {
87        <Value as ParamValue>::to_bool(&self.value)
88    }
89
90    fn is_list(&self) -> bool {
91        <Value as ParamValue>::is_list(&self.value)
92    }
93
94    fn as_slice(&self) -> Cow<'_, [Value]> {
95        <Value as ParamValue>::as_slice(&self.value)
96    }
97}
98
99impl Display for Param {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        let mut body = if self.is_controlled() {
102            format!(
103                "{}:{}|{}={}",
104                String::from_utf8_lossy(self.controlled_vocabulary.unwrap().as_bytes()),
105                self.accession.unwrap(),
106                self.name,
107                self.value
108            )
109        } else {
110            format!("{}={}", self.name, self.value)
111        };
112        if self.unit != Unit::Unknown {
113            body.extend(format!(" {}", self.unit).chars());
114        };
115        f.write_str(body.as_str())
116    }
117}
118
119/// Incrementally build up a [`Param`]
120#[derive(Default, Debug, Clone)]
121pub struct ParamBuilder {
122    name: String,
123    value: Value,
124    accession: Option<AccessionIntCode>,
125    controlled_vocabulary: Option<ControlledVocabulary>,
126    unit: Unit,
127}
128
129impl ParamBuilder {
130    pub fn name<S: ToString>(mut self, name: S) -> Self {
131        self.name = name.to_string();
132        self
133    }
134
135    pub fn value<V: Into<Value>>(mut self, value: V) -> Self {
136        self.value = value.into();
137        self
138    }
139
140    pub fn controlled_vocabulary(mut self, cv: ControlledVocabulary) -> Self {
141        self.controlled_vocabulary = Some(cv);
142        self
143    }
144
145    pub fn accession(mut self, accession: AccessionIntCode) -> Self {
146        self.accession = Some(accession);
147        self
148    }
149
150    /// A convenience method that configures the controlled vocabulary and accession number
151    /// from a [`CURIE`]
152    pub fn curie(mut self, curie: CURIE) -> Self {
153        self.controlled_vocabulary = Some(curie.controlled_vocabulary);
154        self.accession = Some(curie.accession);
155        self
156    }
157
158    pub fn unit(mut self, unit: Unit) -> Self {
159        self.unit = unit;
160        self
161    }
162
163    /// Consume the builder to produce a [`Param`]
164    pub fn build(self) -> Param {
165        let mut this = Param::new();
166        this.name = self.name;
167        this.value = self.value;
168        this.controlled_vocabulary = self.controlled_vocabulary;
169        this.accession = self.accession;
170        this.unit = self.unit;
171        this
172    }
173}
174
175impl Param {
176    /// Create a new, empty [`Param`].
177    ///
178    /// # See also
179    /// - [`Param::new_key_value`]
180    /// - [`Param::builder`]
181    /// - [`ControlledVocabulary::param`]
182    /// - [`ControlledVocabulary::param_val`]
183    pub fn new() -> Param {
184        Param {
185            ..Default::default()
186        }
187    }
188
189    /// Create a new [`ParamBuilder`] to make creating a new [`Param`] more convenient.
190    pub fn builder() -> ParamBuilder {
191        ParamBuilder::default()
192    }
193
194    /// A construction method for [`Param`] that sets [`Param::name`] and [`Param::value`]
195    /// but leaves all other attributes as default.
196    pub fn new_key_value<K: Into<String>, V: Into<Value>>(name: K, value: V) -> Param {
197        let mut inst = Self::new();
198        inst.name = name.into();
199        inst.value = value.into();
200        inst
201    }
202
203    /// Attempt to parse the value of this parameter into `T`.
204    ///
205    /// See [`Value::parse`]
206    pub fn parse<T: str::FromStr>(&self) -> Result<T, T::Err> {
207        self.value.parse::<T>()
208    }
209
210    /// Check if this parameter is defined in a controlled vocabulary
211    pub const fn is_controlled(&self) -> bool {
212        self.accession.is_some()
213    }
214
215    /// Create a [`CURIE`] from [`Param::controlled_vocabulary`] and [`Param::accession`]
216    pub const fn curie(&self) -> Option<CURIE> {
217        match (self.controlled_vocabulary, self.accession) {
218            (Some(cv), Some(acc)) => Some(CURIE::new(cv, acc)),
219            _ => None,
220        }
221    }
222
223    /// Format the [`Param::curie`] as a string, if it exists
224    pub fn curie_str(&self) -> Option<String> {
225        self.curie().map(|c| c.to_string())
226    }
227
228    /// Update [`Param::unit`] inferred from `accession`, failing that, `name`
229    pub fn with_unit<S: AsRef<str>, A: AsRef<str>>(mut self, accession: S, name: A) -> Param {
230        self.unit = Unit::from_accession(accession.as_ref());
231        if matches!(self.unit, Unit::Unknown) {
232            self.unit = Unit::from_name(name.as_ref());
233        }
234        self
235    }
236
237    /// Update [`Param::unit`] from `unit`
238    pub fn with_unit_t(mut self, unit: &Unit) -> Param {
239        self.unit = *unit;
240        self
241    }
242}
243
244impl ParamLike for Param {
245    fn name(&self) -> &str {
246        &self.name
247    }
248
249    fn value(&self) -> ValueRef<'_> {
250        self.value.as_ref()
251    }
252
253    fn accession(&self) -> Option<AccessionIntCode> {
254        self.accession
255    }
256
257    fn controlled_vocabulary(&self) -> Option<ControlledVocabulary> {
258        self.controlled_vocabulary
259    }
260
261    fn unit(&self) -> Unit {
262        self.unit
263    }
264}
265
266impl PartialEq<CURIE> for Param {
267    fn eq(&self, other: &CURIE) -> bool {
268        other.eq(self)
269    }
270}
271
272impl<'a> PartialEq<ParamCow<'a>> for Param {
273    fn eq(&self, other: &ParamCow<'a>) -> bool {
274        self.controlled_vocabulary == other.controlled_vocabulary
275            && self.accession == other.accession
276            && self.name == other.name
277            && self.value == other.value
278            && self.unit == other.unit
279    }
280}
281
282impl PartialEq<Param> for ParamCow<'_> {
283    fn eq(&self, other: &Param) -> bool {
284        self.controlled_vocabulary == other.controlled_vocabulary
285            && self.accession == other.accession
286            && self.name == other.name
287            && self.value == other.value
288            && self.unit == other.unit
289    }
290}
291
292impl Hash for Param {
293    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
294        self.name.hash(state);
295        self.value.hash(state);
296        self.accession.hash(state);
297        self.controlled_vocabulary.hash(state);
298        self.unit.hash(state);
299    }
300}