Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
toolchain: ${{ matrix.toolchain }}

- name: Run tests
run: cargo test --release
run: cargo test

run-all-features:
runs-on: ubuntu-latest
Expand All @@ -37,7 +37,7 @@ jobs:
toolchain: "stable"

- name: Run tests
run: cargo test --features serde,pyo3 --release
run: cargo test --features serde,pyo3

format:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Cargo.lock
# IDE files
.vscode/
.idea/
.zed/

# Perf data
perf.data*
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ontolius"
version = "0.7.3"
version = "0.7.4"
description = "A fast and safe crate for working with biomedical ontologies."
keywords = ["ontology", "bioinformatics", "HPO", "MAxO", "GO"]
edition = "2021"
Expand Down
4 changes: 4 additions & 0 deletions src/ontology/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ where
}

/// Traversals in the ontology index space.
///
/// The child-parent relationship is established solely via the `is_a` relationship.
pub trait HierarchyTraversals<I> {
/// Get the index of the `query` term or `None` if the term is unknown.
fn term_index<Q>(&self, query: &Q) -> Option<I>
Expand Down Expand Up @@ -208,6 +210,8 @@ pub trait HierarchyWalks {
}

/// Tests if an ontology term is a parent, a child, an ancestor, or descendant of another term.
///
/// The child-parent relationship is established solely via the `is_a` relationship.
pub trait HierarchyQueries {
/// Test if `sub` is child of `obj`.
///
Expand Down
141 changes: 141 additions & 0 deletions src/ontology/csr/beta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@ where
}
}

macro_rules! impl_ontology_terms {
($t:ty) => {
impl<I, T> OntologyTerms<T> for $t
where
I: Idx,
{
fn iter_terms<'a>(&'a self) -> impl Iterator<Item = &'a T>
where
T: 'a,
{
(**self).iter_terms()
}

fn term_by_id<ID>(&self, id: &ID) -> Option<&T>
where
ID: Identified,
{
(**self).term_by_id(id)
}
}
};
}
impl_ontology_terms!(&CsrOntology<I, T>);
impl_ontology_terms!(Box<CsrOntology<I, T>>);

impl<I, T> HierarchyTraversals<I> for CsrOntology<I, T>
where
I: Idx + Hash,
Expand Down Expand Up @@ -150,6 +175,39 @@ where
}
}
}
macro_rules! impl_hierarchy_traversal {
($t:ty) => {
impl<I, T> HierarchyTraversals<I> for $t
where
I: Idx + Hash,
{
fn term_index<Q>(&self, query: &Q) -> Option<I>
where
Q: Identified,
{
(**self).term_index(query)
}

fn iter_child_idxs(&self, query: I) -> impl Iterator<Item = I> {
(**self).iter_child_idxs(query)
}

fn iter_descendant_idxs(&self, query: I) -> impl Iterator<Item = I> {
(**self).iter_descendant_idxs(query)
}

fn iter_parent_idxs(&self, query: I) -> impl Iterator<Item = I> {
(**self).iter_descendant_idxs(query)
}

fn iter_ancestor_idxs(&self, query: I) -> impl Iterator<Item = I> {
(**self).iter_ancestor_idxs(query)
}
}
};
}
impl_hierarchy_traversal!(&CsrOntology<I, T>);
impl_hierarchy_traversal!(Box<CsrOntology<I, T>>);

impl<I, T> HierarchyWalks for CsrOntology<I, T>
where
Expand Down Expand Up @@ -213,6 +271,46 @@ where
}
}

macro_rules! impl_hierarchy_walks {
($t:ty) => {
impl<I, T> HierarchyWalks for $t
where
I: Idx + Hash,
T: Identified,
{
fn iter_parent_ids<'a, ID>(&'a self, query: &ID) -> impl Iterator<Item = &'a TermId>
where
ID: Identified,
{
(**self).iter_parent_ids(query)
}

fn iter_child_ids<'a, ID>(&'a self, query: &ID) -> impl Iterator<Item = &'a TermId>
where
ID: Identified,
{
(**self).iter_child_ids(query)
}

fn iter_ancestor_ids<'a, ID>(&'a self, query: &ID) -> impl Iterator<Item = &'a TermId>
where
ID: Identified,
{
(**self).iter_ancestor_ids(query)
}

