Skip to main content

concept_graph/
subsumption.rs

1// SPDX-FileCopyrightText: Cadasto B.V.
2// SPDX-License-Identifier: BUSL-1.1
3
4//! Subsumption over the closure, in the vocabulary FHIR `$subsumes` returns.
5
6use crate::closure::Closure;
7use crate::ordinal::Ordinal;
8
9/// The outcome of testing whether A subsumes B
10/// (<https://hl7.org/fhir/R4B/codesystem-concept-subsumption-outcome.html>).
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Outcome {
13    /// A and B are the same concept.
14    Equivalent,
15    /// A is an ancestor of B.
16    Subsumes,
17    /// A is a descendant of B.
18    SubsumedBy,
19    /// Neither is an ancestor of the other.
20    NotSubsumed,
21}
22
23impl Outcome {
24    /// The FHIR code.
25    #[must_use]
26    pub const fn code(self) -> &'static str {
27        match self {
28            Self::Equivalent => "equivalent",
29            Self::Subsumes => "subsumes",
30            Self::SubsumedBy => "subsumed-by",
31            Self::NotSubsumed => "not-subsumed",
32        }
33    }
34}
35
36/// Tests whether `a` subsumes `b` under `closure`.
37#[must_use]
38pub fn subsumes(closure: &Closure, a: Ordinal, b: Ordinal) -> Outcome {
39    if a == b {
40        Outcome::Equivalent
41    } else if closure.is_ancestor(a, b) {
42        Outcome::Subsumes
43    } else if closure.is_ancestor(b, a) {
44        Outcome::SubsumedBy
45    } else {
46        Outcome::NotSubsumed
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::{Outcome, subsumes};
53    use crate::closure::Closure;
54    use crate::csr::Csr;
55    use crate::ordinal::Ordinal;
56
57    #[test]
58    fn the_four_outcomes() {
59        let o = Ordinal::new;
60        let is_a = Csr::build(4, [(o(1), o(0)), (o(2), o(0)), (o(3), o(1))]).expect("builds");
61        let closure = Closure::compute(&is_a).expect("acyclic");
62        assert_eq!(subsumes(&closure, o(0), o(0)), Outcome::Equivalent);
63        assert_eq!(subsumes(&closure, o(0), o(3)), Outcome::Subsumes);
64        assert_eq!(subsumes(&closure, o(3), o(0)), Outcome::SubsumedBy);
65        assert_eq!(subsumes(&closure, o(2), o(3)), Outcome::NotSubsumed);
66        assert_eq!(Outcome::SubsumedBy.code(), "subsumed-by");
67    }
68}