From 740df52c840a8659ca4cfd73632d73078f8a36a3 Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Tue, 9 Dec 2025 16:54:11 +0100 Subject: [PATCH 1/9] Stable --- src/tree/mod.rs | 1 + src/tree/node.rs | 1 + src/tree/querying/data_query.rs | 111 ++++++++++++++++++++++++++++++ src/tree/querying/mod.rs | 3 + src/tree/querying/presentation.rs | 51 ++++++++++++++ src/tree/querying/queries.rs | 0 6 files changed, 167 insertions(+) create mode 100644 src/tree/querying/data_query.rs create mode 100644 src/tree/querying/mod.rs create mode 100644 src/tree/querying/presentation.rs create mode 100644 src/tree/querying/queries.rs diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 334f60d..fe93e4c 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -4,6 +4,7 @@ mod error; pub mod node; pub mod node_repository; pub mod pointer; +mod querying; mod scopes; pub mod traits; pub(crate) mod utils; diff --git a/src/tree/node.rs b/src/tree/node.rs index 5bbcfc8..8ccaf44 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -38,6 +38,7 @@ impl LocatableNode for DynamicNode { } } +#[derive(Clone)] pub struct MaterializedNode { pub inner: T, spans: HashMap>, diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs new file mode 100644 index 0000000..b0facf8 --- /dev/null +++ b/src/tree/querying/data_query.rs @@ -0,0 +1,111 @@ +use crate::tree::node::MaterializedNode; +use crate::tree::querying::presentation::{First, Grouped, QueryPresentation}; +use crate::tree::scopes::ScopeDefinition; +use crate::tree::traits::NodeRepository; +use phenopackets::schema::v2::Phenopacket; +use phenopackets::schema::v2::core::{OntologyClass, PhenotypicFeature}; +use std::marker::PhantomData; + +trait QueryNodeRepo { + fn query(node_repo: &impl NodeRepository) -> Self; +} + +macro_rules! impl_query_node_repo_for_tuples { + () => {}; + + ($head:ident $(, $tail:ident)*) => { + impl<$head, $($tail),*> QueryNodeRepo for ($head, $($tail),*) + where + $head: QueryNodeRepo, + $($tail: QueryNodeRepo),* + { + fn query(node_repo: &impl NodeRepository) -> Self { + ( + $head::query(node_repo), + $($tail::query(node_repo)),* + ) + } + } + + impl_query_node_repo_for_tuples!($($tail),*); + }; +} +impl_query_node_repo_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + +struct QueryNodesInScope { + pub result: Quantity, + scope: PhantomData, + node_type: PhantomData, +} + +impl< + Scope: ScopeDefinition, + NodeType: Clone + 'static, + Quantity: QueryPresentation>>, +> QueryNodeRepo for QueryNodesInScope +{ + fn query(node_repo: &impl NodeRepository) -> Self { + let a = node_repo + .get_nodes_in_scope::(Scope::layer()) + .unwrap_or_default(); + + QueryNodesInScope { + result: Quantity::present(a), + scope: PhantomData, + node_type: PhantomData, + } + } +} + +struct QueryGroupedNodes { + pub result: Quantity, + _scope: PhantomData, + _node: PhantomData, +} + +impl< + Scope: ScopeDefinition, + NodeType: Clone + 'static, + Quantity: QueryPresentation>>>, +> QueryNodeRepo for QueryGroupedNodes +{ + fn query(node_repo: &impl NodeRepository) -> Self { + let a = node_repo + .get_nodes_for_scope_per_top_level_element::(Scope::layer()) + .unwrap_or_default(); + + QueryGroupedNodes { + result: Quantity::present(a), + _scope: Default::default(), + _node: Default::default(), + } + } +} + +// For show off + +trait TheRuleTrait { + type Query: QueryNodeRepo; + + fn check_erased(&'_ self, board: Self::Query) -> bool; +} + +struct __RuleImplementation1; + +impl TheRuleTrait for __RuleImplementation1 { + type Query = QueryNodesInScope>; + + fn check_erased(&'_ self, board: Self::Query) -> bool { + todo!() + } +} + +struct __RuleImplementation3; + +impl TheRuleTrait for __RuleImplementation3 { + type Query = QueryNodesInScope>; + + fn check_erased(&self, board: Self::Query) -> bool { + todo!() + } +} diff --git a/src/tree/querying/mod.rs b/src/tree/querying/mod.rs new file mode 100644 index 0000000..c0b4d47 --- /dev/null +++ b/src/tree/querying/mod.rs @@ -0,0 +1,3 @@ +pub mod data_query; +pub mod presentation; +pub mod queries; diff --git a/src/tree/querying/presentation.rs b/src/tree/querying/presentation.rs new file mode 100644 index 0000000..ea6a5de --- /dev/null +++ b/src/tree/querying/presentation.rs @@ -0,0 +1,51 @@ +use crate::tree::node::MaterializedNode; + +pub trait QueryPresentation { + fn present(query_res: Input) -> Self + where + Self: Sized; +} + +struct Flattened(pub Vec>); + +impl QueryPresentation>> for Flattened { + fn present(query_res: Vec>) -> Self { + Flattened(query_res) + } +} + +impl QueryPresentation>>> for Flattened { + fn present(query_res: Vec>>) -> Self { + Flattened(query_res.into_iter().flatten().collect()) + } +} + +pub struct First(pub Option>); + +impl QueryPresentation>> for First { + fn present(query_res: Vec>) -> Self { + First(query_res.first().cloned()) + } +} + +impl QueryPresentation>>> for First { + fn present(query_res: Vec>>) -> Self { + for i in query_res { + if let Some(j) = i.into_iter().next() { + return First(Some(j)); + } + } + + First(None) + } +} + +pub struct Grouped(pub Vec>>); + +impl QueryPresentation>>> + for Grouped +{ + fn present(query_res: Vec>>) -> Self { + Grouped(query_res) + } +} diff --git a/src/tree/querying/queries.rs b/src/tree/querying/queries.rs new file mode 100644 index 0000000..e69de29 From d4e6560cc56eb80de36a22b432c482176ea6839c Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Tue, 9 Dec 2025 17:36:00 +0100 Subject: [PATCH 2/9] Deref --- src/tree/node.rs | 2 +- src/tree/querying/data_query.rs | 103 +++++++++++++++++------------- src/tree/querying/presentation.rs | 29 ++++++++- 3 files changed, 88 insertions(+), 46 deletions(-) diff --git a/src/tree/node.rs b/src/tree/node.rs index 8ccaf44..7b7e2d0 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -38,7 +38,7 @@ impl LocatableNode for DynamicNode { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MaterializedNode { pub inner: T, spans: HashMap>, diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs index b0facf8..41c3612 100644 --- a/src/tree/querying/data_query.rs +++ b/src/tree/querying/data_query.rs @@ -1,39 +1,49 @@ use crate::tree::node::MaterializedNode; -use crate::tree::querying::presentation::{First, Grouped, QueryPresentation}; +use crate::tree::querying::presentation::{First, Flattened, Grouped, QueryPresentation}; use crate::tree::scopes::ScopeDefinition; use crate::tree::traits::NodeRepository; use phenopackets::schema::v2::Phenopacket; -use phenopackets::schema::v2::core::{OntologyClass, PhenotypicFeature}; +use phenopackets::schema::v2::core::OntologyClass; use std::marker::PhantomData; -trait QueryNodeRepo { - fn query(node_repo: &impl NodeRepository) -> Self; +trait QueryStrategy { + type Output; + fn query(node_repo: &impl NodeRepository) -> Self::Output; } -macro_rules! impl_query_node_repo_for_tuples { - () => {}; +macro_rules! impl_query_strategy_for_tuples { + ($($name:ident),*) => { + impl<$($name: QueryStrategy),*> QueryStrategy for ($($name,)*) { + type Output = ($($name::Output,)*); - ($head:ident $(, $tail:ident)*) => { - impl<$head, $($tail),*> QueryNodeRepo for ($head, $($tail),*) - where - $head: QueryNodeRepo, - $($tail: QueryNodeRepo),* - { - fn query(node_repo: &impl NodeRepository) -> Self { + fn query(node_repo: &impl NodeRepository) -> Self::Output { ( - $head::query(node_repo), - $($tail::query(node_repo)),* + $($name::query(node_repo),)* ) } } - - impl_query_node_repo_for_tuples!($($tail),*); }; } -impl_query_node_repo_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); -struct QueryNodesInScope { - pub result: Quantity, +impl_query_strategy_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + +#[derive(Debug)] +struct QueryAllNodes { + result: PhantomData, + node_type: PhantomData, +} + +impl>>> + QueryStrategy for QueryAllNodes +{ + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + Presentation::present(node_repo.get_all::().unwrap_or_default()) + } +} +#[derive(Debug)] +struct QueryNodesInScope { + result: PhantomData, scope: PhantomData, node_type: PhantomData, } @@ -41,24 +51,22 @@ struct QueryNodesInScope { impl< Scope: ScopeDefinition, NodeType: Clone + 'static, - Quantity: QueryPresentation>>, -> QueryNodeRepo for QueryNodesInScope + Presentation: QueryPresentation>>, +> QueryStrategy for QueryNodesInScope { - fn query(node_repo: &impl NodeRepository) -> Self { - let a = node_repo + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + let query_result = node_repo .get_nodes_in_scope::(Scope::layer()) .unwrap_or_default(); - QueryNodesInScope { - result: Quantity::present(a), - scope: PhantomData, - node_type: PhantomData, - } + Presentation::present(query_result) } } -struct QueryGroupedNodes { - pub result: Quantity, +#[derive(Debug)] +struct QueryGroupedNodes { + pub result: Presentation, _scope: PhantomData, _node: PhantomData, } @@ -66,26 +74,23 @@ struct QueryGroupedNodes { impl< Scope: ScopeDefinition, NodeType: Clone + 'static, - Quantity: QueryPresentation>>>, -> QueryNodeRepo for QueryGroupedNodes + Presentation: QueryPresentation>>>, +> QueryStrategy for QueryGroupedNodes { - fn query(node_repo: &impl NodeRepository) -> Self { - let a = node_repo + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + let query_result = node_repo .get_nodes_for_scope_per_top_level_element::(Scope::layer()) .unwrap_or_default(); - QueryGroupedNodes { - result: Quantity::present(a), - _scope: Default::default(), - _node: Default::default(), - } + Presentation::present(query_result) } } -// For show off +// Testing and see how it would work from here: trait TheRuleTrait { - type Query: QueryNodeRepo; + type Query: QueryStrategy; fn check_erased(&'_ self, board: Self::Query) -> bool; } @@ -100,10 +105,22 @@ impl TheRuleTrait for __RuleImplementation1 { } } +struct __RuleImplementation2; + +type QueryAll = QueryAllNodes>; + +impl TheRuleTrait for __RuleImplementation2 { + type Query = QueryAll; + + fn check_erased(&self, board: Self::Query) -> bool { + todo!() + } +} + struct __RuleImplementation3; impl TheRuleTrait for __RuleImplementation3 { - type Query = QueryNodesInScope>; + type Query = QueryGroupedNodes>; fn check_erased(&self, board: Self::Query) -> bool { todo!() diff --git a/src/tree/querying/presentation.rs b/src/tree/querying/presentation.rs index ea6a5de..85f8302 100644 --- a/src/tree/querying/presentation.rs +++ b/src/tree/querying/presentation.rs @@ -1,4 +1,5 @@ use crate::tree::node::MaterializedNode; +use std::ops::Deref; pub trait QueryPresentation { fn present(query_res: Input) -> Self @@ -6,7 +7,15 @@ pub trait QueryPresentation { Self: Sized; } -struct Flattened(pub Vec>); +pub struct Flattened(pub Vec>); + +impl Deref for Flattened { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} impl QueryPresentation>> for Flattened { fn present(query_res: Vec>) -> Self { @@ -20,7 +29,15 @@ impl QueryPresentation>>> for Flatt } } -pub struct First(pub Option>); +pub struct First(pub Option>); + +impl Deref for First { + type Target = Option>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} impl QueryPresentation>> for First { fn present(query_res: Vec>) -> Self { @@ -42,6 +59,14 @@ impl QueryPresentation>>> fo pub struct Grouped(pub Vec>>); +impl Deref for Grouped { + type Target = Vec>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl QueryPresentation>>> for Grouped { From 446491f22800ece1dbd55f5f4a774637aa4a14b3 Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Tue, 9 Dec 2025 17:40:51 +0100 Subject: [PATCH 3/9] Better inputs --- src/tree/querying/data_query.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs index 41c3612..66af550 100644 --- a/src/tree/querying/data_query.rs +++ b/src/tree/querying/data_query.rs @@ -29,8 +29,8 @@ impl_query_strategy_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T1 #[derive(Debug)] struct QueryAllNodes { - result: PhantomData, node_type: PhantomData, + presentation: PhantomData, } impl>>> @@ -92,7 +92,7 @@ impl< trait TheRuleTrait { type Query: QueryStrategy; - fn check_erased(&'_ self, board: Self::Query) -> bool; + fn check_erased(&'_ self, board: ::Output) -> bool; } struct __RuleImplementation1; @@ -100,19 +100,21 @@ struct __RuleImplementation1; impl TheRuleTrait for __RuleImplementation1 { type Query = QueryNodesInScope>; - fn check_erased(&'_ self, board: Self::Query) -> bool { + fn check_erased(&'_ self, board: ::Output) -> bool { todo!() } } struct __RuleImplementation2; +// More to be added. type QueryAll = QueryAllNodes>; impl TheRuleTrait for __RuleImplementation2 { type Query = QueryAll; - fn check_erased(&self, board: Self::Query) -> bool { + fn check_erased(&self, board: ::Output) -> bool { + let a = board; todo!() } } @@ -122,7 +124,7 @@ struct __RuleImplementation3; impl TheRuleTrait for __RuleImplementation3 { type Query = QueryGroupedNodes>; - fn check_erased(&self, board: Self::Query) -> bool { + fn check_erased(&self, board: ::Output) -> bool { todo!() } } From 26becf9ca5ebcbd4184a7ff73023d83eebfd6bdd Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Tue, 9 Dec 2025 17:47:54 +0100 Subject: [PATCH 4/9] Clean up --- src/tree/querying/data_query.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs index 66af550..2439499 100644 --- a/src/tree/querying/data_query.rs +++ b/src/tree/querying/data_query.rs @@ -28,10 +28,7 @@ macro_rules! impl_query_strategy_for_tuples { impl_query_strategy_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); #[derive(Debug)] -struct QueryAllNodes { - node_type: PhantomData, - presentation: PhantomData, -} +struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); impl>>> QueryStrategy for QueryAllNodes @@ -42,11 +39,9 @@ impl { - result: PhantomData, - scope: PhantomData, - node_type: PhantomData, -} +struct QueryNodesInScope( + PhantomData<(Scope, Presentation, NodeType)>, +); impl< Scope: ScopeDefinition, @@ -65,11 +60,9 @@ impl< } #[derive(Debug)] -struct QueryGroupedNodes { - pub result: Presentation, - _scope: PhantomData, - _node: PhantomData, -} +struct QueryGroupedNodes( + PhantomData<(Scope, Presentation, NodeType)>, +); impl< Scope: ScopeDefinition, From 533b27103a66a0046b9dfcc12c920de243321ef0 Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Wed, 10 Dec 2025 10:17:15 +0100 Subject: [PATCH 5/9] Rename generic T to NodeType --- src/materializer.rs | 4 +-- src/tree/btree_node_repository.rs | 53 +++++++++++++++------------- src/tree/node.rs | 14 ++++---- src/tree/querying/data_query.rs | 58 ++++++++++++++++++++++++------- src/tree/scopes.rs | 8 ++--- 5 files changed, 87 insertions(+), 50 deletions(-) diff --git a/src/materializer.rs b/src/materializer.rs index eaeadd2..89d735b 100644 --- a/src/materializer.rs +++ b/src/materializer.rs @@ -33,8 +33,8 @@ impl NodeMaterializer { }; } - fn push_to_repo( - materialized: T, + fn push_to_repo( + materialized: NodeType, dyn_node: &DynamicNode, board: &mut NodeRepository, ) { diff --git a/src/tree/btree_node_repository.rs b/src/tree/btree_node_repository.rs index 566b6ef..436fb80 100644 --- a/src/tree/btree_node_repository.rs +++ b/src/tree/btree_node_repository.rs @@ -38,18 +38,18 @@ impl BTreeNodeRepository { .collect() } - fn cast_entry( + fn cast_entry( &self, path: &str, entry: &NodeEntry, - ) -> Result, NodeRepositoryError> + ) -> Result, NodeRepositoryError> where - T: Clone + 'static, + NodeType: Clone + 'static, { - let content_ref = entry.inner.downcast_ref::().ok_or_else(|| { + let content_ref = entry.inner.downcast_ref::().ok_or_else(|| { NodeRepositoryError::CantReinstantiateNode( path.to_string(), - std::any::type_name::().to_string(), + std::any::type_name::().to_string(), ) })?; @@ -61,8 +61,11 @@ impl BTreeNodeRepository { } impl NodeRepository for BTreeNodeRepository { - fn insert(&mut self, node: MaterializedNode) -> Result<(), NodeRepositoryError> { - let type_id = TypeId::of::(); + fn insert( + &mut self, + node: MaterializedNode, + ) -> Result<(), NodeRepositoryError> { + let type_id = TypeId::of::(); let node_path = node.pointer().position().to_string(); let scope = self @@ -88,49 +91,49 @@ impl NodeRepository for BTreeNodeRepository { Ok(()) } - fn get_all(&self) -> Result>, NodeRepositoryError> + fn get_all(&self) -> Result>, NodeRepositoryError> where - T: Clone + 'static, + NodeType: Clone + 'static, { - let target_type = TypeId::of::(); + let target_type = TypeId::of::(); let nodes = self .node_store .iter() .filter(|(_, entry)| entry.type_id == target_type) - .map(|(path, entry)| self.cast_entry::(path.as_str(), entry)) - .collect::>, NodeRepositoryError>>()?; + .map(|(path, entry)| self.cast_entry::(path.as_str(), entry)) + .collect::>, NodeRepositoryError>>()?; Ok(nodes) } - fn get_nodes_in_scope( + fn get_nodes_in_scope( &self, scope: ScopeLayer, - ) -> Result>, NodeRepositoryError> + ) -> Result>, NodeRepositoryError> where - T: Clone + 'static, + NodeType: Clone + 'static, { - let target_type = TypeId::of::(); + let target_type = TypeId::of::(); let nodes = self .node_store .iter() .filter(|(_, entry)| entry.type_id == target_type && entry.scope == scope) - .map(|(path, entry)| self.cast_entry::(path, entry)) - .collect::>, NodeRepositoryError>>()?; + .map(|(path, entry)| self.cast_entry::(path, entry)) + .collect::>, NodeRepositoryError>>()?; Ok(nodes) } - fn get_nodes_for_scope_per_top_level_element( + fn get_nodes_for_scope_per_top_level_element( &self, scope: ScopeLayer, - ) -> Result>>, NodeRepositoryError> + ) -> Result>>, NodeRepositoryError> where - T: Clone + 'static, + NodeType: Clone + 'static, { - let target_type = TypeId::of::(); + let target_type = TypeId::of::(); let top_levels: Vec<&String> = self .node_store @@ -147,10 +150,10 @@ impl NodeRepository for BTreeNodeRepository { .range::(tl_path.to_string()..) .take_while(|(k, _)| k.starts_with(tl_path)) .filter(|(_, entry)| entry.type_id == target_type && entry.scope == scope) - .map(|(path, entry)| self.cast_entry::(path, entry)) - .collect::>, NodeRepositoryError>>()?; + .map(|(path, entry)| self.cast_entry::(path, entry)) + .collect::>, NodeRepositoryError>>()?; - if !children.is_empty() { + if children.is_empty() { output.push(children); } } diff --git a/src/tree/node.rs b/src/tree/node.rs index 7b7e2d0..1f4e1ec 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -39,15 +39,15 @@ impl LocatableNode for DynamicNode { } #[derive(Clone, Debug)] -pub struct MaterializedNode { - pub inner: T, +pub struct MaterializedNode { + pub inner: NodeType, spans: HashMap>, pointer: Pointer, } -impl MaterializedNode { +impl MaterializedNode { pub fn new( - materialized_node: T, + materialized_node: NodeType, spans: HashMap>, pointer: Pointer, ) -> Self { @@ -58,7 +58,7 @@ impl MaterializedNode { } } - pub(crate) fn from_dynamic(materialized: T, dyn_node: &DynamicNode) -> Self { + pub(crate) fn from_dynamic(materialized: NodeType, dyn_node: &DynamicNode) -> Self { Self::new( materialized, dyn_node.spans.clone(), @@ -72,7 +72,7 @@ impl MaterializedNode { } } -impl RetrievableNode for MaterializedNode { +impl RetrievableNode for MaterializedNode { fn value_at(&self, ptr: &Pointer) -> Option> { let node_opt = serde_json::to_value(&self.inner).ok()?; let value = node_opt.pointer(ptr.position())?.clone(); @@ -80,7 +80,7 @@ impl RetrievableNode for MaterializedNode { } } -impl LocatableNode for MaterializedNode { +impl LocatableNode for MaterializedNode { fn span_at(&self, ptr: &Pointer) -> Option<&Range> { self.spans.get(ptr) } diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs index 2439499..68f76ae 100644 --- a/src/tree/querying/data_query.rs +++ b/src/tree/querying/data_query.rs @@ -1,7 +1,10 @@ +use crate::rules::traits::LintRule; use crate::tree::node::MaterializedNode; -use crate::tree::querying::presentation::{First, Flattened, Grouped, QueryPresentation}; +use crate::tree::querying::data_query::queries::{All, GroupedIndividuals, SingleInScope}; +use crate::tree::querying::presentation::{First, Flattened, QueryPresentation}; use crate::tree::scopes::ScopeDefinition; use crate::tree::traits::NodeRepository; +use phenopackets::schema::v1::core::PhenotypicFeature; use phenopackets::schema::v2::Phenopacket; use phenopackets::schema::v2::core::OntologyClass; use std::marker::PhantomData; @@ -11,21 +14,37 @@ trait QueryStrategy { fn query(node_repo: &impl NodeRepository) -> Self::Output; } -macro_rules! impl_query_strategy_for_tuples { - ($($name:ident),*) => { - impl<$($name: QueryStrategy),*> QueryStrategy for ($($name,)*) { - type Output = ($($name::Output,)*); +macro_rules! impl_query_strategy_tuple { + () => { + impl QueryStrategy for () { + type Output = (); + fn query(_node_repo: &impl NodeRepository) -> Self::Output { + + } + } + }; + + ($head:ident $(, $tail:ident)*) => { + impl_query_strategy_tuple!($($tail),*); + + impl<$head, $($tail),*> QueryStrategy for ($head, $($tail,)*) + where + $head: QueryStrategy, + $($tail: QueryStrategy),* + { + type Output = ($head::Output, $($tail::Output,)*); fn query(node_repo: &impl NodeRepository) -> Self::Output { ( - $($name::query(node_repo),)* + $head::query(node_repo), + $($tail::query(node_repo),)* ) } } }; } -impl_query_strategy_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); +impl_query_strategy_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); #[derive(Debug)] struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); @@ -91,7 +110,10 @@ trait TheRuleTrait { struct __RuleImplementation1; impl TheRuleTrait for __RuleImplementation1 { - type Query = QueryNodesInScope>; + type Query = ( + QueryNodesInScope>, + QueryAllNodes>, + ); fn check_erased(&'_ self, board: ::Output) -> bool { todo!() @@ -100,11 +122,23 @@ impl TheRuleTrait for __RuleImplementation1 { struct __RuleImplementation2; -// More to be added. -type QueryAll = QueryAllNodes>; +pub mod queries { + use crate::tree::querying::data_query::{QueryAllNodes, QueryGroupedNodes, QueryNodesInScope}; + use crate::tree::querying::presentation::{First, Flattened, Grouped}; + use phenopackets::schema::v2::Phenopacket; + // More to be added. + pub type All = QueryAllNodes>; + pub type GroupedIndividuals = + QueryGroupedNodes>; + pub type SingleInScope = QueryNodesInScope>; +} impl TheRuleTrait for __RuleImplementation2 { - type Query = QueryAll; + type Query = ( + All, + SingleInScope, + GroupedIndividuals, + ); fn check_erased(&self, board: ::Output) -> bool { let a = board; @@ -115,7 +149,7 @@ impl TheRuleTrait for __RuleImplementation2 { struct __RuleImplementation3; impl TheRuleTrait for __RuleImplementation3 { - type Query = QueryGroupedNodes>; + type Query = GroupedIndividuals; fn check_erased(&self, board: ::Output) -> bool { todo!() diff --git a/src/tree/scopes.rs b/src/tree/scopes.rs index bdf12f2..e5d80c0 100644 --- a/src/tree/scopes.rs +++ b/src/tree/scopes.rs @@ -61,11 +61,11 @@ impl ScopeMappings { scope_map } - fn register(&mut self) { - let type_id = TypeId::of::(); - self.scope_by_type_id.insert(type_id, T::layer()); + fn register(&mut self) { + let type_id = TypeId::of::(); + self.scope_by_type_id.insert(type_id, NodeType::layer()); - for b_field in T::partitioning_fields() { + for b_field in NodeType::partitioning_fields() { self.boundaries.insert(b_field, type_id); } } From 7ccc4180b7dc2b6771a8128665cab739b0f53abf Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Wed, 10 Dec 2025 10:56:18 +0100 Subject: [PATCH 6/9] Linting --- src/tree/querying/data_query.rs | 105 ++++++++++++++++-------------- src/tree/querying/presentation.rs | 1 + 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs index 68f76ae..10bdff3 100644 --- a/src/tree/querying/data_query.rs +++ b/src/tree/querying/data_query.rs @@ -1,16 +1,14 @@ -use crate::rules::traits::LintRule; use crate::tree::node::MaterializedNode; -use crate::tree::querying::data_query::queries::{All, GroupedIndividuals, SingleInScope}; -use crate::tree::querying::presentation::{First, Flattened, QueryPresentation}; + +use crate::tree::querying::presentation::QueryPresentation; use crate::tree::scopes::ScopeDefinition; use crate::tree::traits::NodeRepository; -use phenopackets::schema::v1::core::PhenotypicFeature; -use phenopackets::schema::v2::Phenopacket; -use phenopackets::schema::v2::core::OntologyClass; + use std::marker::PhantomData; trait QueryStrategy { type Output; + #[allow(unused)] fn query(node_repo: &impl NodeRepository) -> Self::Output; } @@ -47,7 +45,7 @@ macro_rules! impl_query_strategy_tuple { impl_query_strategy_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); #[derive(Debug)] -struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); +pub struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); impl>>> QueryStrategy for QueryAllNodes @@ -58,7 +56,7 @@ impl( +pub struct QueryNodesInScope( PhantomData<(Scope, Presentation, NodeType)>, ); @@ -79,7 +77,7 @@ impl< } #[derive(Debug)] -struct QueryGroupedNodes( +pub struct QueryGroupedNodes( PhantomData<(Scope, Presentation, NodeType)>, ); @@ -99,29 +97,6 @@ impl< } } -// Testing and see how it would work from here: - -trait TheRuleTrait { - type Query: QueryStrategy; - - fn check_erased(&'_ self, board: ::Output) -> bool; -} - -struct __RuleImplementation1; - -impl TheRuleTrait for __RuleImplementation1 { - type Query = ( - QueryNodesInScope>, - QueryAllNodes>, - ); - - fn check_erased(&'_ self, board: ::Output) -> bool { - todo!() - } -} - -struct __RuleImplementation2; - pub mod queries { use crate::tree::querying::data_query::{QueryAllNodes, QueryGroupedNodes, QueryNodesInScope}; use crate::tree::querying::presentation::{First, Flattened, Grouped}; @@ -133,25 +108,59 @@ pub mod queries { QueryGroupedNodes>; pub type SingleInScope = QueryNodesInScope>; } -impl TheRuleTrait for __RuleImplementation2 { - type Query = ( - All, - SingleInScope, - GroupedIndividuals, - ); - - fn check_erased(&self, board: ::Output) -> bool { - let a = board; - todo!() + +mod temp_test { + #![allow(dead_code)] + #![allow(unused)] + // Testing and see how it would work from here: + + use crate::tree::querying::data_query::queries::{All, GroupedIndividuals, SingleInScope}; + use crate::tree::querying::data_query::{QueryAllNodes, QueryNodesInScope, QueryStrategy}; + use crate::tree::querying::presentation::{First, Flattened}; + use phenopackets::schema::v2::Phenopacket; + use phenopackets::schema::v2::core::{OntologyClass, PhenotypicFeature}; + + trait TheRuleTrait { + type Query: QueryStrategy; + + fn check_erased(&'_ self, board: ::Output) -> bool; } -} -struct __RuleImplementation3; + struct __RuleImplementation1; + + impl TheRuleTrait for __RuleImplementation1 { + type Query = ( + QueryNodesInScope>, + QueryAllNodes>, + ); + + fn check_erased(&'_ self, board: ::Output) -> bool { + todo!() + } + } + + struct __RuleImplementation2; + + impl TheRuleTrait for __RuleImplementation2 { + type Query = ( + All, + SingleInScope, + GroupedIndividuals, + ); -impl TheRuleTrait for __RuleImplementation3 { - type Query = GroupedIndividuals; + fn check_erased(&self, board: ::Output) -> bool { + let a = board; + todo!() + } + } - fn check_erased(&self, board: ::Output) -> bool { - todo!() + struct __RuleImplementation3; + + impl TheRuleTrait for __RuleImplementation3 { + type Query = GroupedIndividuals; + + fn check_erased(&self, board: ::Output) -> bool { + todo!() + } } } diff --git a/src/tree/querying/presentation.rs b/src/tree/querying/presentation.rs index 85f8302..6583bac 100644 --- a/src/tree/querying/presentation.rs +++ b/src/tree/querying/presentation.rs @@ -1,6 +1,7 @@ use crate::tree::node::MaterializedNode; use std::ops::Deref; +#[allow(unused)] pub trait QueryPresentation { fn present(query_res: Input) -> Self where From 1cc3c785c3f7468a83ca798da7dae90eaad28d74 Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Wed, 10 Dec 2025 11:02:10 +0100 Subject: [PATCH 7/9] Clean up --- src/tree/querying/data_query.rs | 166 ------------------------------ src/tree/querying/mod.rs | 2 +- src/tree/querying/presentation.rs | 8 +- src/tree/querying/queries.rs | 129 +++++++++++++++++++++++ src/tree/querying/traits.rs | 46 +++++++++ 5 files changed, 177 insertions(+), 174 deletions(-) delete mode 100644 src/tree/querying/data_query.rs create mode 100644 src/tree/querying/traits.rs diff --git a/src/tree/querying/data_query.rs b/src/tree/querying/data_query.rs deleted file mode 100644 index 10bdff3..0000000 --- a/src/tree/querying/data_query.rs +++ /dev/null @@ -1,166 +0,0 @@ -use crate::tree::node::MaterializedNode; - -use crate::tree::querying::presentation::QueryPresentation; -use crate::tree::scopes::ScopeDefinition; -use crate::tree::traits::NodeRepository; - -use std::marker::PhantomData; - -trait QueryStrategy { - type Output; - #[allow(unused)] - fn query(node_repo: &impl NodeRepository) -> Self::Output; -} - -macro_rules! impl_query_strategy_tuple { - () => { - impl QueryStrategy for () { - type Output = (); - fn query(_node_repo: &impl NodeRepository) -> Self::Output { - - } - } - }; - - ($head:ident $(, $tail:ident)*) => { - impl_query_strategy_tuple!($($tail),*); - - impl<$head, $($tail),*> QueryStrategy for ($head, $($tail,)*) - where - $head: QueryStrategy, - $($tail: QueryStrategy),* - { - type Output = ($head::Output, $($tail::Output,)*); - - fn query(node_repo: &impl NodeRepository) -> Self::Output { - ( - $head::query(node_repo), - $($tail::query(node_repo),)* - ) - } - } - }; -} - -impl_query_strategy_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); - -#[derive(Debug)] -pub struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); - -impl>>> - QueryStrategy for QueryAllNodes -{ - type Output = Presentation; - fn query(node_repo: &impl NodeRepository) -> Self::Output { - Presentation::present(node_repo.get_all::().unwrap_or_default()) - } -} -#[derive(Debug)] -pub struct QueryNodesInScope( - PhantomData<(Scope, Presentation, NodeType)>, -); - -impl< - Scope: ScopeDefinition, - NodeType: Clone + 'static, - Presentation: QueryPresentation>>, -> QueryStrategy for QueryNodesInScope -{ - type Output = Presentation; - fn query(node_repo: &impl NodeRepository) -> Self::Output { - let query_result = node_repo - .get_nodes_in_scope::(Scope::layer()) - .unwrap_or_default(); - - Presentation::present(query_result) - } -} - -#[derive(Debug)] -pub struct QueryGroupedNodes( - PhantomData<(Scope, Presentation, NodeType)>, -); - -impl< - Scope: ScopeDefinition, - NodeType: Clone + 'static, - Presentation: QueryPresentation>>>, -> QueryStrategy for QueryGroupedNodes -{ - type Output = Presentation; - fn query(node_repo: &impl NodeRepository) -> Self::Output { - let query_result = node_repo - .get_nodes_for_scope_per_top_level_element::(Scope::layer()) - .unwrap_or_default(); - - Presentation::present(query_result) - } -} - -pub mod queries { - use crate::tree::querying::data_query::{QueryAllNodes, QueryGroupedNodes, QueryNodesInScope}; - use crate::tree::querying::presentation::{First, Flattened, Grouped}; - use phenopackets::schema::v2::Phenopacket; - - // More to be added. - pub type All = QueryAllNodes>; - pub type GroupedIndividuals = - QueryGroupedNodes>; - pub type SingleInScope = QueryNodesInScope>; -} - -mod temp_test { - #![allow(dead_code)] - #![allow(unused)] - // Testing and see how it would work from here: - - use crate::tree::querying::data_query::queries::{All, GroupedIndividuals, SingleInScope}; - use crate::tree::querying::data_query::{QueryAllNodes, QueryNodesInScope, QueryStrategy}; - use crate::tree::querying::presentation::{First, Flattened}; - use phenopackets::schema::v2::Phenopacket; - use phenopackets::schema::v2::core::{OntologyClass, PhenotypicFeature}; - - trait TheRuleTrait { - type Query: QueryStrategy; - - fn check_erased(&'_ self, board: ::Output) -> bool; - } - - struct __RuleImplementation1; - - impl TheRuleTrait for __RuleImplementation1 { - type Query = ( - QueryNodesInScope>, - QueryAllNodes>, - ); - - fn check_erased(&'_ self, board: ::Output) -> bool { - todo!() - } - } - - struct __RuleImplementation2; - - impl TheRuleTrait for __RuleImplementation2 { - type Query = ( - All, - SingleInScope, - GroupedIndividuals, - ); - - fn check_erased(&self, board: ::Output) -> bool { - let a = board; - todo!() - } - } - - struct __RuleImplementation3; - - impl TheRuleTrait for __RuleImplementation3 { - type Query = GroupedIndividuals; - - fn check_erased(&self, board: ::Output) -> bool { - todo!() - } - } -} diff --git a/src/tree/querying/mod.rs b/src/tree/querying/mod.rs index c0b4d47..436e089 100644 --- a/src/tree/querying/mod.rs +++ b/src/tree/querying/mod.rs @@ -1,3 +1,3 @@ -pub mod data_query; pub mod presentation; pub mod queries; +mod traits; diff --git a/src/tree/querying/presentation.rs b/src/tree/querying/presentation.rs index 6583bac..53f3528 100644 --- a/src/tree/querying/presentation.rs +++ b/src/tree/querying/presentation.rs @@ -1,13 +1,7 @@ use crate::tree::node::MaterializedNode; +use crate::tree::querying::traits::QueryPresentation; use std::ops::Deref; -#[allow(unused)] -pub trait QueryPresentation { - fn present(query_res: Input) -> Self - where - Self: Sized; -} - pub struct Flattened(pub Vec>); impl Deref for Flattened { diff --git a/src/tree/querying/queries.rs b/src/tree/querying/queries.rs index e69de29..79ad3db 100644 --- a/src/tree/querying/queries.rs +++ b/src/tree/querying/queries.rs @@ -0,0 +1,129 @@ +use crate::tree::node::MaterializedNode; + +use crate::tree::scopes::ScopeDefinition; +use crate::tree::traits::NodeRepository; + +use crate::tree::querying::traits::{QueryPresentation, QueryStrategy}; +use std::marker::PhantomData; + +#[derive(Debug)] +pub struct QueryAllNodes(PhantomData<(Presentation, NodeType)>); + +impl>>> + QueryStrategy for QueryAllNodes +{ + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + Presentation::present(node_repo.get_all::().unwrap_or_default()) + } +} +#[derive(Debug)] +pub struct QueryNodesInScope( + PhantomData<(Scope, Presentation, NodeType)>, +); + +impl< + Scope: ScopeDefinition, + NodeType: Clone + 'static, + Presentation: QueryPresentation>>, +> QueryStrategy for QueryNodesInScope +{ + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + let query_result = node_repo + .get_nodes_in_scope::(Scope::layer()) + .unwrap_or_default(); + + Presentation::present(query_result) + } +} + +#[derive(Debug)] +pub struct QueryGroupedNodes( + PhantomData<(Scope, Presentation, NodeType)>, +); + +impl< + Scope: ScopeDefinition, + NodeType: Clone + 'static, + Presentation: QueryPresentation>>>, +> QueryStrategy for QueryGroupedNodes +{ + type Output = Presentation; + fn query(node_repo: &impl NodeRepository) -> Self::Output { + let query_result = node_repo + .get_nodes_for_scope_per_top_level_element::(Scope::layer()) + .unwrap_or_default(); + + Presentation::present(query_result) + } +} + +pub mod convenience { + use crate::tree::querying::presentation::{First, Flattened, Grouped}; + use crate::tree::querying::queries::{QueryAllNodes, QueryGroupedNodes, QueryNodesInScope}; + use phenopackets::schema::v2::Phenopacket; + + // More to be added. + pub type All = QueryAllNodes>; + pub type GroupedIndividuals = + QueryGroupedNodes>; + pub type SingleInScope = QueryNodesInScope>; +} + +mod temp_test { + #![allow(dead_code)] + #![allow(unused)] + // Testing and see how it would work from here: + + use crate::tree::querying::presentation::{First, Flattened}; + use crate::tree::querying::queries::convenience::{All, GroupedIndividuals, SingleInScope}; + use crate::tree::querying::queries::{QueryAllNodes, QueryNodesInScope}; + use crate::tree::querying::traits::QueryStrategy; + use phenopackets::schema::v2::Phenopacket; + use phenopackets::schema::v2::core::{OntologyClass, PhenotypicFeature}; + + trait TheRuleTrait { + type Query: QueryStrategy; + + fn check_erased(&'_ self, board: ::Output) -> bool; + } + + struct __RuleImplementation1; + + impl TheRuleTrait for __RuleImplementation1 { + type Query = ( + QueryNodesInScope>, + QueryAllNodes>, + ); + + fn check_erased(&'_ self, board: ::Output) -> bool { + todo!() + } + } + + struct __RuleImplementation2; + + impl TheRuleTrait for __RuleImplementation2 { + type Query = ( + All, + SingleInScope, + GroupedIndividuals, + ); + + fn check_erased(&self, board: ::Output) -> bool { + let a = board; + todo!() + } + } + + struct __RuleImplementation3; + + impl TheRuleTrait for __RuleImplementation3 { + type Query = GroupedIndividuals; + + fn check_erased(&self, board: ::Output) -> bool { + todo!() + } + } +} diff --git a/src/tree/querying/traits.rs b/src/tree/querying/traits.rs new file mode 100644 index 0000000..76b6deb --- /dev/null +++ b/src/tree/querying/traits.rs @@ -0,0 +1,46 @@ +use crate::tree::traits::NodeRepository; + +pub(crate) trait QueryStrategy { + type Output; + #[allow(unused)] + fn query(node_repo: &impl NodeRepository) -> Self::Output; +} + +macro_rules! impl_query_strategy_tuple { + () => { + impl QueryStrategy for () { + type Output = (); + fn query(_node_repo: &impl NodeRepository) -> Self::Output { + + } + } + }; + + ($head:ident $(, $tail:ident)*) => { + impl_query_strategy_tuple!($($tail),*); + + impl<$head, $($tail),*> QueryStrategy for ($head, $($tail,)*) + where + $head: QueryStrategy, + $($tail: QueryStrategy),* + { + type Output = ($head::Output, $($tail::Output,)*); + + fn query(node_repo: &impl NodeRepository) -> Self::Output { + ( + $head::query(node_repo), + $($tail::query(node_repo),)* + ) + } + } + }; +} + +impl_query_strategy_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + +#[allow(unused)] +pub(crate) trait QueryPresentation { + fn present(query_res: Input) -> Self + where + Self: Sized; +} From c297c3c45d4f50c9c90474354ede9cc8e72258d8 Mon Sep 17 00:00:00 2001 From: SmartMonkey Date: Wed, 10 Dec 2025 11:15:14 +0100 Subject: [PATCH 8/9] Fix bug --- src/tree/btree_node_repository.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tree/btree_node_repository.rs b/src/tree/btree_node_repository.rs index 436fb80..575a9d3 100644 --- a/src/tree/btree_node_repository.rs +++ b/src/tree/btree_node_repository.rs @@ -153,9 +153,7 @@ impl NodeRepository for BTreeNodeRepository { .map(|(path, entry)| self.cast_entry::(path, entry)) .collect::>, NodeRepositoryError>>()?; - if children.is_empty() { - output.push(children); - } + output.push(children); } Ok(output) From bdab9518f744490caf8f5047a52f3787b99e7b3a Mon Sep 17 00:00:00 2001 From: Rouven Reuter <49242091+SmartMonkey-git@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:59:59 +0100 Subject: [PATCH 9/9] Implement rules (#81) --- src/materializer.rs | 12 +-- src/phenolint.rs | 4 +- src/rules/curies/curie_format_rule.rs | 7 +- .../disease_consistency_rule.rs | 56 ++++++----- src/rules/resources.rs | 49 ++++++---- src/rules/rule_registry.rs | 7 +- src/rules/traits.rs | 26 +++-- src/tree/btree_node_repository.rs | 2 +- src/tree/mod.rs | 3 +- src/tree/node_repository.rs | 98 ------------------- src/tree/querying/mod.rs | 2 +- src/tree/querying/queries.rs | 3 +- src/tree/querying/traits.rs | 11 ++- src/tree/scopes.rs | 11 +-- src/tree/traits.rs | 2 +- tests/test_custom_rule.rs | 7 +- 16 files changed, 106 insertions(+), 194 deletions(-) delete mode 100644 src/tree/node_repository.rs diff --git a/src/materializer.rs b/src/materializer.rs index 89d735b..1828bb6 100644 --- a/src/materializer.rs +++ b/src/materializer.rs @@ -1,7 +1,6 @@ use crate::parsing::traits::ParsableNode; use crate::tree::node::{DynamicNode, MaterializedNode}; -use crate::tree::node_repository::NodeRepository; -use crate::tree::traits::LocatableNode; +use crate::tree::traits::{LocatableNode, NodeRepository}; use log::error; use phenopackets::schema::v2::core::{ Diagnosis, Disease, OntologyClass, PhenotypicFeature, Resource, VitalStatus, @@ -11,7 +10,7 @@ use phenopackets::schema::v2::{Cohort, Phenopacket}; pub(crate) struct NodeMaterializer; impl NodeMaterializer { - pub fn materialize_nodes(&mut self, dyn_node: &DynamicNode, repo: &mut NodeRepository) { + pub fn materialize_nodes(&mut self, dyn_node: &DynamicNode, repo: &mut impl NodeRepository) { if let Some(cohort) = Cohort::parse(dyn_node) { Self::push_to_repo(cohort, dyn_node, repo); } else if let Some(oc) = OntologyClass::parse(dyn_node) { @@ -33,12 +32,13 @@ impl NodeMaterializer { }; } - fn push_to_repo( + fn push_to_repo( materialized: NodeType, dyn_node: &DynamicNode, - board: &mut NodeRepository, + board: &mut impl NodeRepository, ) { let node = MaterializedNode::from_dynamic(materialized, dyn_node); - board.insert(node); + // TODO: Error throwing + board.insert(node).expect("Unable to insert node"); } } diff --git a/src/phenolint.rs b/src/phenolint.rs index 1a374d3..cf1d1f2 100644 --- a/src/phenolint.rs +++ b/src/phenolint.rs @@ -14,13 +14,13 @@ use crate::schema_validation::validator::PhenopacketSchemaValidator; use crate::traits::Lint; use crate::tree::abstract_pheno_tree::AbstractTreeTraversal; use crate::tree::node::DynamicNode; -use crate::tree::node_repository::NodeRepository; use crate::tree::pointer::Pointer; use log::{error, warn}; use phenopackets::schema::v2::Phenopacket; use prost::Message; use serde_json::Value; +use crate::tree::btree_node_repository::BTreeNodeRepository; use std::fs; use std::path::PathBuf; @@ -71,7 +71,7 @@ impl Lint for Phenolint { let root_node = DynamicNode::new(&values, &spans, Pointer::at_root()); let apt = AbstractTreeTraversal::new(values, spans); - let mut node_repo: NodeRepository = NodeRepository::new(); + let mut node_repo = BTreeNodeRepository::new(); for node in apt.traverse() { self.node_materializer diff --git a/src/rules/curies/curie_format_rule.rs b/src/rules/curies/curie_format_rule.rs index a5e3a3c..41bd08d 100644 --- a/src/rules/curies/curie_format_rule.rs +++ b/src/rules/curies/curie_format_rule.rs @@ -10,7 +10,8 @@ use crate::report::traits::{CompileReport, RegisterableReport, ReportFromContext use crate::rules::rule_registration::RuleRegistration; use crate::rules::traits::RuleMetaData; use crate::rules::traits::{LintRule, RuleCheck, RuleFromContext}; -use crate::tree::node_repository::List; +use crate::tree::querying::presentation::Flattened; +use crate::tree::querying::queries::convenience::All; use crate::tree::traits::{LocatableNode, Node}; use phenolint_macros::{register_report, register_rule}; use phenopackets::schema::v2::core::OntologyClass; @@ -38,9 +39,9 @@ impl RuleFromContext for CurieFormatRule { } impl RuleCheck for CurieFormatRule { - type Data<'a> = List<'a, OntologyClass>; + type Query = All; - fn check(&self, data: Self::Data<'_>) -> Vec { + fn check(&self, data: Flattened) -> Vec { let mut violations = vec![]; for node in data.0.iter() { diff --git a/src/rules/interpretation/disease_consistency_rule.rs b/src/rules/interpretation/disease_consistency_rule.rs index cd2eafe..5a6a763 100644 --- a/src/rules/interpretation/disease_consistency_rule.rs +++ b/src/rules/interpretation/disease_consistency_rule.rs @@ -14,8 +14,9 @@ use crate::report::traits::{CompileReport, RegisterableReport, ReportFromContext use crate::rules::rule_registration::RuleRegistration; use crate::rules::traits::RuleMetaData; use crate::rules::traits::{LintRule, RuleCheck, RuleFromContext}; -use crate::tree::node_repository::List; use crate::tree::pointer::Pointer; +use crate::tree::querying::presentation::Grouped; +use crate::tree::querying::queries::convenience::GroupedIndividuals; use crate::tree::traits::{LocatableNode, Node}; use phenolint_macros::{register_patch, register_report, register_rule}; use phenopackets::schema::v2::core::{Diagnosis, Disease, OntologyClass}; @@ -38,34 +39,37 @@ impl RuleFromContext for DiseaseConsistencyRule { } impl RuleCheck for DiseaseConsistencyRule { - type Data<'a> = (List<'a, Diagnosis>, List<'a, Disease>); + type Query = (GroupedIndividuals, GroupedIndividuals); - fn check(&self, data: Self::Data<'_>) -> Vec { + fn check(&self, data: (Grouped, Grouped)) -> Vec { let mut violations = vec![]; - let disease_terms: Vec<(&str, &str)> = data - .1 - .iter() - .filter_map(|disease| { - disease - .inner - .term - .as_ref() - .map(|oc| (oc.id.as_str(), oc.label.as_str())) - }) - .collect(); - - for diagnosis in data.0.iter() { - if let Some(oc) = &diagnosis.inner.disease - && !disease_terms.contains(&(oc.id.as_str(), oc.label.as_str())) - { - violations.push(LintViolation::new( - ViolationSeverity::Warning, - LintRule::rule_id(self), - NonEmptyVec::with_single_entry( - diagnosis.pointer().clone().down("disease").clone(), - ), - )) + let (diagnosis_groups, diseases_group) = data; + + for (diagnosis, diseases) in diagnosis_groups.0.iter().zip(diseases_group.0) { + let disease_terms: Vec<(&str, &str)> = diseases + .iter() + .filter_map(|disease| { + disease + .inner + .term + .as_ref() + .map(|oc| (oc.id.as_str(), oc.label.as_str())) + }) + .collect(); + + for diagnosis in diagnosis.iter() { + if let Some(oc) = &diagnosis.inner.disease + && !disease_terms.contains(&(oc.id.as_str(), oc.label.as_str())) + { + violations.push(LintViolation::new( + ViolationSeverity::Warning, + LintRule::rule_id(self), + NonEmptyVec::with_single_entry( + diagnosis.pointer().clone().down("disease").clone(), + ), + )) + } } } diff --git a/src/rules/resources.rs b/src/rules/resources.rs index 315498a..a7a3d46 100644 --- a/src/rules/resources.rs +++ b/src/rules/resources.rs @@ -8,8 +8,9 @@ use crate::report::traits::RuleReport; use crate::report::traits::{CompileReport, RegisterableReport, ReportFromContext}; use crate::rules::rule_registration::RuleRegistration; use crate::rules::traits::{LintRule, RuleCheck, RuleFromContext, RuleMetaData}; -use crate::tree::node_repository::List; use crate::tree::pointer::Pointer; +use crate::tree::querying::presentation::Grouped; +use crate::tree::querying::queries::convenience::GroupedIndividuals; use crate::tree::traits::{LocatableNode, Node}; use phenolint_macros::{register_report, register_rule}; use phenopackets::schema::v2::core::{OntologyClass, Resource}; @@ -35,28 +36,34 @@ impl RuleFromContext for CuriesHaveResourcesRule { } impl RuleCheck for CuriesHaveResourcesRule { - type Data<'a> = (List<'a, OntologyClass>, List<'a, Resource>); - - fn check(&self, data: Self::Data<'_>) -> Vec { - let known_prefixes: HashSet<_> = data - .1 - .iter() - .map(|r| r.inner.namespace_prefix.as_str()) - .collect(); + type Query = ( + GroupedIndividuals, + GroupedIndividuals, + ); + fn check(&self, data: (Grouped, Grouped)) -> Vec { + let (ontology_classes, resources) = data; let mut violations = vec![]; - for node in data.0.iter() { - if let Some(prefix) = find_prefix(node.inner.id.as_str()) - && !known_prefixes.contains(prefix) - { - violations.push(LintViolation::new( - ViolationSeverity::Error, - LintRule::rule_id(self), - node.pointer().clone().into(), // <- warns about the ontology class itself - )); + for (o, r) in ontology_classes.0.iter().zip(resources.0) { + let known_prefixes: HashSet<_> = r + .iter() + .map(|r| r.inner.namespace_prefix.as_str()) + .collect(); + + for node in o.iter() { + if let Some(prefix) = find_prefix(node.inner.id.as_str()) + && !known_prefixes.contains(prefix) + { + violations.push(LintViolation::new( + ViolationSeverity::Error, + LintRule::rule_id(self), + node.pointer().clone().into(), // <- warns about the ontology class itself + )); + } } } + violations } } @@ -66,8 +73,8 @@ mod test_curies_have_resources { use crate::rules::resources::CuriesHaveResourcesRule; use crate::rules::traits::{RuleCheck, RuleMetaData}; use crate::tree::node::MaterializedNode; - use crate::tree::node_repository::List; use crate::tree::pointer::Pointer; + use crate::tree::querying::presentation::Grouped; use phenopackets::schema::v2::core::OntologyClass; #[test] @@ -82,8 +89,8 @@ mod test_curies_have_resources { Default::default(), Pointer::new("/phenotypicFeatures/0/type"), )]; - let resources = []; - let data = (List(&ocs), List(&resources)); + let resources = vec![vec![]]; + let data = (Grouped(vec![ocs.to_vec()]), Grouped(resources)); let violations = rule.check(data); diff --git a/src/rules/rule_registry.rs b/src/rules/rule_registry.rs index 9bd3534..aa1296f 100644 --- a/src/rules/rule_registry.rs +++ b/src/rules/rule_registry.rs @@ -80,7 +80,8 @@ mod tests { use crate::rules::traits::RuleCheck; use crate::rules::traits::RuleFromContext; use crate::rules::traits::RuleMetaData; - use crate::tree::node_repository::List; + use crate::tree::querying::presentation::Flattened; + use crate::tree::querying::queries::convenience::All; use phenolint_macros::register_rule; use phenopackets::schema::v2::core::OntologyClass; use rstest::rstest; @@ -100,9 +101,9 @@ mod tests { } } impl RuleCheck for TestRule { - type Data<'a> = List<'a, OntologyClass>; + type Query = All; - fn check(&self, _: Self::Data<'_>) -> Vec { + fn check(&self, _: Flattened) -> Vec { todo!() } } diff --git a/src/rules/traits.rs b/src/rules/traits.rs index 97173cb..656729b 100644 --- a/src/rules/traits.rs +++ b/src/rules/traits.rs @@ -1,15 +1,17 @@ use crate::LinterContext; use crate::diagnostics::LintViolation; use crate::error::FromContextError; -use crate::tree::node_repository::NodeRepository; +use crate::tree::btree_node_repository::BTreeNodeRepository; +use crate::tree::querying::traits::QueryStrategy; -pub trait LintRule: RuleFromContext + Send + Sync { +pub trait LintRule: Send + Sync { fn rule_id(&self) -> &str; - fn check_erased(&self, board: &NodeRepository) -> Vec; + // Needs to be concrete type, because NodeRepository trait is not dyn compatible :( + fn check_erased(&self, board: &BTreeNodeRepository) -> Vec; } -pub trait RuleMetaData: Send + Sync { +pub trait RuleMetaData { fn rule_id(&self) -> &str; } @@ -20,28 +22,22 @@ pub trait RuleFromContext { } pub trait RuleCheck: Send + Sync + 'static { - type Data<'a>: LintData<'a> + ?Sized; - fn check(&self, data: Self::Data<'_>) -> Vec; + type Query: QueryStrategy; + fn check(&self, data: ::Output) -> Vec; } impl LintRule for T where T: RuleCheck + RuleFromContext + RuleMetaData, - for<'a> ::Data<'a>: Sized, + for<'a> ::Query: Sized, { fn rule_id(&self) -> &str { self.rule_id() } - fn check_erased(&self, board: &NodeRepository) -> Vec { - let data = ::Data::fetch(board); + fn check_erased(&self, board: &BTreeNodeRepository) -> Vec { + let data = ::Query::query(board); self.check(data) } } - -pub trait LintData<'a> { - fn fetch(board: &'a NodeRepository) -> Self - where - Self: Sized; -} diff --git a/src/tree/btree_node_repository.rs b/src/tree/btree_node_repository.rs index 575a9d3..dcf31a2 100644 --- a/src/tree/btree_node_repository.rs +++ b/src/tree/btree_node_repository.rs @@ -15,7 +15,7 @@ struct NodeEntry { inner: Box, } -pub(crate) struct BTreeNodeRepository { +pub struct BTreeNodeRepository { node_store: BTreeMap, span_store: BTreeMap>, scope_mappings: ScopeMappings, diff --git a/src/tree/mod.rs b/src/tree/mod.rs index fe93e4c..7b8dd6f 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -2,9 +2,8 @@ pub(crate) mod abstract_pheno_tree; pub(crate) mod btree_node_repository; mod error; pub mod node; -pub mod node_repository; pub mod pointer; -mod querying; +pub mod querying; mod scopes; pub mod traits; pub(crate) mod utils; diff --git a/src/tree/node_repository.rs b/src/tree/node_repository.rs deleted file mode 100644 index d1eeac2..0000000 --- a/src/tree/node_repository.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::rules::traits::LintData; -use crate::tree::node::MaterializedNode; -use crate::tree::pointer::Pointer; -use crate::tree::traits::LocatableNode; - -use std::any::{Any, TypeId}; -use std::collections::HashMap; -use std::ops::Deref; - -#[derive(Default)] -pub struct NodeRepository { - board: HashMap>, -} - -impl NodeRepository { - pub fn new() -> NodeRepository { - NodeRepository { - board: HashMap::new(), - } - } - - fn get_raw(&self) -> &[MaterializedNode] { - self.board - .get(&TypeId::of::()) - .and_then(|b| b.downcast_ref::>>()) - .map(|v| v.as_slice()) - .unwrap_or(&[]) - } - - pub fn insert(&mut self, node: MaterializedNode) { - self.board - .entry(TypeId::of::()) - .or_insert_with(|| Box::new(Vec::>::new())) - .downcast_mut::>>() - .unwrap() - .push(node); - } - - pub fn node_by_pointer(&self, ptr: &Pointer) -> Option<&MaterializedNode> { - for nodes in self.board.values() { - let casted_node = nodes - .downcast_ref::>>() - .expect("Should be downcastable"); - - for node in casted_node.iter() { - if node.pointer() == ptr { - return Some(node); - } - } - } - None - } -} - -pub struct Single<'a, T: 'static>(pub Option<&'a MaterializedNode>); - -impl<'a, T> LintData<'a> for Single<'a, T> { - fn fetch(board: &'a NodeRepository) -> Self { - Single(board.get_raw::().first()) - } -} - -pub struct List<'a, T: 'static>(pub &'a [MaterializedNode]); - -impl<'a, T> Deref for List<'a, T> { - type Target = &'a [MaterializedNode]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a, T> LintData<'a> for List<'a, T> { - fn fetch(board: &'a NodeRepository) -> Self { - List(board.get_raw()) - } -} - -impl<'a, A, B> LintData<'a> for (A, B) -where - A: LintData<'a>, - B: LintData<'a>, -{ - fn fetch(board: &'a NodeRepository) -> Self { - (A::fetch(board), B::fetch(board)) - } -} - -impl<'a, A, B, C> LintData<'a> for (A, B, C) -where - A: LintData<'a>, - B: LintData<'a>, - C: LintData<'a>, -{ - fn fetch(board: &'a NodeRepository) -> Self { - (A::fetch(board), B::fetch(board), C::fetch(board)) - } -} diff --git a/src/tree/querying/mod.rs b/src/tree/querying/mod.rs index 436e089..6bf3045 100644 --- a/src/tree/querying/mod.rs +++ b/src/tree/querying/mod.rs @@ -1,3 +1,3 @@ pub mod presentation; pub mod queries; -mod traits; +pub mod traits; diff --git a/src/tree/querying/queries.rs b/src/tree/querying/queries.rs index 79ad3db..b7e91b0 100644 --- a/src/tree/querying/queries.rs +++ b/src/tree/querying/queries.rs @@ -1,9 +1,8 @@ use crate::tree::node::MaterializedNode; -use crate::tree::scopes::ScopeDefinition; use crate::tree::traits::NodeRepository; -use crate::tree::querying::traits::{QueryPresentation, QueryStrategy}; +use crate::tree::querying::traits::{QueryPresentation, QueryStrategy, ScopeDefinition}; use std::marker::PhantomData; #[derive(Debug)] diff --git a/src/tree/querying/traits.rs b/src/tree/querying/traits.rs index 76b6deb..3bfbc14 100644 --- a/src/tree/querying/traits.rs +++ b/src/tree/querying/traits.rs @@ -1,6 +1,7 @@ +use crate::tree::scopes::ScopeLayer; use crate::tree::traits::NodeRepository; -pub(crate) trait QueryStrategy { +pub trait QueryStrategy { type Output; #[allow(unused)] fn query(node_repo: &impl NodeRepository) -> Self::Output; @@ -44,3 +45,11 @@ pub(crate) trait QueryPresentation { where Self: Sized; } + +pub trait ScopeDefinition { + fn layer() -> ScopeLayer; + + fn partitioning_fields() -> &'static [&'static str] { + &[] + } +} diff --git a/src/tree/scopes.rs b/src/tree/scopes.rs index e5d80c0..663215d 100644 --- a/src/tree/scopes.rs +++ b/src/tree/scopes.rs @@ -1,23 +1,16 @@ use crate::tree::pointer::Pointer; +use crate::tree::querying::traits::ScopeDefinition; use phenopackets::schema::v2::{Cohort, Family, Phenopacket}; use std::any::TypeId; use std::cell::Cell; use std::collections::HashMap; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) enum ScopeLayer { +pub enum ScopeLayer { Individual = 0, Aggregated = 1, } -pub(crate) trait ScopeDefinition { - fn layer() -> ScopeLayer; - - fn partitioning_fields() -> &'static [&'static str] { - &[] - } -} - impl ScopeDefinition for Phenopacket { fn layer() -> ScopeLayer { ScopeLayer::Individual diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 1a3fc7e..8d9e203 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -20,7 +20,7 @@ pub trait RetrievableNode { fn value_at(&self, ptr: &Pointer) -> Option>; } -pub(crate) trait NodeRepository { +pub trait NodeRepository { fn insert( &mut self, node: MaterializedNode, diff --git a/tests/test_custom_rule.rs b/tests/test_custom_rule.rs index 297f386..d7fc003 100644 --- a/tests/test_custom_rule.rs +++ b/tests/test_custom_rule.rs @@ -18,8 +18,9 @@ use phenolint::report::specs::{LabelSpecs, ReportSpecs}; use phenolint::report::traits::{CompileReport, RegisterableReport, ReportFromContext, RuleReport}; use phenolint::rules::traits::LintRule; use phenolint::rules::traits::{RuleCheck, RuleFromContext}; -use phenolint::tree::node_repository::List; use phenolint::tree::pointer::Pointer; +use phenolint::tree::querying::presentation::Flattened; +use phenolint::tree::querying::queries::convenience::All; use phenolint::tree::traits::Node; use phenolint_macros::{register_patch, register_report, register_rule}; use phenopackets::schema::v2::Phenopacket; @@ -43,9 +44,9 @@ impl RuleFromContext for CustomRule { } impl RuleCheck for CustomRule { - type Data<'a> = List<'a, OntologyClass>; + type Query = All; - fn check(&self, _: Self::Data<'_>) -> Vec { + fn check(&self, _: Flattened) -> Vec { vec![LintViolation::new( ViolationSeverity::Info, LintRule::rule_id(self),