diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e090946e..206c1b28 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -83,7 +83,7 @@ jobs: strategy: matrix: rust: - - 1.71.0 + - 1.75.0 steps: - uses: actions/checkout@v4.2.2 - uses: dtolnay/rust-toolchain@v1 diff --git a/Cargo.toml b/Cargo.toml index 297afd9b..968fcf22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ description = "rust interface to tskit" license = "MIT" homepage = "https://github.com/tskit-dev/tskit-rust" repository = "https://github.com/tskit-dev/tskit-rust" -rust-version = "1.71.0" +rust-version = "1.75.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lints.rust] diff --git a/src/lib.rs b/src/lib.rs index 22c96307..1d5da50a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,9 @@ pub use sys::flags::*; pub use table_collection::TableCollection; pub use traits::IndividualLocation; pub use traits::IndividualParents; +pub use traits::ObjectSafeTableIteration; +pub use traits::TableAccess; +pub use traits::TableIteration; pub use tree_interface::{NodeTraversalOrder, TreeInterface}; pub use trees::{Tree, TreeSequence}; diff --git a/src/prelude.rs b/src/prelude.rs index 9392aa33..f4d6ec63 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,7 @@ //! Export commonly-use types and traits +pub use crate::TableAccess; +pub use crate::TableIteration; pub use streaming_iterator::DoubleEndedStreamingIterator; pub use streaming_iterator::StreamingIterator; pub use { diff --git a/src/traits.rs b/src/traits.rs index fbc03532..53617409 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -119,3 +119,164 @@ impl_individual_parents!( ); impl_individual_parents!(N, usize, &[crate::IndividualId; N], self, self.as_slice()); impl_individual_parents!(N, usize, [crate::IndividualId; N], self, self.as_slice()); + +pub trait TableAccess { + fn edges(&self) -> &crate::EdgeTable; + fn nodes(&self) -> &crate::NodeTable; + fn sites(&self) -> &crate::SiteTable; + fn mutations(&self) -> &crate::MutationTable; + fn migrations(&self) -> &crate::MigrationTable; + fn populations(&self) -> &crate::PopulationTable; +} + +pub trait TableIteration: TableAccess { + fn edges_iter(&self) -> impl Iterator + '_ { + self.edges().iter() + } + fn nodes_iter(&self) -> impl Iterator + '_ { + self.nodes().iter() + } + fn sites_iter(&self) -> impl Iterator + '_ { + self.sites().iter() + } + fn mutations_iter(&self) -> impl Iterator + '_ { + self.mutations().iter() + } + fn migrations_iter(&self) -> impl Iterator + '_ { + self.migrations().iter() + } + fn populations_iter(&self) -> impl Iterator + '_ { + self.populations().iter() + } +} + +pub trait ObjectSafeTableIteration: TableAccess { + fn edges_iter(&self) -> Box + '_> { + Box::new(self.edges().iter()) + } + fn nodes_iter(&self) -> Box + '_> { + Box::new(self.nodes().iter()) + } + fn sites_iter(&self) -> Box + '_> { + Box::new(self.sites().iter()) + } + fn mutations_iter(&self) -> Box + '_> { + Box::new(self.mutations().iter()) + } + fn migrations_iter(&self) -> Box + '_> { + Box::new(self.migrations().iter()) + } + fn populations_iter(&self) -> Box + '_> { + Box::new(Box::new(self.populations().iter())) + } +} + +impl TableAccess for crate::TableCollection { + fn edges(&self) -> &crate::EdgeTable { + self.edges() + } + + fn nodes(&self) -> &crate::NodeTable { + self.nodes() + } + + fn sites(&self) -> &crate::SiteTable { + self.sites() + } + + fn mutations(&self) -> &crate::MutationTable { + self.mutations() + } + + fn migrations(&self) -> &crate::MigrationTable { + self.migrations() + } + + fn populations(&self) -> &crate::PopulationTable { + self.populations() + } +} + +impl TableAccess for crate::TreeSequence { + fn edges(&self) -> &crate::EdgeTable { + self.tables().edges() + } + + fn nodes(&self) -> &crate::NodeTable { + self.tables().nodes() + } + + fn sites(&self) -> &crate::SiteTable { + self.tables().sites() + } + + fn mutations(&self) -> &crate::MutationTable { + self.tables().mutations() + } + + fn migrations(&self) -> &crate::MigrationTable { + self.tables().migrations() + } + + fn populations(&self) -> &crate::PopulationTable { + self.tables().populations() + } +} + +impl TableAccess for &T +where + T: TableAccess, +{ + fn migrations(&self) -> &crate::MigrationTable { + T::migrations(self) + } + + fn mutations(&self) -> &crate::MutationTable { + T::mutations(self) + } + + fn edges(&self) -> &crate::EdgeTable { + T::edges(self) + } + fn sites(&self) -> &crate::SiteTable { + T::sites(self) + } + fn nodes(&self) -> &crate::NodeTable { + T::nodes(self) + } + fn populations(&self) -> &crate::PopulationTable { + T::populations(self) + } +} + +impl TableAccess for Box +where + T: TableAccess, +{ + fn migrations(&self) -> &crate::MigrationTable { + self.as_ref().migrations() + } + + fn edges(&self) -> &crate::EdgeTable { + self.as_ref().edges() + } + + fn nodes(&self) -> &crate::NodeTable { + self.as_ref().nodes() + } + + fn sites(&self) -> &crate::SiteTable { + self.as_ref().sites() + } + + fn mutations(&self) -> &crate::MutationTable { + self.as_ref().mutations() + } + + fn populations(&self) -> &crate::PopulationTable { + self.as_ref().populations() + } +} + +impl TableIteration for T where T: TableAccess + ?Sized {} +impl ObjectSafeTableIteration for T where T: TableAccess + ?Sized {} diff --git a/tests/test_table_traits.rs b/tests/test_table_traits.rs new file mode 100644 index 00000000..19a31c2d --- /dev/null +++ b/tests/test_table_traits.rs @@ -0,0 +1,265 @@ +use tskit::prelude::*; + +#[derive(PartialEq, Debug)] +struct IteratorOutput { + edges: Vec, + nodes: Vec, + sites: Vec, + mutations: Vec, + migrations: Vec, + populations: Vec, +} + +impl IteratorOutput { + fn new_from_tables(tables: &tskit::TableCollection) -> Self { + let edges = tables.edges().iter().collect::>(); + let nodes = tables.nodes().iter().collect::>(); + let sites = tables.sites().iter().collect::>(); + let mutations = tables.mutations().iter().collect::>(); + let populations = tables.populations().iter().collect::>(); + let migrations = tables.migrations().iter().collect::>(); + Self { + edges, + nodes, + sites, + mutations, + populations, + migrations, + } + } + + fn new_from_treeseq(treeseq: &tskit::TreeSequence) -> Self { + let edges = treeseq.tables().edges().iter().collect::>(); + let nodes = treeseq.tables().nodes().iter().collect::>(); + let sites = treeseq.tables().sites().iter().collect::>(); + let mutations = treeseq.tables().mutations().iter().collect::>(); + let populations = treeseq.tables().populations().iter().collect::>(); + let migrations = treeseq.tables().migrations().iter().collect::>(); + Self { + edges, + nodes, + sites, + mutations, + populations, + migrations, + } + } + + fn new_from_table_access(access: &T) -> Self + where + T: tskit::TableAccess, + { + let edges = access.edges().iter().collect::>(); + let nodes = access.nodes().iter().collect::>(); + let sites = access.sites().iter().collect::>(); + let mutations = access.mutations().iter().collect::>(); + let populations = access.populations().iter().collect::>(); + let migrations = access.migrations().iter().collect::>(); + Self { + edges, + nodes, + sites, + mutations, + populations, + migrations, + } + } + + fn new_from_table_access_impl_syntax(access: impl TableAccess) -> Self { + Self::new_from_table_access(&access) + } + + fn new_from_table_iteration(iterator: &T) -> Self + where + T: TableIteration, + { + let edges = iterator.edges_iter().collect::>(); + let nodes = iterator.nodes_iter().collect::>(); + let sites = iterator.sites_iter().collect::>(); + let mutations = iterator.mutations_iter().collect::>(); + let populations = iterator.populations_iter().collect::>(); + let migrations = iterator.migrations_iter().collect::>(); + Self { + edges, + nodes, + sites, + mutations, + populations, + migrations, + } + } + + fn new_from_dyn(dynamic: &dyn tskit::ObjectSafeTableIteration) -> Self { + let edges_iter: Box + '_> = dynamic.edges_iter(); + let edges = edges_iter.collect::>(); + let nodes = dynamic.nodes_iter().collect::>(); + let sites = dynamic.sites_iter().collect::>(); + let mutations = dynamic.mutations_iter().collect::>(); + let populations = dynamic.populations_iter().collect::>(); + let migrations = dynamic.migrations_iter().collect::>(); + Self { + edges, + nodes, + sites, + mutations, + populations, + migrations, + } + } +} + +fn validate_output_from_tables(tables: tskit::TableCollection) { + let tables_output = IteratorOutput::new_from_tables(&tables); + { + let access_output = IteratorOutput::new_from_table_access(&tables); + assert_eq!(tables_output, access_output); + } + { + let iteration_output = IteratorOutput::new_from_table_iteration(&tables); + assert_eq!(tables_output, iteration_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(&tables); + assert_eq!(tables_output, impl_syntax_output); + } + let boxed = Box::new(tables); + { + let dynamic_output = IteratorOutput::new_from_dyn(&boxed); + assert_eq!(tables_output, dynamic_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(&boxed); + assert_eq!(tables_output, impl_syntax_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(boxed); + assert_eq!(tables_output, impl_syntax_output); + } +} + +fn validate_output_from_table_ref(tables: tskit::TableCollection) { + let tref = &tables; + let tables_output = IteratorOutput::new_from_tables(tref); + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(tref); + assert_eq!(tables_output, impl_syntax_output); + } + { + let iteration_output = IteratorOutput::new_from_table_iteration(tref); + assert_eq!(tables_output, iteration_output); + } + let boxed = Box::new(tref); + { + let dynamic_output = IteratorOutput::new_from_dyn(&boxed); + assert_eq!(tables_output, dynamic_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(&boxed); + assert_eq!(tables_output, impl_syntax_output); + } +} + +fn validate_output_from_treeseq(treeseq: tskit::TreeSequence) { + let treeseq_output = IteratorOutput::new_from_treeseq(&treeseq); + { + let access_output = IteratorOutput::new_from_table_access(&treeseq); + assert_eq!(treeseq_output, access_output); + } + { + let iteration_output = IteratorOutput::new_from_table_iteration(&treeseq); + assert_eq!(treeseq_output, iteration_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(&treeseq); + assert_eq!(treeseq_output, impl_syntax_output); + } + let boxed = Box::new(treeseq); + { + let dynamic_output = IteratorOutput::new_from_dyn(&boxed); + assert_eq!(treeseq_output, dynamic_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(boxed); + assert_eq!(treeseq_output, impl_syntax_output); + } +} + +fn validate_output_from_treeseq_ref(treeseq: tskit::TreeSequence) { + let treeseq_ref = &treeseq; + let treeseq_output = IteratorOutput::new_from_treeseq(treeseq_ref); + { + let access_output = IteratorOutput::new_from_table_access(treeseq_ref); + assert_eq!(treeseq_output, access_output); + } + { + let iteration_output = IteratorOutput::new_from_table_iteration(treeseq_ref); + assert_eq!(treeseq_output, iteration_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(treeseq_ref); + assert_eq!(treeseq_output, impl_syntax_output); + } + let boxed = Box::new(treeseq_ref); + { + let dynamic_output = IteratorOutput::new_from_dyn(&boxed); + assert_eq!(treeseq_output, dynamic_output); + } + { + let impl_syntax_output = IteratorOutput::new_from_table_access_impl_syntax(boxed); + assert_eq!(treeseq_output, impl_syntax_output); + } +} + +fn make_tables() -> tskit::TableCollection { + let mut tables = tskit::TableCollection::new(100.).unwrap(); + let pop0 = tables.add_population().unwrap(); + let pop1 = tables.add_population().unwrap(); + tables + .add_node(tskit::NodeFlags::default(), 0.0, pop1, -1) + .unwrap(); + tables + .add_node(tskit::NodeFlags::default(), 1.0, pop0, -1) + .unwrap(); + tables + .add_node(tskit::NodeFlags::default(), 1.0, pop1, -1) + .unwrap(); + tables.add_edge(0., 50., 1, 0).unwrap(); + tables.add_edge(50., 100., 2, 0).unwrap(); + let site = tables.add_site(0.25, None).unwrap(); + tables.add_mutation(site, 1, -1, 2.0, None).unwrap(); + tables +} + +#[test] +fn test_traits_with_table_collection() { + let tables = make_tables(); + validate_output_from_tables(tables) +} + +#[test] +fn test_traits_with_table_collection_ref() { + let tables = make_tables(); + validate_output_from_table_ref(tables) +} + +#[test] +fn test_traits_with_tree_sequence() { + let mut tables = make_tables(); + tables + .full_sort(tskit::TableSortOptions::default()) + .unwrap(); + tables.build_index().unwrap(); + let treeseq = tskit::TreeSequence::try_from(tables).unwrap(); + validate_output_from_treeseq(treeseq) +} + +#[test] +fn test_traits_with_tree_sequence_ref() { + let mut tables = make_tables(); + tables + .full_sort(tskit::TableSortOptions::default()) + .unwrap(); + tables.build_index().unwrap(); + let treeseq = tskit::TreeSequence::try_from(tables).unwrap(); + validate_output_from_treeseq_ref(treeseq) +}