diff --git a/Cargo.toml b/Cargo.toml index 0aea780c6..2cf882e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,7 @@ name = "tree_traversals" [[example]] name = "forward_simulation" + +[[example]] +name = "metadata_schema" +required-features = ["derive"] diff --git a/examples/metadata_schema.rs b/examples/metadata_schema.rs new file mode 100644 index 000000000..4a92b1451 --- /dev/null +++ b/examples/metadata_schema.rs @@ -0,0 +1,29 @@ +use tskit::prelude::*; +use tskit::TableCollection; + +#[derive(serde::Serialize, serde::Deserialize, tskit::metadata::PopulationMetadata)] +#[serializer("serde_json")] +struct PopulationMetadata { + name: String, +} + +fn main() { + let from_fp11 = r#" + { + "codec": "json", + "type": "object", + "name": "Population metadata", + "properties": {"name": {"type": "string"}} + }"#; + + let mut tables = TableCollection::new(10.0).unwrap(); + tables + .add_population_with_metadata(&PopulationMetadata { + name: "YRB".to_string(), + }) + .unwrap(); + tables + .set_json_metadata_schema_from_str(tskit::MetadataSchema::Populations, from_fp11) + .unwrap(); + tables.dump("testit.trees", 0).unwrap(); +} diff --git a/python_scripts/read_tablesfile.py b/python_scripts/read_tablesfile.py new file mode 100644 index 000000000..8ed3b6838 --- /dev/null +++ b/python_scripts/read_tablesfile.py @@ -0,0 +1,7 @@ +import tskit +import sys + +for f in sys.argv[1:]: + tables = tskit.TableCollection.load(f) + for pop in tables.populations: + print(pop) diff --git a/python_scripts/requirements.txt b/python_scripts/requirements.txt new file mode 100644 index 000000000..72b8dd4e2 --- /dev/null +++ b/python_scripts/requirements.txt @@ -0,0 +1 @@ +tskit = "0.5.1" diff --git a/src/lib.rs b/src/lib.rs index ba670ee8c..e8fcf7ed6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -435,7 +435,7 @@ pub use mutation_table::{MutationTable, MutationTableRow, OwnedMutationTable}; pub use node_table::{NodeTable, NodeTableRow, OwnedNodeTable}; pub use population_table::{OwnedPopulationTable, PopulationTable, PopulationTableRow}; pub use site_table::{OwnedSiteTable, SiteTable, SiteTableRow}; -pub use table_collection::TableCollection; +pub use table_collection::{MetadataSchema, TableCollection}; pub use traits::IndividualLocation; pub use traits::IndividualParents; pub use traits::NodeListGenerator; diff --git a/src/table_collection.rs b/src/table_collection.rs index fb54588e3..724fb23e2 100644 --- a/src/table_collection.rs +++ b/src/table_collection.rs @@ -24,9 +24,23 @@ use crate::TskReturnValue; use crate::TskitTypeAccess; use crate::{tsk_id_t, tsk_size_t}; use crate::{EdgeId, NodeId}; +use libc::{c_char, strlen}; use ll_bindings::tsk_table_collection_free; use mbox::MBox; +pub enum MetadataSchema { + Toplevel, + Edges, + Nodes, + Sites, + Mutations, + Individuals, + Populations, + Migrations, + #[cfg(feature = "provenance")] + Provenance, +} + /// A table collection. /// /// This is a thin wrapper around the C type @@ -1202,6 +1216,35 @@ impl TableCollection { }; handle_tsk_return_value!(rv) } + + /// Set a metadata schema + /// + /// # Examples + /// + pub fn set_json_metadata_schema_from_str( + &mut self, + level: MetadataSchema, + schema: impl AsRef, + ) -> TskReturnValue { + println!("{} {}", schema.as_ref(), schema.as_ref().len()); + let cstr = std::ffi::CString::new(schema.as_ref()).unwrap(); + println!("{:?}", cstr); + let len = unsafe { strlen(cstr.as_bytes_with_nul().as_ptr() as *const c_char) }; + println!("{:?}", cstr); + println!("{}", len); + let rv = match level { + MetadataSchema::Populations => unsafe { + ll_bindings::tsk_population_table_set_metadata_schema( + &mut (*self.inner).populations, + cstr.as_bytes_with_nul().as_ptr() as *const c_char, + len.try_into().unwrap(), + ) + }, + _ => unimplemented!("haven't done it yet"), + }; + println!("rv = {}", rv); + handle_tsk_return_value!(rv) + } } impl TableAccess for TableCollection { @@ -2296,3 +2339,43 @@ mod test_adding_migrations { } } } + +#[cfg(test)] +mod test_metadata_schema { + use super::*; + + #[test] + fn population_metadata_schema() { + let json_schema3 = r#"{"codec":"json","type":"object","name":"Population metadata","properties:"{"name":{"type":"string"}}}"#; + let json_schema3 = r#"{"codec":"json","type":"object","name":"Population metadata","properties":{"name":{"type":"string"}}}"#; + let json_schema2 = r#"{"codec":"json","name":"Population metadata","properties":{"name":{"type":"string"}},"type":"object"}"#; + let json_schema = r#"{"codec":"json","name":"Population metadata","properties":{"name":{"type":"string"}},"type":"object"}"#; + + // YOU CANNOT HAVE A TRAILING COMMA AT THE END!!!!!! + let from_fp11 = r#" + { + "codec": "json", + "type": "object", + "name": "Population metadata", + "properties": {"name": {"type": "string"}} + }"#; + + assert_eq!(json_schema, json_schema2); + let mut tables = TableCollection::new(10.).unwrap(); + assert!(tables + .set_json_metadata_schema_from_str(MetadataSchema::Populations, from_fp11) + .is_ok()); + assert!(!unsafe { (*tables.as_ptr()).populations.metadata_schema.is_null() }); + let len = unsafe { (*tables.as_ptr()).populations.metadata_schema_length }; + assert!(len > 0, "{}", len); + let schema = + unsafe { std::ffi::CStr::from_ptr((*tables.as_ptr()).populations.metadata_schema) }; + assert_eq!(schema.to_str().unwrap(), from_fp11); + tables.dump("foo.trees", 0).unwrap(); + + //let tables = TableCollection::new_from_file("bananas.tables").unwrap(); + //let schema = + // unsafe { std::ffi::CStr::from_ptr((*tables.as_ptr()).populations.metadata_schema) }; + //println!("from tskit = {}", schema.to_str().unwrap()); + } +}