elfo_core/dumping/
raw.rs

1use std::{borrow::Cow, convert::AsRef};
2
3use serde::{Serialize, Serializer};
4use serde_json::value::RawValue;
5
6/// A wrapper around a raw dump (e.g. received from WS/HTTP).
7///
8/// If the dump is valid JSON, it will be serialized as inner JSON
9/// with saving formatting, but without newlines (replaced with a space).
10///
11/// Otherwise, the dump is serialized as JSON string.
12#[derive(Debug)]
13#[stability::unstable]
14pub struct Raw<T>(pub T);
15
16impl<T: AsRef<str>> Serialize for Raw<T> {
17    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        let s = self.0.as_ref();
22        let r = replace_newline(s);
23
24        if let Some(value) = as_raw_json(&r) {
25            value.serialize(serializer)
26        } else {
27            // Will be serialized as a JSON string with escaping.
28            s.serialize(serializer)
29        }
30    }
31}
32
33fn replace_newline(raw: &str) -> Cow<'_, str> {
34    if raw.contains('\n') {
35        Cow::from(raw.replace('\n', " "))
36    } else {
37        Cow::from(raw)
38    }
39}
40
41fn as_raw_json(raw: &str) -> Option<&RawValue> {
42    let raw = raw.trim();
43    serde_json::from_str(raw).ok()
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[derive(Serialize)]
51    struct Data {
52        a: u32,
53        r: Raw<String>,
54    }
55
56    #[test]
57    fn json() {
58        assert_eq!(
59            serde_json::to_string(&Data {
60                a: 42,
61                r: Raw(r#"{"b":42}"#.into()),
62            })
63            .unwrap(),
64            r#"{"a":42,"r":{"b":42}}"#
65        );
66
67        assert_eq!(
68            serde_json::to_string(&Data {
69                a: 42,
70                r: Raw("{\"b\":\n42}".into()),
71            })
72            .unwrap(),
73            r#"{"a":42,"r":{"b": 42}}"#
74        );
75    }
76
77    #[test]
78    fn non_json() {
79        assert_eq!(
80            serde_json::to_string(&Data {
81                a: 42,
82                r: Raw("foo\nbar".into()),
83            })
84            .unwrap(),
85            r#"{"a":42,"r":"foo\nbar"}"#
86        );
87    }
88}