mastodon_async/helpers/
json.rs

1use std::{
2    fs::{File, OpenOptions},
3    io::{Read, Write},
4    path::Path,
5};
6
7use serde_json;
8
9use crate::{Data, Result};
10
11/// Attempts to deserialize a Data struct from a string
12pub fn from_str(s: &str) -> Result<Data> {
13    Ok(serde_json::from_str(s)?)
14}
15
16/// Attempts to deserialize a Data struct from a slice of bytes
17pub fn from_slice(s: &[u8]) -> Result<Data> {
18    Ok(serde_json::from_slice(s)?)
19}
20
21/// Attempts to deserialize a Data struct from something that implements
22/// the std::io::Read trait
23pub fn from_reader<R: Read>(mut r: R) -> Result<Data> {
24    let mut buffer = Vec::new();
25    r.read_to_end(&mut buffer)?;
26    from_slice(&buffer)
27}
28
29/// Attempts to deserialize a Data struct from a file
30pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Data> {
31    let path = path.as_ref();
32    let file = File::open(path)?;
33    from_reader(file)
34}
35
36/// Attempts to serialize a Data struct to a String
37pub fn to_string(data: &Data) -> Result<String> {
38    Ok(serde_json::to_string_pretty(data)?)
39}
40
41/// Attempts to serialize a Data struct to a Vec of bytes
42pub fn to_vec(data: &Data) -> Result<Vec<u8>> {
43    Ok(serde_json::to_vec(data)?)
44}
45
46/// Attempts to serialize a Data struct to something that implements the
47/// std::io::Write trait
48pub fn to_writer<W: Write>(data: &Data, writer: W) -> Result<()> {
49    Ok(serde_json::to_writer(writer, data)?)
50}
51
52/// Attempts to serialize a Data struct to a file
53///
54/// When opening the file, this will set the `.write(true)` and
55/// `.truncate(true)` options, use the next method for more
56/// fine-grained control
57pub fn to_file<P: AsRef<Path>>(data: &Data, path: P) -> Result<()> {
58    let mut options = OpenOptions::new();
59    options.create(true).write(true).truncate(true);
60    to_file_with_options(data, path, options)?;
61    Ok(())
62}
63
64/// Attempts to serialize a Data struct to a file
65pub fn to_file_with_options<P: AsRef<Path>>(
66    data: &Data,
67    path: P,
68    options: OpenOptions,
69) -> Result<()> {
70    let path = path.as_ref();
71    let file = options.open(path)?;
72    to_writer(data, file)?;
73    Ok(())
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use std::{fs::OpenOptions, io::Cursor};
80    use tempfile::{tempdir, NamedTempFile};
81
82    const DOC: &str = indoc!(
83        r#"
84            {
85                "base": "https://example.com",
86                "client_id": "adbc01234",
87                "client_secret": "0987dcba",
88                "redirect": "urn:ietf:wg:oauth:2.0:oob",
89                "token": "fedc5678"
90            }
91    "#
92    );
93
94    #[test]
95    fn test_from_str() {
96        let desered = from_str(DOC).expect("Couldn't deserialize Data");
97        assert_eq!(
98            desered,
99            Data {
100                base: "https://example.com".into(),
101                client_id: "adbc01234".into(),
102                client_secret: "0987dcba".into(),
103                redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
104                token: "fedc5678".into(),
105            }
106        );
107    }
108    #[test]
109    fn test_from_slice() {
110        let doc = DOC.as_bytes();
111        let desered = from_slice(doc).expect("Couldn't deserialize Data");
112        assert_eq!(
113            desered,
114            Data {
115                base: "https://example.com".into(),
116                client_id: "adbc01234".into(),
117                client_secret: "0987dcba".into(),
118                redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
119                token: "fedc5678".into(),
120            }
121        );
122    }
123    #[test]
124    fn test_from_reader() {
125        let doc = DOC.as_bytes();
126        let doc = Cursor::new(doc);
127        let desered = from_reader(doc).expect("Couldn't deserialize Data");
128        assert_eq!(
129            desered,
130            Data {
131                base: "https://example.com".into(),
132                client_id: "adbc01234".into(),
133                client_secret: "0987dcba".into(),
134                redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
135                token: "fedc5678".into(),
136            }
137        );
138    }
139    #[test]
140    fn test_from_file() {
141        let mut datafile = NamedTempFile::new().expect("Couldn't create tempfile");
142        write!(&mut datafile, "{}", DOC).expect("Couldn't write Data to file");
143        let desered = from_file(datafile.path()).expect("Couldn't deserialize Data");
144        assert_eq!(
145            desered,
146            Data {
147                base: "https://example.com".into(),
148                client_id: "adbc01234".into(),
149                client_secret: "0987dcba".into(),
150                redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
151                token: "fedc5678".into(),
152            }
153        );
154    }
155    #[test]
156    fn test_to_string() {
157        let data = Data {
158            base: "https://example.com".into(),
159            client_id: "adbc01234".into(),
160            client_secret: "0987dcba".into(),
161            redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
162            token: "fedc5678".into(),
163        };
164        let s = to_string(&data).expect("Couldn't serialize Data");
165        let desered = from_str(&s).expect("Couldn't deserialize Data");
166        assert_eq!(data, desered);
167    }
168    #[test]
169    fn test_to_vec() {
170        let data = Data {
171            base: "https://example.com".into(),
172            client_id: "adbc01234".into(),
173            client_secret: "0987dcba".into(),
174            redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
175            token: "fedc5678".into(),
176        };
177        let v = to_vec(&data).expect("Couldn't write to vec");
178        let desered = from_slice(&v).expect("Couldn't deserialize data");
179        assert_eq!(data, desered);
180    }
181    #[test]
182    fn test_to_writer() {
183        let data = Data {
184            base: "https://example.com".into(),
185            client_id: "adbc01234".into(),
186            client_secret: "0987dcba".into(),
187            redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
188            token: "fedc5678".into(),
189        };
190        let mut buffer = Vec::new();
191        to_writer(&data, &mut buffer).expect("Couldn't write to writer");
192        let reader = Cursor::new(buffer);
193        let desered = from_reader(reader).expect("Couldn't deserialize Data");
194        assert_eq!(data, desered);
195    }
196    #[test]
197    fn test_to_file() {
198        let data = Data {
199            base: "https://example.com".into(),
200            client_id: "adbc01234".into(),
201            client_secret: "0987dcba".into(),
202            redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
203            token: "fedc5678".into(),
204        };
205        let tempdir = tempdir().expect("Couldn't create tempdir");
206        let filename = tempdir.path().join("mastodon-data.json");
207        to_file(&data, &filename).expect("Couldn't write to file");
208        let desered = from_file(&filename).expect("Couldn't deserialize Data");
209        assert_eq!(data, desered);
210    }
211    #[test]
212    fn test_to_file_with_options() {
213        let data = Data {
214            base: "https://example.com".into(),
215            client_id: "adbc01234".into(),
216            client_secret: "0987dcba".into(),
217            redirect: "urn:ietf:wg:oauth:2.0:oob".into(),
218            token: "fedc5678".into(),
219        };
220        let file = NamedTempFile::new().expect("Couldn't create tempfile");
221        let mut options = OpenOptions::new();
222        options.write(true).create(false).truncate(true);
223        to_file_with_options(&data, file.path(), options).expect("Couldn't write to file");
224        let desered = from_file(file.path()).expect("Couldn't deserialize Data");
225        assert_eq!(data, desered);
226    }
227}