@@ -152,14 +152,13 @@ impl<'a> IndividualTable<'a> {
152
152
///
153
153
/// # Returns
154
154
///
155
- /// The result type is `Option<T>`
156
- /// where `T`: [`tskit::metadata::IndividualMetadata`](crate::metadata::IndividualMetadata).
157
- /// `Some(T)` if there is metadata. `None` if the metadata field is empty for a given
158
- /// row.
155
+ /// * `Some(Ok(T))` if `row` is valid and decoding succeeded.
156
+ /// * `Some(Err(_))` if `row` is not valid and decoding failed.
157
+ /// * `None` if `row` is not valid.
159
158
///
160
159
/// # Errors
161
160
///
162
- /// * [`TskitError::IndexError `] if `row` is out of range .
161
+ /// * [`TskitError::MetadataError `] if decoding fails .
163
162
///
164
163
/// # Examples
165
164
///
@@ -190,15 +189,16 @@ impl<'a> IndividualTable<'a> {
190
189
/// # let metadata = IndividualMetadata{x: 1};
191
190
/// # assert!(tables.add_individual_with_metadata(0, None, None,
192
191
/// # &metadata).is_ok());
193
- /// // We know the metadata are here, so we unwrap the Result and the Option
192
+ /// // We know the metadata are here, so we unwrap the Option and the Result:
194
193
/// let decoded = tables.individuals().metadata::<IndividualMetadata>(0.into()).unwrap().unwrap();
195
194
/// assert_eq!(decoded.x, 1);
196
195
/// # }
197
196
/// ```
198
197
///
199
198
/// ## Checking for errors and absence of metadata
200
199
///
201
- /// Handling both the possibility of error and optional metadata leads to some verbosity:
200
+ /// The `Option<Result<_>>` return value allows all
201
+ /// three return possibilities to be easily covered:
202
202
///
203
203
/// ```
204
204
/// # #[cfg(feature = "derive")] {
@@ -213,22 +213,112 @@ impl<'a> IndividualTable<'a> {
213
213
/// # assert!(tables
214
214
/// # .add_individual_with_metadata(0, None, None, &metadata)
215
215
/// # .is_ok());
216
- /// // First, check the Result.
217
- /// let decoded_option = match tables
218
- /// .individuals()
219
- /// .metadata::<IndividualMetadata>(0.into())
216
+ /// match tables.individuals().metadata::<IndividualMetadata>(0.into())
220
217
/// {
221
- /// Some(metadata_option) => metadata_option,
222
- /// None => panic!("expected metadata"),
218
+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
219
+ /// Some(Err(_)) => panic!("got an error??"),
220
+ /// None => panic!("Got None??"),
223
221
/// };
224
- /// // Now, check the contents of the Option
225
- /// match decoded_option {
226
- /// Ok(metadata) => assert_eq!(metadata.x, 1),
227
- /// Err(e) => panic!("error decoding metadata: {:?}", e),
222
+ /// # }
223
+ /// ```
224
+ ///
225
+ /// ## Attempting to use the wrong type.
226
+ ///
227
+ /// Let's define a mutation metadata type with the exact same fields
228
+ /// as our individual metadata defined above:
229
+ ///
230
+ /// ```
231
+ /// # #[cfg(feature = "derive")] {
232
+ /// #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
233
+ /// #[serializer("serde_json")]
234
+ /// struct MutationMetadata {
235
+ /// x: i32,
236
+ /// }
237
+ /// # }
238
+ /// ```
239
+ ///
240
+ /// This type has the wrong trait bound and will cause compilation to fail:
241
+ ///
242
+ #[ cfg_attr(
243
+ feature = "derive" ,
244
+ doc = r##"
245
+ ```compile_fail
246
+ # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
247
+ # #[serializer("serde_json")]
248
+ # struct MutationMetadata {
249
+ # x: i32,
250
+ # }
251
+ # use tskit::TableAccess;
252
+ # let mut tables = tskit::TableCollection::new(10.).unwrap();
253
+ match tables.individuals().metadata::<MutationMetadata>(0.into())
254
+ {
255
+ Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
256
+ Some(Err(_)) => panic!("got an error??"),
257
+ None => panic!("Got None??"),
258
+ };
259
+ ```
260
+ "##
261
+ ) ]
262
+ ///
263
+ /// ## Limitations: different type, same trait bound
264
+ ///
265
+ /// Finally, let us consider a different struct that has identical
266
+ /// fields to `IndividualMetadata` defined above and also implements
267
+ /// the correct trait:
268
+ ///
269
+ /// ```
270
+ /// # #[cfg(feature = "derive")] {
271
+ /// #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
272
+ /// #[serializer("serde_json")]
273
+ /// struct IndividualMetadataToo {
274
+ /// x: i32,
228
275
/// }
229
276
/// # }
230
277
/// ```
231
- pub fn metadata < T : metadata:: MetadataRoundtrip > (
278
+ ///
279
+ /// Let's walk through a detailed example:
280
+ ///
281
+ /// ```
282
+ /// # #[cfg(feature = "derive")] {
283
+ /// # use tskit::TableAccess;
284
+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
285
+ /// # #[serializer("serde_json")]
286
+ /// # struct IndividualMetadata {
287
+ /// # x: i32,
288
+ /// # }
289
+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
290
+ /// # #[serializer("serde_json")]
291
+ /// # struct IndividualMetadataToo {
292
+ /// # x: i32,
293
+ /// # }
294
+ /// // create a mutable table collection
295
+ /// let mut tables = tskit::TableCollection::new(100.).unwrap();
296
+ /// // Create some metadata based on our FIRST type
297
+ /// let metadata = IndividualMetadata { x: 1 };
298
+ /// // Add a row with our metadata
299
+ /// assert!(tables.add_individual_with_metadata(0, None, None, &metadata).is_ok());
300
+ /// // Trying to fetch using our SECOND type as the generic type works!
301
+ /// match tables.individuals().metadata::<IndividualMetadataToo>(0.into())
302
+ /// {
303
+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
304
+ /// Some(Err(_)) => panic!("got an error??"),
305
+ /// None => panic!("Got None??"),
306
+ /// };
307
+ /// # }
308
+ /// ```
309
+ ///
310
+ /// What is going on here?
311
+ /// Both types satisfy the same trait bound ([`metadata::IndividualMetadata`])
312
+ /// and their data fields look identical to `serde_json`.
313
+ /// Thus, one is exchangeable for the other because they have the exact same
314
+ /// *behavior*.
315
+ ///
316
+ /// However, it is also true that this is (often/usually/always) not exactly what we want.
317
+ /// We are experimenting with encapsulation APIs involving traits with
318
+ /// [associated
319
+ /// types](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) to enforce at *compile time* that exactly one type (`struct/enum`, etc.) is a valid
320
+ /// metadata type for a table.
321
+ pub fn metadata < T : metadata:: IndividualMetadata > (
232
322
& ' a self ,
233
323
row : IndividualId ,
234
324
) -> Option < Result < T , TskitError > > {
0 commit comments