@@ -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,104 @@ 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
+ /// ```compile_fail
243
+ /// # #[cfg(feature = "derive")] {
244
+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
245
+ /// # #[serializer("serde_json")]
246
+ /// # struct MutationMetadata {
247
+ /// # x: i32,
248
+ /// # }
249
+ /// # use tskit::TableAccess;
250
+ /// # let mut tables = tskit::TableCollection::new(10.).unwrap();
251
+ /// match tables.individuals().metadata::<MutationMetadata>(0.into())
252
+ /// {
253
+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
254
+ /// Some(Err(_)) => panic!("got an error??"),
255
+ /// None => panic!("Got None??"),
256
+ /// };
257
+ /// # }
258
+ /// ```
259
+ ///
260
+ /// ## Limitations: different type, same trait bound
261
+ ///
262
+ /// Finally, let us consider a different struct that has identical
263
+ /// fields to `IndividualMetadata` defined above and also implements
264
+ /// the correct trait:
265
+ ///
266
+ /// ```
267
+ /// # #[cfg(feature = "derive")] {
268
+ /// #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
269
+ /// #[serializer("serde_json")]
270
+ /// struct IndividualMetadataToo {
271
+ /// x: i32,
228
272
/// }
229
273
/// # }
230
274
/// ```
231
- pub fn metadata < T : metadata:: MetadataRoundtrip > (
275
+ ///
276
+ /// Let's walk through a detailed example:
277
+ ///
278
+ /// ```
279
+ /// # #[cfg(feature = "derive")] {
280
+ /// # use tskit::TableAccess;
281
+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
282
+ /// # #[serializer("serde_json")]
283
+ /// # struct IndividualMetadata {
284
+ /// # x: i32,
285
+ /// # }
286
+ /// // create a mutable table collection
287
+ /// let mut tables = tskit::TableCollection::new(100.).unwrap();
288
+ /// // Create some metadata based on our FIRST type
289
+ /// let metadata = IndividualMetadata { x: 1 };
290
+ /// // Add a row with our metadata
291
+ /// assert!(tables.add_individual_with_metadata(0, None, None, &metadata).is_ok());
292
+ /// // Trying to fetch using our SECOND type as the generic type works!
293
+ /// match tables.individuals().metadata::<IndividualMetadataToo>(0.into())
294
+ /// {
295
+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
296
+ /// Some(Err(_)) => panic!("got an error??"),
297
+ /// None => panic!("Got None??"),
298
+ /// };
299
+ /// # }
300
+ /// ```
301
+ ///
302
+ /// What is going on here?
303
+ /// Both types satisfy the same trait bound ([`metadata::IndividualMetadata`])
304
+ /// and their data fields look identical to `serde_json`.
305
+ /// Thus, one is exchangeable for the other because they have the exact same
306
+ /// *behavior*.
307
+ ///
308
+ /// However, it is also true that this is (often/usually/always) not exactly what we want.
309
+ /// We are experimenting with encapsulation APIs involving traits with
310
+ /// [associated
311
+ /// 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
312
+ /// metadata type for a table.
313
+ pub fn metadata < T : metadata:: IndividualMetadata > (
232
314
& ' a self ,
233
315
row : IndividualId ,
234
316
) -> Option < Result < T , TskitError > > {
0 commit comments