Skip to content

Commit cf545e0

Browse files
authored
Add test file exploring some new feature ideas. (#251)
* Idea is to elide a lot of non-ergonomic generics from metadata retrieval API.
1 parent dd5978f commit cf545e0

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

tests/experimental.rs

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#![cfg(feature = "derive")]
2+
3+
mod experimental_features {
4+
5+
use tskit::TableAccess;
6+
7+
// Name is not great.
8+
// We'd like to have this be : tskit::TableAccess,
9+
// but that's a big ask at this stage.
10+
trait MutationMetadataExtraction {
11+
type Item: tskit::metadata::MutationMetadata;
12+
13+
fn get_mutation_metadata<M: Into<tskit::MutationId>>(
14+
&self,
15+
row: M,
16+
) -> Result<Option<Self::Item>, tskit::TskitError>;
17+
}
18+
19+
#[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
20+
#[serializer("serde_json")]
21+
struct MutationMetadataType {
22+
effect_size: f64,
23+
}
24+
25+
// Goal:
26+
//
27+
// A table newtype can let us define tables in terms
28+
// of their metadata.
29+
// If we want a table collection that only uses
30+
// MutationMetadata, then the traits defined here
31+
// suffice.
32+
33+
struct MyTableCollection(tskit::TableCollection);
34+
35+
// Deref'ing for pure convenience
36+
impl std::ops::Deref for MyTableCollection {
37+
type Target = tskit::TableCollection;
38+
39+
fn deref(&self) -> &Self::Target {
40+
&self.0
41+
}
42+
}
43+
44+
impl std::ops::DerefMut for MyTableCollection {
45+
fn deref_mut(&mut self) -> &mut Self::Target {
46+
&mut self.0
47+
}
48+
}
49+
50+
impl MutationMetadataExtraction for MyTableCollection {
51+
type Item = MutationMetadataType;
52+
fn get_mutation_metadata<M: Into<tskit::MutationId>>(
53+
&self,
54+
row: M,
55+
) -> Result<Option<Self::Item>, tskit::TskitError> {
56+
self.mutations().metadata::<Self::Item>(row.into())
57+
}
58+
}
59+
60+
#[test]
61+
fn test_table_collection_newtype() {
62+
let mut tables = MyTableCollection(tskit::TableCollection::new(1.0).unwrap());
63+
let md = MutationMetadataType { effect_size: 0.1 };
64+
tables
65+
.add_mutation_with_metadata(0, 0, 0, 0.0, None, &md)
66+
.unwrap();
67+
let decoded = tables.get_mutation_metadata(0).unwrap().unwrap();
68+
assert_eq!(decoded.effect_size, 0.10);
69+
70+
// current API requires
71+
let decoded = tables
72+
.mutations()
73+
.metadata::<MutationMetadataType>(0.into())
74+
.unwrap()
75+
.unwrap();
76+
assert_eq!(decoded.effect_size, 0.10);
77+
}
78+
}
79+
80+
mod experimental_features_refined {
81+
82+
// To make further progress from here:
83+
// AsTables becomes a trait in tskit.
84+
// Given that, a blanket implementation
85+
// for TableAccess is trivial boilerplate.
86+
// Given those two things:
87+
// * Can define these metadata-centric traits.
88+
// * proc-macro can implement them "easily"
89+
//
90+
// Caveats:
91+
//
92+
// * How to apply the same logic to TreeSequence?
93+
// - There is currently no .tables() function there.
94+
// - but it does implement TableAccess.
95+
// - Hmmm...proc macros to the rescue?
96+
//
97+
// * On the surface it looks "tempting" to allow
98+
// a non-owning TableCollection to hold, but never
99+
// Drop, a raw pointer. But that kinda thing
100+
// seems very un-rusty.
101+
//
102+
// I may have the logic backwards:
103+
//
104+
// * We want to constrain on a trait that it itself
105+
// constrained by TableAccess.
106+
// * What kind of proc-macro can we use to implement that,
107+
// again "easily"?
108+
109+
use tskit::TableAccess;
110+
111+
trait AsTableCollection {
112+
fn as_tables(&self) -> &tskit::TableCollection;
113+
}
114+
115+
// Name is not great.
116+
// We'd like to have this be : tskit::TableAccess,
117+
// but that's a big ask at this stage.
118+
// See notes above.
119+
trait MutationMetadataExtraction: AsTableCollection {
120+
type Item: tskit::metadata::MutationMetadata;
121+
122+
fn get_mutation_metadata<M: Into<tskit::MutationId>>(
123+
&self,
124+
row: M,
125+
) -> Result<Option<Self::Item>, tskit::TskitError> {
126+
self.as_tables()
127+
.mutations()
128+
.metadata::<Self::Item>(row.into())
129+
}
130+
}
131+
132+
#[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
133+
#[serializer("serde_json")]
134+
struct MutationMetadataType {
135+
effect_size: f64,
136+
}
137+
138+
// Goal:
139+
//
140+
// A table newtype can let us define tables in terms
141+
// of their metadata.
142+
// If we want a table collection that only uses
143+
// MutationMetadata, then the traits defined here
144+
// suffice.
145+
146+
struct MyTableCollection(tskit::TableCollection);
147+
148+
impl AsTableCollection for MyTableCollection {
149+
fn as_tables(&self) -> &tskit::TableCollection {
150+
&self.0
151+
}
152+
}
153+
154+
impl MutationMetadataExtraction for MyTableCollection {
155+
type Item = MutationMetadataType;
156+
}
157+
158+
#[test]
159+
fn test_table_collection_newtype() {
160+
let mut tables = MyTableCollection(tskit::TableCollection::new(1.0).unwrap());
161+
let md = MutationMetadataType { effect_size: 0.1 };
162+
tables
163+
.0
164+
.add_mutation_with_metadata(0, 0, 0, 0.0, None, &md)
165+
.unwrap();
166+
let decoded = tables.get_mutation_metadata(0).unwrap().unwrap();
167+
assert_eq!(decoded.effect_size, 0.10);
168+
169+
// current API requires
170+
let decoded = tables
171+
.0
172+
.mutations()
173+
.metadata::<MutationMetadataType>(0.into())
174+
.unwrap()
175+
.unwrap();
176+
assert_eq!(decoded.effect_size, 0.10);
177+
}
178+
}

0 commit comments

Comments
 (0)