fn iter_descendant_ids<'a, ID>(&'a self, query: &ID) -> impl Iterator<Item = &'a TermId>
where
ID: Identified,
{
(**self).iter_descendant_ids(query)
}
}
};
}
impl_hierarchy_walks!(&CsrOntology<I, T>);
impl_hierarchy_walks!(Box<CsrOntology<I, T>>);

impl<I, T> HierarchyQueries for CsrOntology<I, T>
where
I: Idx + Hash,
Expand Down Expand Up @@ -274,6 +372,49 @@ where
}
}

macro_rules! impl_hierarchy_queries {
($t:ty) => {
impl<I, T> HierarchyQueries for $t
where
I: Idx + Hash,
{
fn is_child_of<S, O>(&self, sub: &S, obj: &O) -> bool
where
S: Identified,
O: Identified,
{
(**self).is_child_of(sub, obj)
}

fn is_descendant_of<S, O>(&self, sub: &S, obj: &O) -> bool
where
S: Identified,
O: Identified,
{
(**self).is_descendant_of(sub, obj)
}

fn is_parent_of<S, O>(&self, sub: &S, obj: &O) -> bool
where
S: Identified,
O: Identified,
{
(**self).is_parent_of(sub, obj)
}

fn is_ancestor_of<S, O>(&self, sub: &S, obj: &O) -> bool
where
S: Identified,
O: Identified,
{
(**self).is_ancestor_of(sub, obj)
}
}
};
}
impl_hierarchy_queries!(&CsrOntology<I, T>);
impl_hierarchy_queries!(Box<CsrOntology<I, T>>);

impl<I, T> MetadataAware for CsrOntology<I, T>
where
I: Idx,
Expand Down
31 changes: 31 additions & 0 deletions src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ pub mod simple {
}
}

impl Identified for &SimpleMinimalTerm {
fn identifier(&self) -> &TermId {
(**self).identifier()
}
}

impl AltTermIdAware for SimpleMinimalTerm {
type TermIdIter<'a>
= std::slice::Iter<'a, TermId>
Expand All @@ -150,6 +156,21 @@ pub mod simple {
}
}

impl AltTermIdAware for &SimpleMinimalTerm {
type TermIdIter<'a>
= std::slice::Iter<'a, TermId>
where
Self: 'a;

fn iter_alt_term_ids(&self) -> Self::TermIdIter<'_> {
(**self).iter_alt_term_ids()
}

fn alt_term_id_count(&self) -> usize {
(**self).alt_term_id_count()
}
}

impl MinimalTerm for SimpleMinimalTerm {
fn name(&self) -> &str {
self.name.as_str()
Expand All @@ -160,6 +181,16 @@ pub mod simple {
}
}

impl MinimalTerm for &SimpleMinimalTerm {
fn name(&self) -> &str {
(**self).name()
}

fn is_current(&self) -> bool {
(**self).is_current()
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SimpleTerm {
term_id: TermId,
Expand Down
28 changes: 28 additions & 0 deletions tests/test_ontology.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
mod csr {

use ontolius::ontology::{
csr::MinimalCsrOntology, HierarchyQueries, HierarchyTraversals, HierarchyWalks,
OntologyTerms,
};

#[test]
fn test_csr_ontology_can_be_used_with_trait_bounds() {
let o: Option<MinimalCsrOntology> = None;

if o.is_some() {
// This will never run. It does not matter,
// since it is enough that the code compiles.
let o: &MinimalCsrOntology = o.as_ref().unwrap();

pretend_to_use_hierarchy_queries(o);
pretend_to_use_hierarchy_walks(o);
pretend_to_use_hierarchy_traversals(o);
pretend_to_use_ontology_terms(o);
}

fn pretend_to_use_hierarchy_queries(_o: impl HierarchyQueries) {}
fn pretend_to_use_hierarchy_walks(_o: impl HierarchyWalks) {}
fn pretend_to_use_hierarchy_traversals<I>(_o: impl HierarchyTraversals<I>) {}
fn pretend_to_use_ontology_terms<T>(_o: impl OntologyTerms<T>) {}
}
}
Loading