1use std::{future::Future, path::Path};
2
3use serde::{de::DeserializeOwned, Serialize};
4use tokio::fs;
5use tracing::{debug, error, info};
6
7use crate::fs::{ensure_dir, error::Error};
8
9pub trait Json: Sized {
11 fn from_json_path<P: AsRef<Path> + Send>(
12 path: P,
13 ) -> impl Future<Output = Result<Self, Error>> + Send
14 where
15 Self: DeserializeOwned,
16 {
17 async {
18 info!("reading {}", path.as_ref().display());
19
20 if !path.as_ref().exists() {
21 error!("file not found: {}", path.as_ref().display())
22 }
23 let file_contents = fs::read(path).await?;
24
25 debug!("{}", String::from_utf8_lossy(&file_contents));
26
27 let result = serde_json::from_slice(&file_contents)?;
28
29 Ok(result)
30 }
31 }
32
33 fn to_json_path<P: AsRef<Path> + Send>(
34 &self,
35 path: P,
36 ) -> impl Future<Output = Result<(), Error>>
37 where
38 Self: Serialize,
39 {
40 async {
41 let json = serde_json::to_string_pretty(self)?;
42 let parent = path.as_ref().parent().unwrap();
43 ensure_dir(&parent, None).await?;
44 fs::write(path, json).await?;
45 Ok(())
46 }
47 }
48}
49
50impl<T> Json for T where T: Serialize + DeserializeOwned {}