|
3 | 3 | //! This module defines a generic wrapper that allows for flexible deserialization |
4 | 4 | //! of annotations with custom types in SBML-related data structures. |
5 | 5 |
|
6 | | -use serde::Deserialize; |
| 6 | +use serde::de::{self, MapAccess, Visitor}; |
| 7 | +use serde::{Deserialize, Deserializer, Serialize}; |
| 8 | +use std::fmt; |
| 9 | +use std::marker::PhantomData; |
7 | 10 |
|
8 | 11 | /// A generic wrapper struct for deserializing XML annotations. |
9 | 12 | /// |
10 | 13 | /// This struct allows for flexible deserialization of annotations by wrapping |
11 | 14 | /// a generic type `T` with a specific XML structure. It is particularly useful |
12 | 15 | /// when working with serialized metadata in SBML models. |
13 | 16 | /// |
| 17 | +/// The custom deserializer iterates through all child elements within the |
| 18 | +/// `<annotation>` tag and attempts to deserialize each one into type `T`. |
| 19 | +/// If multiple elements can be parsed into `T`, the first successful one is used. |
| 20 | +/// Elements that cannot be parsed into `T` are silently ignored. |
| 21 | +/// |
14 | 22 | /// # Type Parameters |
15 | 23 | /// * `T` - The type of the annotation content to be deserialized |
16 | 24 | /// |
17 | | -/// # Serde Configuration |
18 | | -/// * Renames the root XML element to "annotation" |
19 | | -/// * Uses "$value" to capture the inner content |
20 | | -#[derive(Debug, Deserialize, Clone)] |
| 25 | +/// # Behavior |
| 26 | +/// * Expects XML with root element named "annotation" |
| 27 | +/// * Iterates through all child elements |
| 28 | +/// * Attempts to deserialize each child element into type `T` |
| 29 | +/// * Returns the first successful match |
| 30 | +/// * Ignores elements that cannot be parsed into `T` |
| 31 | +/// |
| 32 | +/// # Example |
| 33 | +/// ```xml |
| 34 | +/// <annotation> |
| 35 | +/// <test>some_value</test> |
| 36 | +/// <other_field>ignored</other_field> |
| 37 | +/// <name>also_ignored</name> |
| 38 | +/// </annotation> |
| 39 | +/// ``` |
| 40 | +/// |
| 41 | +/// When deserializing into `Wrapper<TestStruct>` where `TestStruct` has a `test` field, |
| 42 | +/// only the `<test>` element would be successfully parsed, while others are ignored. |
| 43 | +#[derive(Debug, Clone, Serialize)] |
21 | 44 | #[serde(rename = "annotation")] |
22 | 45 | pub(crate) struct Wrapper<T> { |
23 | 46 | /// The actual annotation content |
24 | | - /// |
25 | | - /// Uses a special serde rename to capture the inner XML value |
26 | | - #[serde(rename = "$value")] |
27 | 47 | pub(crate) annotation: T, |
28 | 48 | } |
| 49 | + |
| 50 | +impl<'de, T> Deserialize<'de> for Wrapper<T> |
| 51 | +where |
| 52 | + T: Deserialize<'de>, |
| 53 | +{ |
| 54 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 55 | + where |
| 56 | + D: Deserializer<'de>, |
| 57 | + { |
| 58 | + struct WrapperVisitor<T> { |
| 59 | + marker: PhantomData<T>, |
| 60 | + } |
| 61 | + |
| 62 | + impl<'de, T> Visitor<'de> for WrapperVisitor<T> |
| 63 | + where |
| 64 | + T: Deserialize<'de>, |
| 65 | + { |
| 66 | + type Value = Wrapper<T>; |
| 67 | + |
| 68 | + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 69 | + formatter.write_str("an annotation element with parseable content") |
| 70 | + } |
| 71 | + |
| 72 | + fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> |
| 73 | + where |
| 74 | + A: MapAccess<'de>, |
| 75 | + { |
| 76 | + let mut last_error: Option<String> = None; |
| 77 | + |
| 78 | + // Iterate through all key-value pairs |
| 79 | + while let Some(key) = map.next_key::<String>()? { |
| 80 | + // Try to deserialize the next value into T |
| 81 | + // This handles both single values and nested structures |
| 82 | + match map.next_value::<T>() { |
| 83 | + Ok(parsed_value) => { |
| 84 | + // Successfully parsed this element into T |
| 85 | + return Ok(Wrapper { |
| 86 | + annotation: parsed_value, |
| 87 | + }); |
| 88 | + } |
| 89 | + Err(err) => { |
| 90 | + // This element couldn't be parsed into T, store error and continue |
| 91 | + last_error = Some(format!("Failed to parse '{}': {}", key, err)); |
| 92 | + continue; |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + // If we get here, no element could be parsed into T |
| 98 | + match last_error { |
| 99 | + Some(err) => Err(de::Error::custom(err)), |
| 100 | + None => Err(de::Error::custom( |
| 101 | + "no elements found that could be parsed into the target type", |
| 102 | + )), |
| 103 | + } |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + // Use a map deserializer since XML elements are treated as key-value pairs |
| 108 | + deserializer.deserialize_map(WrapperVisitor { |
| 109 | + marker: PhantomData, |
| 110 | + }) |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +impl<T> Wrapper<T> { |
| 115 | + /// Creates a new wrapper with the given annotation content. |
| 116 | + #[allow(dead_code)] |
| 117 | + pub(crate) fn new(annotation: T) -> Self { |
| 118 | + Self { annotation } |
| 119 | + } |
| 120 | + |
| 121 | + /// Gets a reference to the annotation content. |
| 122 | + #[allow(dead_code)] |
| 123 | + pub(crate) fn get(&self) -> &T { |
| 124 | + &self.annotation |
| 125 | + } |
| 126 | + |
| 127 | + /// Consumes the wrapper and returns the annotation content. |
| 128 | + #[allow(dead_code)] |
| 129 | + pub(crate) fn into_inner(self) -> T { |
| 130 | + self.annotation |
| 131 | + } |
| 132 | +} |
0 commit comments