Skip to content

Commit a2c41af

Browse files
authored
test: metadata without serde (#753)
1 parent 1227ac8 commit a2c41af

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,6 @@ required-features = ["derive"]
6464
[[example]]
6565
name = "bincode_metadata"
6666
required-features = ["derive"]
67+
68+
[[example]]
69+
name = "manual_metadata_encoding"

examples/manual_metadata_encoding.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use core::str;
2+
3+
use tskit::metadata::MetadataRoundtrip;
4+
5+
struct MutationMetadata {
6+
effect_size: f64,
7+
dominance: f64,
8+
}
9+
10+
impl MetadataRoundtrip for MutationMetadata {
11+
fn encode(&self) -> Result<Vec<u8>, tskit::metadata::MetadataError> {
12+
let mut rv = vec![];
13+
rv.extend_from_slice(&self.effect_size.to_le_bytes());
14+
rv.extend_from_slice(&self.dominance.to_le_bytes());
15+
Ok(rv)
16+
}
17+
18+
fn decode(md: &[u8]) -> Result<Self, tskit::metadata::MetadataError>
19+
where
20+
Self: Sized,
21+
{
22+
let slice: [u8; 8] = md[0..8].try_into().unwrap();
23+
let effect_size = f64::from_le_bytes(slice);
24+
let slice: [u8; 8] = md[8..].try_into().unwrap();
25+
let dominance = f64::from_le_bytes(slice);
26+
Ok(Self {
27+
effect_size,
28+
dominance,
29+
})
30+
}
31+
}
32+
33+
impl tskit::metadata::MutationMetadata for MutationMetadata {}
34+
35+
struct IndividualMetadata {
36+
name: String,
37+
phenotypes: Vec<i32>,
38+
}
39+
40+
impl MetadataRoundtrip for IndividualMetadata {
41+
fn encode(&self) -> Result<Vec<u8>, tskit::metadata::MetadataError> {
42+
let mut rv = vec![];
43+
rv.extend_from_slice(self.name.len().to_le_bytes().as_slice());
44+
rv.extend_from_slice(self.name.as_bytes());
45+
rv.extend_from_slice(self.phenotypes.len().to_le_bytes().as_slice());
46+
for i in self.phenotypes.iter() {
47+
rv.extend_from_slice(i.to_le_bytes().as_slice());
48+
}
49+
Ok(rv)
50+
}
51+
fn decode(md: &[u8]) -> Result<Self, tskit::metadata::MetadataError>
52+
where
53+
Self: Sized,
54+
{
55+
let size: [u8; std::mem::size_of::<usize>()] =
56+
md[0..std::mem::size_of::<usize>()].try_into().unwrap();
57+
let size = usize::from_le_bytes(size);
58+
let md = &md[std::mem::size_of::<usize>()..];
59+
let name = str::from_utf8(&md[0..size]).unwrap().to_string();
60+
let md = &md[size..];
61+
let md = &md[std::mem::size_of::<usize>()..];
62+
let mut phenotypes = vec![];
63+
// NOTE: production code would want to validate that
64+
// the remaining number of bytes are correct
65+
let chunks = md.chunks_exact(std::mem::size_of::<i32>());
66+
for c in chunks {
67+
// Unwrap b/c the conversion cannot fail b/c the chunk size is correct!
68+
let a: [u8; std::mem::size_of::<i32>()] = c.try_into().unwrap();
69+
phenotypes.push(i32::from_le_bytes(a));
70+
}
71+
Ok(Self { name, phenotypes })
72+
}
73+
}
74+
75+
impl tskit::metadata::IndividualMetadata for IndividualMetadata {}
76+
77+
fn main() {
78+
let ts = make_treeseq().unwrap();
79+
ts.dump("with_json_metadata.trees", 0).unwrap();
80+
}
81+
82+
fn make_tables() -> anyhow::Result<tskit::TableCollection> {
83+
let mut tables = tskit::TableCollection::new(100.0)?;
84+
let pop0 = tables.add_population()?;
85+
let ind0 = tables.add_individual_with_metadata(
86+
0,
87+
None,
88+
None,
89+
&IndividualMetadata {
90+
name: "Jerome".to_string(),
91+
phenotypes: vec![0, 1, 2, 0],
92+
},
93+
)?;
94+
let node0 = tables.add_node(tskit::NodeFlags::new_sample(), 0.0, pop0, ind0)?;
95+
let site0 = tables.add_site(50.0, Some("A".as_bytes()))?;
96+
let _ = tables.add_mutation_with_metadata(
97+
site0,
98+
node0,
99+
tskit::MutationId::NULL,
100+
1.0,
101+
Some("G".as_bytes()),
102+
&MutationMetadata {
103+
effect_size: -1e-3,
104+
dominance: 0.1,
105+
},
106+
)?;
107+
tables.build_index()?;
108+
Ok(tables)
109+
}
110+
111+
fn make_treeseq() -> anyhow::Result<tskit::TreeSequence> {
112+
Ok(make_tables()?.tree_sequence(0)?)
113+
}
114+
115+
#[test]
116+
fn test_mutation_metadata_roundtrip() {
117+
let md = MutationMetadata {
118+
effect_size: 0.1,
119+
dominance: 0.25,
120+
};
121+
let encoded = md.encode().unwrap();
122+
let decoded = MutationMetadata::decode(&encoded).unwrap();
123+
assert_eq!(md.effect_size, decoded.effect_size);
124+
assert_eq!(md.dominance, decoded.dominance);
125+
}
126+
127+
#[test]
128+
fn test_individual_metadata_roundtrip() {
129+
let md = IndividualMetadata {
130+
name: "Jerome".to_string(),
131+
phenotypes: vec![10, 9],
132+
};
133+
let encoded = md.encode().unwrap();
134+
let decoded = IndividualMetadata::decode(&encoded).unwrap();
135+
assert_eq!(md.name, decoded.name);
136+
assert_eq!(md.phenotypes, decoded.phenotypes);
137+
}

0 commit comments

Comments
 (0)