Skip to main content

sbol/client/
om.rs

1//! Owned typed structs for the OM (Ontology of units of Measure) classes
2//! adopted by SBOL 3 (Appendix A.2).
3//!
4//! `Measure` is a bare Identified attached to other SBOL objects through
5//! `sbol:hasMeasure`. The `Unit` hierarchy (`SingularUnit`,
6//! `CompoundUnit`, `UnitDivision`, `UnitExponentiation`,
7//! `UnitMultiplication`, `PrefixedUnit`) and the `Prefix` hierarchy
8//! (`SIPrefix`, `BinaryPrefix`) are TopLevels carrying labels, symbols,
9//! and structural references that the descriptor-driven serializer
10//! emits through the shared `Emitter`.
11
12use crate::client::accessors::{impl_sbol_identified, impl_sbol_top_level};
13use crate::client::builder::{
14    BinaryPrefixBuilder, CompoundUnitBuilder, MeasureBuilder, PrefixBuilder, PrefixedUnitBuilder,
15    SIPrefixBuilder, SingularUnitBuilder, UnitBuilder, UnitDivisionBuilder,
16    UnitExponentiationBuilder, UnitMultiplicationBuilder,
17};
18use crate::client::shared::{iris, literals};
19use crate::client::to_rdf::{Emitter, emit_identified, emit_top_level, seed_triples};
20use crate::client::{IdentifiedData, ToRdf, TopLevelData, TryFromObject};
21use crate::error::BuildError;
22use crate::identity::{DisplayId, Namespace};
23use crate::vocab::*;
24use crate::{Iri, Object, Resource, SbolClass, Triple};
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27#[non_exhaustive]
28pub struct Measure {
29    pub identity: Resource,
30    pub identified: IdentifiedData,
31    pub types: Vec<Iri>,
32    pub has_unit: Option<Resource>,
33    pub has_numerical_value: Option<String>,
34}
35
36impl Measure {
37    pub fn new(
38        parent: &Resource,
39        display_id: impl TryInto<DisplayId, Error = BuildError>,
40        has_unit: Resource,
41        has_numerical_value: f64,
42    ) -> Result<Self, BuildError> {
43        Self::builder(parent, display_id)?
44            .has_unit(has_unit)
45            .has_numerical_value(has_numerical_value)
46            .build()
47    }
48
49    pub fn builder(
50        parent: &Resource,
51        display_id: impl TryInto<DisplayId, Error = BuildError>,
52    ) -> Result<MeasureBuilder, BuildError> {
53        MeasureBuilder::seed(parent, display_id.try_into()?)
54    }
55}
56
57impl ToRdf for Measure {
58    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
59        let mut triples = seed_triples(&self.identity, SbolClass::OmMeasure);
60        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmMeasure);
61        emit_identified(&mut e, &self.identified)?;
62        e.iris(SBOL_TYPE, &self.types)?;
63        e.resource(OM_HAS_UNIT, self.has_unit.as_ref())?;
64        e.literal(OM_HAS_NUMERICAL_VALUE, self.has_numerical_value.as_deref())?;
65        drop(e);
66        Ok(triples)
67    }
68}
69
70impl TryFromObject for Measure {
71    fn try_from_object(object: &Object) -> Option<Self> {
72        Some(Self {
73            identity: object.identity().clone(),
74            identified: IdentifiedData::from_object(object),
75            types: iris(object, SBOL_TYPE),
76            has_unit: object.first_resource(OM_HAS_UNIT).cloned(),
77            has_numerical_value: object
78                .first_literal_value(OM_HAS_NUMERICAL_VALUE)
79                .map(ToOwned::to_owned),
80        })
81    }
82}
83
84/// Shared OM Unit fields. Carried by every concrete Unit subclass so
85/// the label/symbol/comment serializers can be reused.
86#[derive(Clone, Debug, Default, PartialEq, Eq)]
87#[non_exhaustive]
88pub struct UnitData {
89    pub label: Option<String>,
90    pub symbol: Option<String>,
91    pub alternative_labels: Vec<String>,
92    pub alternative_symbols: Vec<String>,
93    pub comment: Option<String>,
94    pub long_comment: Option<String>,
95}
96
97impl UnitData {
98    pub(crate) fn from_object(object: &Object) -> Self {
99        Self {
100            label: object.first_literal_value(OM_LABEL).map(ToOwned::to_owned),
101            symbol: object.first_literal_value(OM_SYMBOL).map(ToOwned::to_owned),
102            alternative_labels: literals(object, OM_ALTERNATIVE_LABEL),
103            alternative_symbols: literals(object, OM_ALTERNATIVE_SYMBOL),
104            comment: object
105                .first_literal_value(OM_COMMENT)
106                .map(ToOwned::to_owned),
107            long_comment: object
108                .first_literal_value(OM_LONG_COMMENT)
109                .map(ToOwned::to_owned),
110        }
111    }
112}
113
114pub(crate) fn emit_unit_fields(e: &mut Emitter<'_>, data: &UnitData) -> Result<(), BuildError> {
115    e.literal(OM_LABEL, data.label.as_deref())?;
116    e.literal(OM_SYMBOL, data.symbol.as_deref())?;
117    e.literals(OM_ALTERNATIVE_LABEL, &data.alternative_labels)?;
118    e.literals(OM_ALTERNATIVE_SYMBOL, &data.alternative_symbols)?;
119    e.literal(OM_COMMENT, data.comment.as_deref())?;
120    e.literal(OM_LONG_COMMENT, data.long_comment.as_deref())?;
121    Ok(())
122}
123
124#[derive(Clone, Debug, PartialEq, Eq)]
125#[non_exhaustive]
126pub struct Unit {
127    pub identity: Resource,
128    pub identified: IdentifiedData,
129    pub top_level: TopLevelData,
130    pub unit: UnitData,
131}
132
133impl Unit {
134    pub fn new(
135        namespace: impl TryInto<Namespace, Error = BuildError>,
136        display_id: impl TryInto<DisplayId, Error = BuildError>,
137        label: impl Into<String>,
138        symbol: impl Into<String>,
139    ) -> Result<Self, BuildError> {
140        Self::builder(namespace, display_id)?
141            .label(label)
142            .symbol(symbol)
143            .build()
144    }
145
146    pub fn builder(
147        namespace: impl TryInto<Namespace, Error = BuildError>,
148        display_id: impl TryInto<DisplayId, Error = BuildError>,
149    ) -> Result<UnitBuilder, BuildError> {
150        Ok(UnitBuilder::seed(
151            namespace.try_into()?,
152            display_id.try_into()?,
153        ))
154    }
155}
156
157impl ToRdf for Unit {
158    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
159        let mut triples = seed_triples(&self.identity, SbolClass::OmUnit);
160        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmUnit);
161        emit_identified(&mut e, &self.identified)?;
162        emit_top_level(&mut e, &self.top_level)?;
163        emit_unit_fields(&mut e, &self.unit)?;
164        drop(e);
165        Ok(triples)
166    }
167}
168
169impl TryFromObject for Unit {
170    fn try_from_object(object: &Object) -> Option<Self> {
171        Some(Self {
172            identity: object.identity().clone(),
173            identified: IdentifiedData::from_object(object),
174            top_level: TopLevelData::from_object(object),
175            unit: UnitData::from_object(object),
176        })
177    }
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
181#[non_exhaustive]
182pub struct SingularUnit {
183    pub identity: Resource,
184    pub identified: IdentifiedData,
185    pub top_level: TopLevelData,
186    pub unit: UnitData,
187    pub has_unit: Option<Resource>,
188    pub has_factor: Option<String>,
189}
190
191impl SingularUnit {
192    pub fn new(
193        namespace: impl TryInto<Namespace, Error = BuildError>,
194        display_id: impl TryInto<DisplayId, Error = BuildError>,
195        label: impl Into<String>,
196        symbol: impl Into<String>,
197    ) -> Result<Self, BuildError> {
198        Self::builder(namespace, display_id)?
199            .label(label)
200            .symbol(symbol)
201            .build()
202    }
203
204    pub fn builder(
205        namespace: impl TryInto<Namespace, Error = BuildError>,
206        display_id: impl TryInto<DisplayId, Error = BuildError>,
207    ) -> Result<SingularUnitBuilder, BuildError> {
208        Ok(SingularUnitBuilder::seed(
209            namespace.try_into()?,
210            display_id.try_into()?,
211        ))
212    }
213}
214
215impl ToRdf for SingularUnit {
216    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
217        let mut triples = seed_triples(&self.identity, SbolClass::OmSingularUnit);
218        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmSingularUnit);
219        emit_identified(&mut e, &self.identified)?;
220        emit_top_level(&mut e, &self.top_level)?;
221        emit_unit_fields(&mut e, &self.unit)?;
222        e.resource(OM_HAS_UNIT, self.has_unit.as_ref())?;
223        e.literal(OM_HAS_FACTOR, self.has_factor.as_deref())?;
224        drop(e);
225        Ok(triples)
226    }
227}
228
229impl TryFromObject for SingularUnit {
230    fn try_from_object(object: &Object) -> Option<Self> {
231        Some(Self {
232            identity: object.identity().clone(),
233            identified: IdentifiedData::from_object(object),
234            top_level: TopLevelData::from_object(object),
235            unit: UnitData::from_object(object),
236            has_unit: object.first_resource(OM_HAS_UNIT).cloned(),
237            has_factor: object
238                .first_literal_value(OM_HAS_FACTOR)
239                .map(ToOwned::to_owned),
240        })
241    }
242}
243
244#[derive(Clone, Debug, PartialEq, Eq)]
245#[non_exhaustive]
246pub struct CompoundUnit {
247    pub identity: Resource,
248    pub identified: IdentifiedData,
249    pub top_level: TopLevelData,
250    pub unit: UnitData,
251}
252
253impl CompoundUnit {
254    pub fn new(
255        namespace: impl TryInto<Namespace, Error = BuildError>,
256        display_id: impl TryInto<DisplayId, Error = BuildError>,
257        label: impl Into<String>,
258        symbol: impl Into<String>,
259    ) -> Result<Self, BuildError> {
260        Self::builder(namespace, display_id)?
261            .label(label)
262            .symbol(symbol)
263            .build()
264    }
265
266    pub fn builder(
267        namespace: impl TryInto<Namespace, Error = BuildError>,
268        display_id: impl TryInto<DisplayId, Error = BuildError>,
269    ) -> Result<CompoundUnitBuilder, BuildError> {
270        Ok(CompoundUnitBuilder::seed(
271            namespace.try_into()?,
272            display_id.try_into()?,
273        ))
274    }
275}
276
277impl ToRdf for CompoundUnit {
278    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
279        let mut triples = seed_triples(&self.identity, SbolClass::OmCompoundUnit);
280        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmCompoundUnit);
281        emit_identified(&mut e, &self.identified)?;
282        emit_top_level(&mut e, &self.top_level)?;
283        emit_unit_fields(&mut e, &self.unit)?;
284        drop(e);
285        Ok(triples)
286    }
287}
288
289impl TryFromObject for CompoundUnit {
290    fn try_from_object(object: &Object) -> Option<Self> {
291        Some(Self {
292            identity: object.identity().clone(),
293            identified: IdentifiedData::from_object(object),
294            top_level: TopLevelData::from_object(object),
295            unit: UnitData::from_object(object),
296        })
297    }
298}
299
300#[derive(Clone, Debug, PartialEq, Eq)]
301#[non_exhaustive]
302pub struct UnitDivision {
303    pub identity: Resource,
304    pub identified: IdentifiedData,
305    pub top_level: TopLevelData,
306    pub unit: UnitData,
307    pub has_numerator: Option<Resource>,
308    pub has_denominator: Option<Resource>,
309}
310
311impl UnitDivision {
312    pub fn new(
313        namespace: impl TryInto<Namespace, Error = BuildError>,
314        display_id: impl TryInto<DisplayId, Error = BuildError>,
315        label: impl Into<String>,
316        symbol: impl Into<String>,
317        has_numerator: Resource,
318        has_denominator: Resource,
319    ) -> Result<Self, BuildError> {
320        Self::builder(namespace, display_id)?
321            .label(label)
322            .symbol(symbol)
323            .has_numerator(has_numerator)
324            .has_denominator(has_denominator)
325            .build()
326    }
327
328    pub fn builder(
329        namespace: impl TryInto<Namespace, Error = BuildError>,
330        display_id: impl TryInto<DisplayId, Error = BuildError>,
331    ) -> Result<UnitDivisionBuilder, BuildError> {
332        Ok(UnitDivisionBuilder::seed(
333            namespace.try_into()?,
334            display_id.try_into()?,
335        ))
336    }
337}
338
339impl ToRdf for UnitDivision {
340    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
341        let mut triples = seed_triples(&self.identity, SbolClass::OmUnitDivision);
342        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmUnitDivision);
343        emit_identified(&mut e, &self.identified)?;
344        emit_top_level(&mut e, &self.top_level)?;
345        emit_unit_fields(&mut e, &self.unit)?;
346        e.resource(OM_HAS_NUMERATOR, self.has_numerator.as_ref())?;
347        e.resource(OM_HAS_DENOMINATOR, self.has_denominator.as_ref())?;
348        drop(e);
349        Ok(triples)
350    }
351}
352
353impl TryFromObject for UnitDivision {
354    fn try_from_object(object: &Object) -> Option<Self> {
355        Some(Self {
356            identity: object.identity().clone(),
357            identified: IdentifiedData::from_object(object),
358            top_level: TopLevelData::from_object(object),
359            unit: UnitData::from_object(object),
360            has_numerator: object.first_resource(OM_HAS_NUMERATOR).cloned(),
361            has_denominator: object.first_resource(OM_HAS_DENOMINATOR).cloned(),
362        })
363    }
364}
365
366#[derive(Clone, Debug, PartialEq, Eq)]
367#[non_exhaustive]
368pub struct UnitExponentiation {
369    pub identity: Resource,
370    pub identified: IdentifiedData,
371    pub top_level: TopLevelData,
372    pub unit: UnitData,
373    pub has_base: Option<Resource>,
374    pub has_exponent: Option<i64>,
375}
376
377impl UnitExponentiation {
378    pub fn new(
379        namespace: impl TryInto<Namespace, Error = BuildError>,
380        display_id: impl TryInto<DisplayId, Error = BuildError>,
381        label: impl Into<String>,
382        symbol: impl Into<String>,
383        has_base: Resource,
384        has_exponent: i64,
385    ) -> Result<Self, BuildError> {
386        Self::builder(namespace, display_id)?
387            .label(label)
388            .symbol(symbol)
389            .has_base(has_base)
390            .has_exponent(has_exponent)
391            .build()
392    }
393
394    pub fn builder(
395        namespace: impl TryInto<Namespace, Error = BuildError>,
396        display_id: impl TryInto<DisplayId, Error = BuildError>,
397    ) -> Result<UnitExponentiationBuilder, BuildError> {
398        Ok(UnitExponentiationBuilder::seed(
399            namespace.try_into()?,
400            display_id.try_into()?,
401        ))
402    }
403}
404
405impl ToRdf for UnitExponentiation {
406    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
407        let mut triples = seed_triples(&self.identity, SbolClass::OmUnitExponentiation);
408        let mut e = Emitter::new(
409            &mut triples,
410            &self.identity,
411            SbolClass::OmUnitExponentiation,
412        );
413        emit_identified(&mut e, &self.identified)?;
414        emit_top_level(&mut e, &self.top_level)?;
415        emit_unit_fields(&mut e, &self.unit)?;
416        e.resource(OM_HAS_BASE, self.has_base.as_ref())?;
417        e.i64(OM_HAS_EXPONENT, self.has_exponent)?;
418        drop(e);
419        Ok(triples)
420    }
421}
422
423impl TryFromObject for UnitExponentiation {
424    fn try_from_object(object: &Object) -> Option<Self> {
425        Some(Self {
426            identity: object.identity().clone(),
427            identified: IdentifiedData::from_object(object),
428            top_level: TopLevelData::from_object(object),
429            unit: UnitData::from_object(object),
430            has_base: object.first_resource(OM_HAS_BASE).cloned(),
431            has_exponent: object
432                .first_literal_value(OM_HAS_EXPONENT)
433                .and_then(|v| v.parse().ok()),
434        })
435    }
436}
437
438#[derive(Clone, Debug, PartialEq, Eq)]
439#[non_exhaustive]
440pub struct UnitMultiplication {
441    pub identity: Resource,
442    pub identified: IdentifiedData,
443    pub top_level: TopLevelData,
444    pub unit: UnitData,
445    pub has_term1: Option<Resource>,
446    pub has_term2: Option<Resource>,
447}
448
449impl UnitMultiplication {
450    pub fn new(
451        namespace: impl TryInto<Namespace, Error = BuildError>,
452        display_id: impl TryInto<DisplayId, Error = BuildError>,
453        label: impl Into<String>,
454        symbol: impl Into<String>,
455        has_term1: Resource,
456        has_term2: Resource,
457    ) -> Result<Self, BuildError> {
458        Self::builder(namespace, display_id)?
459            .label(label)
460            .symbol(symbol)
461            .has_term1(has_term1)
462            .has_term2(has_term2)
463            .build()
464    }
465
466    pub fn builder(
467        namespace: impl TryInto<Namespace, Error = BuildError>,
468        display_id: impl TryInto<DisplayId, Error = BuildError>,
469    ) -> Result<UnitMultiplicationBuilder, BuildError> {
470        Ok(UnitMultiplicationBuilder::seed(
471            namespace.try_into()?,
472            display_id.try_into()?,
473        ))
474    }
475}
476
477impl ToRdf for UnitMultiplication {
478    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
479        let mut triples = seed_triples(&self.identity, SbolClass::OmUnitMultiplication);
480        let mut e = Emitter::new(
481            &mut triples,
482            &self.identity,
483            SbolClass::OmUnitMultiplication,
484        );
485        emit_identified(&mut e, &self.identified)?;
486        emit_top_level(&mut e, &self.top_level)?;
487        emit_unit_fields(&mut e, &self.unit)?;
488        e.resource(OM_HAS_TERM1, self.has_term1.as_ref())?;
489        e.resource(OM_HAS_TERM2, self.has_term2.as_ref())?;
490        drop(e);
491        Ok(triples)
492    }
493}
494
495impl TryFromObject for UnitMultiplication {
496    fn try_from_object(object: &Object) -> Option<Self> {
497        Some(Self {
498            identity: object.identity().clone(),
499            identified: IdentifiedData::from_object(object),
500            top_level: TopLevelData::from_object(object),
501            unit: UnitData::from_object(object),
502            has_term1: object.first_resource(OM_HAS_TERM1).cloned(),
503            has_term2: object.first_resource(OM_HAS_TERM2).cloned(),
504        })
505    }
506}
507
508#[derive(Clone, Debug, PartialEq, Eq)]
509#[non_exhaustive]
510pub struct PrefixedUnit {
511    pub identity: Resource,
512    pub identified: IdentifiedData,
513    pub top_level: TopLevelData,
514    pub unit: UnitData,
515    pub has_unit: Option<Resource>,
516    pub has_prefix: Option<Resource>,
517}
518
519impl PrefixedUnit {
520    pub fn new(
521        namespace: impl TryInto<Namespace, Error = BuildError>,
522        display_id: impl TryInto<DisplayId, Error = BuildError>,
523        label: impl Into<String>,
524        symbol: impl Into<String>,
525        has_unit: Resource,
526        has_prefix: Resource,
527    ) -> Result<Self, BuildError> {
528        Self::builder(namespace, display_id)?
529            .label(label)
530            .symbol(symbol)
531            .has_unit(has_unit)
532            .has_prefix(has_prefix)
533            .build()
534    }
535
536    pub fn builder(
537        namespace: impl TryInto<Namespace, Error = BuildError>,
538        display_id: impl TryInto<DisplayId, Error = BuildError>,
539    ) -> Result<PrefixedUnitBuilder, BuildError> {
540        Ok(PrefixedUnitBuilder::seed(
541            namespace.try_into()?,
542            display_id.try_into()?,
543        ))
544    }
545}
546
547impl ToRdf for PrefixedUnit {
548    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
549        let mut triples = seed_triples(&self.identity, SbolClass::OmPrefixedUnit);
550        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmPrefixedUnit);
551        emit_identified(&mut e, &self.identified)?;
552        emit_top_level(&mut e, &self.top_level)?;
553        emit_unit_fields(&mut e, &self.unit)?;
554        e.resource(OM_HAS_UNIT, self.has_unit.as_ref())?;
555        e.resource(OM_HAS_PREFIX, self.has_prefix.as_ref())?;
556        drop(e);
557        Ok(triples)
558    }
559}
560
561impl TryFromObject for PrefixedUnit {
562    fn try_from_object(object: &Object) -> Option<Self> {
563        Some(Self {
564            identity: object.identity().clone(),
565            identified: IdentifiedData::from_object(object),
566            top_level: TopLevelData::from_object(object),
567            unit: UnitData::from_object(object),
568            has_unit: object.first_resource(OM_HAS_UNIT).cloned(),
569            has_prefix: object.first_resource(OM_HAS_PREFIX).cloned(),
570        })
571    }
572}
573
574/// Shared OM Prefix fields. Carried by every concrete Prefix subclass
575/// so the label/symbol/factor serializers can be reused.
576#[derive(Clone, Debug, Default, PartialEq, Eq)]
577#[non_exhaustive]
578pub struct PrefixData {
579    pub label: Option<String>,
580    pub symbol: Option<String>,
581    pub has_factor: Option<String>,
582    pub alternative_labels: Vec<String>,
583    pub alternative_symbols: Vec<String>,
584    pub comment: Option<String>,
585    pub long_comment: Option<String>,
586}
587
588impl PrefixData {
589    pub(crate) fn from_object(object: &Object) -> Self {
590        Self {
591            label: object.first_literal_value(OM_LABEL).map(ToOwned::to_owned),
592            symbol: object.first_literal_value(OM_SYMBOL).map(ToOwned::to_owned),
593            has_factor: object
594                .first_literal_value(OM_HAS_FACTOR)
595                .map(ToOwned::to_owned),
596            alternative_labels: literals(object, OM_ALTERNATIVE_LABEL),
597            alternative_symbols: literals(object, OM_ALTERNATIVE_SYMBOL),
598            comment: object
599                .first_literal_value(OM_COMMENT)
600                .map(ToOwned::to_owned),
601            long_comment: object
602                .first_literal_value(OM_LONG_COMMENT)
603                .map(ToOwned::to_owned),
604        }
605    }
606}
607
608pub(crate) fn emit_prefix_fields(e: &mut Emitter<'_>, data: &PrefixData) -> Result<(), BuildError> {
609    e.literal(OM_LABEL, data.label.as_deref())?;
610    e.literal(OM_SYMBOL, data.symbol.as_deref())?;
611    e.literal(OM_HAS_FACTOR, data.has_factor.as_deref())?;
612    e.literals(OM_ALTERNATIVE_LABEL, &data.alternative_labels)?;
613    e.literals(OM_ALTERNATIVE_SYMBOL, &data.alternative_symbols)?;
614    e.literal(OM_COMMENT, data.comment.as_deref())?;
615    e.literal(OM_LONG_COMMENT, data.long_comment.as_deref())?;
616    Ok(())
617}
618
619#[derive(Clone, Debug, PartialEq, Eq)]
620#[non_exhaustive]
621pub struct Prefix {
622    pub identity: Resource,
623    pub identified: IdentifiedData,
624    pub top_level: TopLevelData,
625    pub prefix: PrefixData,
626}
627
628impl Prefix {
629    pub fn new(
630        namespace: impl TryInto<Namespace, Error = BuildError>,
631        display_id: impl TryInto<DisplayId, Error = BuildError>,
632        label: impl Into<String>,
633        symbol: impl Into<String>,
634        has_factor: f64,
635    ) -> Result<Self, BuildError> {
636        Self::builder(namespace, display_id)?
637            .label(label)
638            .symbol(symbol)
639            .has_factor(has_factor)
640            .build()
641    }
642
643    pub fn builder(
644        namespace: impl TryInto<Namespace, Error = BuildError>,
645        display_id: impl TryInto<DisplayId, Error = BuildError>,
646    ) -> Result<PrefixBuilder, BuildError> {
647        Ok(PrefixBuilder::seed(
648            namespace.try_into()?,
649            display_id.try_into()?,
650        ))
651    }
652}
653
654impl ToRdf for Prefix {
655    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
656        let mut triples = seed_triples(&self.identity, SbolClass::OmPrefix);
657        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmPrefix);
658        emit_identified(&mut e, &self.identified)?;
659        emit_top_level(&mut e, &self.top_level)?;
660        emit_prefix_fields(&mut e, &self.prefix)?;
661        drop(e);
662        Ok(triples)
663    }
664}
665
666impl TryFromObject for Prefix {
667    fn try_from_object(object: &Object) -> Option<Self> {
668        Some(Self {
669            identity: object.identity().clone(),
670            identified: IdentifiedData::from_object(object),
671            top_level: TopLevelData::from_object(object),
672            prefix: PrefixData::from_object(object),
673        })
674    }
675}
676
677#[derive(Clone, Debug, PartialEq, Eq)]
678#[non_exhaustive]
679pub struct SIPrefix {
680    pub identity: Resource,
681    pub identified: IdentifiedData,
682    pub top_level: TopLevelData,
683    pub prefix: PrefixData,
684}
685
686impl SIPrefix {
687    pub fn new(
688        namespace: impl TryInto<Namespace, Error = BuildError>,
689        display_id: impl TryInto<DisplayId, Error = BuildError>,
690        label: impl Into<String>,
691        symbol: impl Into<String>,
692        has_factor: f64,
693    ) -> Result<Self, BuildError> {
694        Self::builder(namespace, display_id)?
695            .label(label)
696            .symbol(symbol)
697            .has_factor(has_factor)
698            .build()
699    }
700
701    pub fn builder(
702        namespace: impl TryInto<Namespace, Error = BuildError>,
703        display_id: impl TryInto<DisplayId, Error = BuildError>,
704    ) -> Result<SIPrefixBuilder, BuildError> {
705        Ok(SIPrefixBuilder::seed(
706            namespace.try_into()?,
707            display_id.try_into()?,
708        ))
709    }
710}
711
712impl ToRdf for SIPrefix {
713    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
714        let mut triples = seed_triples(&self.identity, SbolClass::OmSiPrefix);
715        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmSiPrefix);
716        emit_identified(&mut e, &self.identified)?;
717        emit_top_level(&mut e, &self.top_level)?;
718        emit_prefix_fields(&mut e, &self.prefix)?;
719        drop(e);
720        Ok(triples)
721    }
722}
723
724impl TryFromObject for SIPrefix {
725    fn try_from_object(object: &Object) -> Option<Self> {
726        Some(Self {
727            identity: object.identity().clone(),
728            identified: IdentifiedData::from_object(object),
729            top_level: TopLevelData::from_object(object),
730            prefix: PrefixData::from_object(object),
731        })
732    }
733}
734
735#[derive(Clone, Debug, PartialEq, Eq)]
736#[non_exhaustive]
737pub struct BinaryPrefix {
738    pub identity: Resource,
739    pub identified: IdentifiedData,
740    pub top_level: TopLevelData,
741    pub prefix: PrefixData,
742}
743
744impl BinaryPrefix {
745    pub fn new(
746        namespace: impl TryInto<Namespace, Error = BuildError>,
747        display_id: impl TryInto<DisplayId, Error = BuildError>,
748        label: impl Into<String>,
749        symbol: impl Into<String>,
750        has_factor: f64,
751    ) -> Result<Self, BuildError> {
752        Self::builder(namespace, display_id)?
753            .label(label)
754            .symbol(symbol)
755            .has_factor(has_factor)
756            .build()
757    }
758
759    pub fn builder(
760        namespace: impl TryInto<Namespace, Error = BuildError>,
761        display_id: impl TryInto<DisplayId, Error = BuildError>,
762    ) -> Result<BinaryPrefixBuilder, BuildError> {
763        Ok(BinaryPrefixBuilder::seed(
764            namespace.try_into()?,
765            display_id.try_into()?,
766        ))
767    }
768}
769
770impl ToRdf for BinaryPrefix {
771    fn to_rdf_triples(&self) -> Result<Vec<Triple>, BuildError> {
772        let mut triples = seed_triples(&self.identity, SbolClass::OmBinaryPrefix);
773        let mut e = Emitter::new(&mut triples, &self.identity, SbolClass::OmBinaryPrefix);
774        emit_identified(&mut e, &self.identified)?;
775        emit_top_level(&mut e, &self.top_level)?;
776        emit_prefix_fields(&mut e, &self.prefix)?;
777        drop(e);
778        Ok(triples)
779    }
780}
781
782impl TryFromObject for BinaryPrefix {
783    fn try_from_object(object: &Object) -> Option<Self> {
784        Some(Self {
785            identity: object.identity().clone(),
786            identified: IdentifiedData::from_object(object),
787            top_level: TopLevelData::from_object(object),
788            prefix: PrefixData::from_object(object),
789        })
790    }
791}
792
793impl_sbol_identified!(
794    Measure,
795    Unit,
796    SingularUnit,
797    CompoundUnit,
798    UnitDivision,
799    UnitExponentiation,
800    UnitMultiplication,
801    PrefixedUnit,
802    Prefix,
803    SIPrefix,
804    BinaryPrefix,
805);
806impl_sbol_top_level!(
807    Unit,
808    SingularUnit,
809    CompoundUnit,
810    UnitDivision,
811    UnitExponentiation,
812    UnitMultiplication,
813    PrefixedUnit,
814    Prefix,
815    SIPrefix,
816    BinaryPrefix,
817);