insta/
serialization.rs

1use serde::de::value::Error as ValueError;
2use serde::Serialize;
3
4use crate::content::{json, yaml, Content, ContentSerializer};
5use crate::settings::Settings;
6
7pub enum SerializationFormat {
8    #[cfg(feature = "csv")]
9    Csv,
10    #[cfg(feature = "ron")]
11    Ron,
12    #[cfg(feature = "toml")]
13    Toml,
14    Yaml,
15    Json,
16    JsonCompact,
17}
18
19#[derive(Debug)]
20pub enum SnapshotLocation {
21    Inline,
22    File,
23}
24
25pub fn serialize_content(mut content: Content, format: SerializationFormat) -> String {
26    content = Settings::with(|settings| {
27        if settings.sort_maps() {
28            content.sort_maps();
29        }
30        #[cfg(feature = "redactions")]
31        {
32            for (selector, redaction) in settings.iter_redactions() {
33                content = selector.redact(content, redaction);
34            }
35        }
36        content
37    });
38
39    match format {
40        SerializationFormat::Yaml => yaml::to_string(&content)[4..].to_string(),
41        SerializationFormat::Json => json::to_string_pretty(&content),
42        SerializationFormat::JsonCompact => json::to_string_compact(&content),
43        #[cfg(feature = "csv")]
44        SerializationFormat::Csv => {
45            let mut buf = Vec::with_capacity(128);
46            {
47                let mut writer = csv::Writer::from_writer(&mut buf);
48                // if the top-level content we're serializing is a vector we
49                // want to serialize it multiple times once for each item.
50                if let Some(content_slice) = content.as_slice() {
51                    for content in content_slice {
52                        writer.serialize(content).unwrap();
53                    }
54                } else {
55                    writer.serialize(&content).unwrap();
56                }
57                writer.flush().unwrap();
58            }
59            if buf.ends_with(b"\n") {
60                buf.truncate(buf.len() - 1);
61            }
62            String::from_utf8(buf).unwrap()
63        }
64        #[cfg(feature = "ron")]
65        SerializationFormat::Ron => {
66            let mut buf = Vec::new();
67            let mut config = ron::ser::PrettyConfig::new();
68            config.new_line = "\n".to_string();
69            config.indentor = "  ".to_string();
70            config.struct_names = true;
71            let mut serializer = ron::ser::Serializer::with_options(
72                &mut buf,
73                Some(config),
74                ron::options::Options::default(),
75            )
76            .unwrap();
77            content.serialize(&mut serializer).unwrap();
78            String::from_utf8(buf).unwrap()
79        }
80        #[cfg(feature = "toml")]
81        SerializationFormat::Toml => {
82            let mut rv = toml::to_string_pretty(&content).unwrap();
83            if rv.ends_with('\n') {
84                rv.truncate(rv.len() - 1);
85            }
86            rv
87        }
88    }
89}
90
91pub fn serialize_value<S: Serialize>(s: &S, format: SerializationFormat) -> String {
92    let serializer = ContentSerializer::<ValueError>::new();
93    let content = Serialize::serialize(s, serializer).unwrap();
94    serialize_content(content, format)
95}
96
97#[cfg(feature = "redactions")]
98pub fn serialize_value_redacted<S: Serialize>(
99    s: &S,
100    redactions: &[(crate::redaction::Selector, crate::redaction::Redaction)],
101    format: SerializationFormat,
102) -> String {
103    let serializer = ContentSerializer::<ValueError>::new();
104    let mut content = Serialize::serialize(s, serializer).unwrap();
105    for (selector, redaction) in redactions {
106        content = selector.redact(content, redaction);
107    }
108    serialize_content(content, format)
109}
110
111#[test]
112fn test_yaml_serialization() {
113    let yaml = serialize_content(
114        Content::Map(vec![
115            (
116                Content::from("env"),
117                Content::Seq(vec![
118                    Content::from("ENVIRONMENT"),
119                    Content::from("production"),
120                ]),
121            ),
122            (
123                Content::from("cmdline"),
124                Content::Seq(vec![Content::from("my-tool"), Content::from("run")]),
125            ),
126        ]),
127        SerializationFormat::Yaml,
128    );
129    crate::assert_snapshot!(&yaml, @r###"
130    env:
131      - ENVIRONMENT
132      - production
133    cmdline:
134      - my-tool
135      - run
136    "###);
137
138    let inline_yaml = serialize_content(
139        Content::Map(vec![
140            (
141                Content::from("env"),
142                Content::Seq(vec![
143                    Content::from("ENVIRONMENT"),
144                    Content::from("production"),
145                ]),
146            ),
147            (
148                Content::from("cmdline"),
149                Content::Seq(vec![Content::from("my-tool"), Content::from("run")]),
150            ),
151        ]),
152        SerializationFormat::Yaml,
153    );
154    crate::assert_snapshot!(&inline_yaml, @r###"
155    env:
156      - ENVIRONMENT
157      - production
158    cmdline:
159      - my-tool
160      - run
161    "###);
162}