phenopacket_builder/v2/core/
base.rs

1use crate::{Build, Buildable, Set, Unset};
2use phenopackets::schema::v2::core::time_element::Element;
3use phenopackets::schema::v2::core::{
4    Age, ExternalReference, GestationalAge, OntologyClass, TimeElement,
5};
6use std::marker::PhantomData;
7
8#[derive(Debug, Default, Clone, PartialEq)]
9pub struct OntologyClassBuilder<T = Unset> {
10    id: Option<String>,
11    label: Option<String>,
12    data: PhantomData<T>,
13}
14
15impl OntologyClassBuilder<Unset> {
16    pub fn id_label(
17        self,
18        id: impl Into<String>,
19        label: impl Into<String>,
20    ) -> OntologyClassBuilder<Set> {
21        OntologyClassBuilder {
22            id: Some(id.into()),
23            label: Some(label.into()),
24            data: PhantomData,
25        }
26    }
27}
28
29impl Buildable for OntologyClass {
30    type Builder = OntologyClassBuilder;
31}
32
33impl Build<OntologyClass> for OntologyClassBuilder<Set> {
34    fn build(self) -> OntologyClass {
35        OntologyClass {
36            id: self.id.expect("id must have been set"),
37            label: self.label.expect("label must have been set"),
38        }
39    }
40}
41
42#[derive(Debug, Default, Clone, PartialEq)]
43pub struct TimeElementBuilder<T = Unset> {
44    element: Option<Element>,
45    data: PhantomData<T>,
46}
47
48impl<T> TimeElementBuilder<T> {
49    /// Set the gestational age.
50    ///
51    /// # Examples
52    ///
53    /// Create the gestational age, for instance corresponding to 25 weeks and 5 days:
54    ///
55    /// ```
56    /// use phenopacket_builder::{Build, Buildable};
57    /// use phenopackets::schema::v2::core::{GestationalAge, TimeElement};
58    /// use phenopackets::schema::v2::core::time_element::Element;
59    ///
60    /// let te: TimeElement = TimeElement::builder()
61    ///                         .gestational_age(GestationalAge::builder()
62    ///                             .weeks(25)
63    ///                             .days(5)
64    ///                         ).build();
65    ///
66    /// assert_eq!(
67    ///     te.element,
68    ///     Some(Element::GestationalAge(
69    ///         GestationalAge {
70    ///             weeks: 25,
71    ///             days: 5,
72    ///         }
73    ///     ))
74    /// );
75    pub fn gestational_age(
76        self,
77        gestational_age: impl Build<GestationalAge>,
78    ) -> TimeElementBuilder<Set> {
79        TimeElementBuilder {
80            element: Some(Element::GestationalAge(gestational_age.build())),
81            data: PhantomData,
82        }
83    }
84
85    pub fn gestational_age_weeks(self, weeks: impl Into<i32>) -> TimeElementBuilder<Set> {
86        TimeElementBuilder {
87            element: Some(Element::GestationalAge(GestationalAge {
88                weeks: weeks.into(),
89                days: Default::default(),
90            })),
91            data: PhantomData,
92        }
93    }
94
95    pub fn gestational_age_weeks_days(
96        self,
97        weeks: impl Into<i32>,
98        days: impl Into<i32>,
99    ) -> TimeElementBuilder<Set> {
100        TimeElementBuilder {
101            element: Some(Element::GestationalAge(GestationalAge {
102                weeks: weeks.into(),
103                days: days.into(),
104            })),
105            data: PhantomData,
106        }
107    }
108
109    /// Set the age.
110    ///
111    /// # Examples
112    ///
113    /// Use ISO8601 duration, such as `P1Y2D`:
114    ///
115    /// ```
116    /// use phenopacket_builder::{Build, Buildable};
117    /// use phenopackets::schema::v2::core::{Age, TimeElement};
118    /// use phenopackets::schema::v2::core::time_element::Element;
119    ///
120    /// let te: TimeElement = TimeElement::builder()
121    ///                         .age(Age::builder()
122    ///                             .iso8601duration("P1Y2D")
123    ///                         ).build();
124    ///
125    /// assert_eq!(
126    ///     te.element,
127    ///     Some(Element::Age(
128    ///         Age {
129    ///             iso8601duration: "P1Y2D".to_string()
130    ///         }
131    ///     ))
132    /// );
133    /// ```
134    pub fn age(self, age: impl Build<Age>) -> TimeElementBuilder<Set> {
135        TimeElementBuilder {
136            element: Some(Element::Age(age.build())),
137            data: PhantomData,
138        }
139    }
140
141    /// Set the age with a corresponding ISO8601 duration.
142    ///
143    /// # Examples
144    ///
145    /// Create age corresponding to 1 year and 2 days:
146    ///
147    /// ```
148    /// use phenopacket_builder::{Build, Buildable};
149    /// use phenopackets::schema::v2::core::{Age, TimeElement};
150    /// use phenopackets::schema::v2::core::time_element::Element;
151    ///
152    /// let te: TimeElement = TimeElement::builder()
153    ///                         .age_iso8601duration("P1Y2D")
154    ///                         .build();
155    ///
156    /// assert_eq!(
157    ///     te.element,
158    ///     Some(Element::Age(
159    ///         Age {
160    ///             iso8601duration: "P1Y2D".to_string()
161    ///         }
162    ///     ))
163    /// );
164    /// ```
165    pub fn age_iso8601duration(
166        self,
167        iso8601duration: impl Into<String>,
168    ) -> TimeElementBuilder<Set> {
169        TimeElementBuilder {
170            element: Some(Element::Age(Age {
171                iso8601duration: iso8601duration.into(),
172            })),
173            data: PhantomData,
174        }
175    }
176
177    pub fn ontology_class(self, oc: impl Build<OntologyClass>) -> TimeElementBuilder<Set> {
178        TimeElementBuilder {
179            element: Some(Element::OntologyClass(oc.build())),
180            data: PhantomData,
181        }
182    }
183
184    pub fn timestamp(self, ts: impl Build<prost_types::Timestamp>) -> TimeElementBuilder<Set> {
185        TimeElementBuilder {
186            element: Some(Element::Timestamp(ts.build())),
187            data: PhantomData,
188        }
189    }
190
191    // TODO: add support for timestamp and interval
192}
193
194impl Buildable for TimeElement {
195    type Builder = TimeElementBuilder;
196}
197
198impl Build<TimeElement> for TimeElementBuilder<Set> {
199    fn build(self) -> TimeElement {
200        self.element
201            .map(|e| TimeElement { element: Some(e) })
202            .expect("element must have been set")
203    }
204}
205
206#[derive(Debug, Default, Clone, PartialEq)]
207pub struct AgeBuilder<T = Unset> {
208    iso8601duration: Option<String>,
209    data: PhantomData<T>,
210}
211
212impl Buildable for Age {
213    type Builder = AgeBuilder;
214}
215
216impl AgeBuilder<Unset> {
217    /// Set the age value as ISO8601 duration.
218    ///
219    /// Note: the string is *not* checked to constitute a valid ISO8601 duration.
220    ///
221    /// # Example
222    ///
223    /// ```
224    /// use phenopacket_builder::{Build, Buildable};
225    /// use phenopackets::schema::v2::core::Age;
226    ///
227    /// let age: Age = Age::builder()
228    ///                 .iso8601duration("P1Y2M4D")
229    ///                 .build();
230    ///
231    /// assert_eq!(
232    ///     age,
233    ///     Age {
234    ///         iso8601duration: "P1Y2M4D".to_string(),
235    ///     }
236    /// )
237    /// ```
238    pub fn iso8601duration(self, iso8601duration: impl Into<String>) -> AgeBuilder<Set> {
239        AgeBuilder {
240            iso8601duration: Some(iso8601duration.into()),
241            data: PhantomData,
242        }
243    }
244}
245
246impl Build<Age> for AgeBuilder<Set> {
247    fn build(self) -> Age {
248        Age {
249            iso8601duration: self
250                .iso8601duration
251                .expect("iso8601duration must have been set"),
252        }
253    }
254}
255
256#[derive(Debug, Default, Clone, PartialEq)]
257pub struct GestationalAgeBuilder<T = Unset> {
258    weeks: Option<i32>,
259    days: Option<i32>,
260    data: PhantomData<T>,
261}
262
263impl GestationalAgeBuilder<Unset> {
264    pub fn weeks(self, weeks: impl Into<i32>) -> GestationalAgeBuilder<Set> {
265        GestationalAgeBuilder {
266            weeks: Some(weeks.into()),
267            days: self.days,
268            data: PhantomData,
269        }
270    }
271}
272
273impl<T> GestationalAgeBuilder<T> {
274    pub fn days(mut self, days: impl Into<i32>) -> GestationalAgeBuilder<T> {
275        self.days = Some(days.into());
276        self
277    }
278}
279
280impl Buildable for GestationalAge {
281    type Builder = GestationalAgeBuilder;
282}
283
284impl Build<GestationalAge> for GestationalAgeBuilder<Set> {
285    fn build(self) -> GestationalAge {
286        GestationalAge {
287            weeks: self.weeks.expect("weeks must have been set"),
288            days: self.days.unwrap_or(0),
289        }
290    }
291}
292
293#[derive(Debug, Default, Clone, PartialEq)]
294pub struct ExternalReferenceBuilder {
295    id: Option<String>,
296    reference: Option<String>,
297    description: Option<String>,
298}
299
300impl ExternalReferenceBuilder {
301    pub fn id(mut self, id: impl Into<String>) -> ExternalReferenceBuilder {
302        self.id = Some(id.into());
303        self
304    }
305
306    pub fn reference(mut self, reference: impl Into<String>) -> ExternalReferenceBuilder {
307        self.reference = Some(reference.into());
308        self
309    }
310
311    pub fn description(mut self, description: impl Into<String>) -> ExternalReferenceBuilder {
312        self.description = Some(description.into());
313        self
314    }
315}
316
317impl Buildable for ExternalReference {
318    type Builder = ExternalReferenceBuilder;
319}
320
321impl Build<ExternalReference> for ExternalReferenceBuilder {
322    fn build(self) -> ExternalReference {
323        ExternalReference {
324            id: self.id.unwrap_or_default(),
325            reference: self.reference.unwrap_or_default(),
326            description: self.description.unwrap_or_default(),
327        }
328    }
329}