1use crate::config::Config;
6use crate::error::{io_error_with_path, Result};
7use std::path::Path;
8
9pub fn load_config_from_file<T: Config>(path: impl AsRef<Path>) -> Result<T> {
26 let path = path.as_ref();
27 let content = std::fs::read_to_string(path)
28 .map_err(|e| io_error_with_path(e, path, "read config from"))?;
29 let config: T = serde_json::from_str(&content)?;
30 config.validate()?;
31 Ok(config)
32}
33
34pub fn save_config_to_file<T: Config>(config: &T, path: impl AsRef<Path>) -> Result<()> {
52 config.validate()?;
53 let content = serde_json::to_string_pretty(config)?;
54 let path = path.as_ref();
55 std::fs::write(path, content).map_err(|e| io_error_with_path(e, path, "write config to"))?;
56 Ok(())
57}
58
59pub fn read_json<T>(path: impl AsRef<Path>) -> Result<T>
61where
62 T: serde::de::DeserializeOwned,
63{
64 let path = path.as_ref();
65 let content =
66 std::fs::read_to_string(path).map_err(|e| io_error_with_path(e, path, "read JSON from"))?;
67 serde_json::from_str(&content).map_err(Into::into)
68}
69
70pub fn write_json<T>(path: impl AsRef<Path>, value: &T, pretty: bool) -> Result<()>
72where
73 T: serde::Serialize,
74{
75 let path = path.as_ref();
76 let content = if pretty {
77 serde_json::to_string_pretty(value)?
78 } else {
79 serde_json::to_string(value)?
80 };
81 std::fs::write(path, content).map_err(|e| io_error_with_path(e, path, "write JSON to"))?;
82 Ok(())
83}
84
85pub fn ensure_dir(path: impl AsRef<Path>) -> Result<()> {
87 let path = path.as_ref();
88 if !path.exists() {
89 std::fs::create_dir_all(path)?;
90 }
91 Ok(())
92}
93
94pub fn canonical_path(path: impl AsRef<Path>) -> Result<std::path::PathBuf> {
96 path.as_ref().canonicalize().map_err(Into::into)
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use serde::{Deserialize, Serialize};
103
104 #[derive(Debug, Serialize, Deserialize, PartialEq)]
105 struct TestConfig {
106 name: String,
107 value: u32,
108 }
109
110 impl Config for TestConfig {}
111
112 #[test]
113 fn test_config_roundtrip() {
114 let config = TestConfig {
115 name: "test".to_string(),
116 value: 42,
117 };
118
119 let json = serde_json::to_string(&config).unwrap();
120 let deserialized: TestConfig = serde_json::from_str(&json).unwrap();
121
122 assert_eq!(config, deserialized);
123 }
124}