Skip to main content

deser_stringified/
lib.rs

1use serde::{de::{DeserializeOwned, Visitor}, Deserializer};
2
3/// deserialize a stringified field embedded in other data using a custom
4/// parser, allowing you to parse JSON, YAML, or any other serde-compatible
5/// format.
6pub fn deser_stringified_format<'de, D, T, F, E>(deserializer: D, parser: F) -> Result<T, D::Error>
7where
8    D: Deserializer<'de>,
9    T: serde::de::DeserializeOwned,
10    F: Fn(&str) -> Result<T, E>,
11    E: std::fmt::Display,
12{
13    struct StringifiedFormatVisitor<T, F, E> {
14        parser: F,
15        marker: std::marker::PhantomData<T>,
16        error_marker: std::marker::PhantomData<E>,
17    }
18
19    impl<'de, T, F, E> Visitor<'de> for StringifiedFormatVisitor<T, F, E>
20    where
21        T: serde::de::DeserializeOwned,
22        F: Fn(&str) -> Result<T, E>,
23        E: std::fmt::Display,
24    {
25        type Value = T;
26
27        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
28            write!(
29                formatter,
30                "a string containing formatted data that parses to type {}",
31                std::any::type_name::<T>()
32            )
33        }
34
35        fn visit_str<A>(self, value: &str) -> Result<Self::Value, A>
36        where
37            A: serde::de::Error,
38        {
39            (self.parser)(value).map_err(A::custom)
40        }
41
42        fn visit_borrowed_str<A>(self, value: &'de str) -> Result<Self::Value, A>
43        where
44            A: serde::de::Error,
45        {
46            (self.parser)(value).map_err(A::custom)
47        }
48    }
49
50    deserializer.deserialize_str(StringifiedFormatVisitor {
51        parser,
52        marker: std::marker::PhantomData,
53        error_marker: std::marker::PhantomData,
54    })
55}
56
57#[cfg(feature = "serde_json")]
58pub fn deser_stringified_json<'de, D, T>(deserializer: D) -> Result<T, D::Error>
59where
60    D: serde::Deserializer<'de>,
61    T: DeserializeOwned,
62{
63    // closure is necessary to solve lifetime issues
64    deser_stringified_format(deserializer, |s| serde_json::from_str(s))
65}
66
67#[cfg(feature = "serde_yaml")]
68pub fn deser_stringified_yaml<'de, D, T>(deserializer: D) -> Result<T, D::Error>
69where
70    D: serde::Deserializer<'de>,
71    T: DeserializeOwned,
72{
73    deser_stringified_format(deserializer, |s| serde_yaml::from_str(s))
74}
75
76#[cfg(feature = "toml")]
77pub fn deser_stringified_toml<'de, D, T>(deserializer: D) -> Result<T, D::Error>
78where
79    D: serde::Deserializer<'de>,
80    T: serde::de::DeserializeOwned,
81{
82    deser_stringified_format(deserializer, |s| toml::from_str(s))
83}
84
85#[cfg(test)]
86mod tests {
87    use serde::Deserialize;
88    use super::*;
89
90    #[derive(Deserialize)]
91    struct Metadata {
92        key: i32,
93        enabled: bool,
94    }
95
96    #[test]
97    #[cfg(feature = "serde_json")]
98    fn json_parsing() {
99        #[derive(Deserialize)]
100        struct Example<T: DeserializeOwned> {
101            #[serde(deserialize_with = "deser_stringified_json")]
102            data: T,
103        }
104
105        let json_str = r#"{"data": "{\"key\": 1, \"enabled\": false}"}"#;
106        let parsed: Example<serde_json::Value> = serde_json::from_str(json_str).unwrap();
107        assert_eq!(parsed.data, serde_json::json!({"key": 1, "enabled": false}));
108
109        let struct_parsed: Example<Metadata> = serde_json::from_str(json_str).unwrap();
110        assert_eq!(struct_parsed.data.key, 1);
111        assert_eq!(struct_parsed.data.enabled, false);
112    }
113
114    #[test]
115    #[cfg(feature = "serde_yaml")]
116    fn yaml_parsing() {
117        #[derive(Deserialize)]
118        struct Example<T: DeserializeOwned> {
119            #[serde(deserialize_with = "deser_stringified_yaml")]
120            data: T,
121        }
122
123        let yaml_str = r#"
124data: |
125  key: 1
126  enabled: false
127"#;
128        let parsed: Example<serde_yaml::Value> = serde_yaml::from_str(yaml_str).unwrap();
129
130        let mut expected_mapping = serde_yaml::Mapping::new();
131        expected_mapping.insert(
132            serde_yaml::Value::String("key".to_string()),
133            serde_yaml::Value::Number(1.into()),
134        );
135        expected_mapping.insert(
136            serde_yaml::Value::String("enabled".to_string()),
137            serde_yaml::Value::Bool(false),
138        );
139        let expected = serde_yaml::Value::Mapping(expected_mapping);
140        assert_eq!(parsed.data, expected);
141
142        let struct_parsed: Example<Metadata> = serde_yaml::from_str(yaml_str).unwrap();
143        assert_eq!(struct_parsed.data.key, 1);
144        assert_eq!(struct_parsed.data.enabled, false);
145    }
146
147    #[test]
148    #[cfg(feature = "toml")]
149    fn toml_parsing() {
150        #[derive(Deserialize)]
151        struct Example<T: serde::de::DeserializeOwned> {
152            #[serde(deserialize_with = "deser_stringified_toml")]
153            data: T,
154        }
155        
156        let toml_str = "data = \"\"\"\nkey = 1\nenabled = false\n\"\"\"";
157        let parsed: Example<toml::Value> = toml::from_str(toml_str).unwrap();
158        
159        let mut expected = toml::value::Table::new();
160        expected.insert("key".to_string(), toml::Value::Integer(1));
161        expected.insert("enabled".to_string(), toml::Value::Boolean(false));
162        assert_eq!(parsed.data, toml::Value::Table(expected));
163        
164        let struct_parsed: Example<Metadata> = toml::from_str(toml_str).unwrap();
165        assert_eq!(struct_parsed.data.key, 1);
166        assert_eq!(struct_parsed.data.enabled, false);
167    }
168}