1use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5use std::path::{Path, PathBuf};
6use tokio::fs;
7
8pub async fn ensure_dir_exists(path: &Path) -> Result<()> {
10 if !path.exists() {
11 fs::create_dir_all(path)
12 .await
13 .with_context(|| format!("Failed to create directory: {}", path.display()))?;
14 }
15 Ok(())
16}
17
18pub async fn read_file_with_context(path: &Path, context: &str) -> Result<String> {
20 fs::read_to_string(path)
21 .await
22 .with_context(|| format!("Failed to read {}: {}", context, path.display()))
23}
24
25pub async fn write_file_with_context(path: &Path, content: &str, context: &str) -> Result<()> {
27 if let Some(parent) = path.parent() {
28 ensure_dir_exists(parent).await?;
29 }
30 fs::write(path, content)
31 .await
32 .with_context(|| format!("Failed to write {}: {}", context, path.display()))
33}
34
35pub async fn write_json_file<T: Serialize>(path: &Path, data: &T) -> Result<()> {
37 let json = serde_json::to_string_pretty(data)
38 .with_context(|| format!("Failed to serialize data for {}", path.display()))?;
39
40 write_file_with_context(path, &json, "JSON data").await
41}
42
43pub async fn read_json_file<T: for<'de> Deserialize<'de>>(path: &Path) -> Result<T> {
45 let content = read_file_with_context(path, "JSON file").await?;
46
47 serde_json::from_str(&content)
48 .with_context(|| format!("Failed to parse JSON from {}", path.display()))
49}
50
51pub fn parse_json_with_context<T: for<'de> Deserialize<'de>>(
53 content: &str,
54 context: &str,
55) -> Result<T> {
56 serde_json::from_str(content).with_context(|| format!("Failed to parse JSON from {}", context))
57}
58
59pub fn serialize_json_with_context<T: Serialize>(data: &T, context: &str) -> Result<String> {
61 serde_json::to_string(data).with_context(|| format!("Failed to serialize JSON for {}", context))
62}
63
64pub fn serialize_json_pretty_with_context<T: Serialize>(data: &T, context: &str) -> Result<String> {
66 serde_json::to_string_pretty(data)
67 .with_context(|| format!("Failed to pretty-serialize JSON for {}", context))
68}
69
70pub fn canonicalize_with_context(path: &Path, context: &str) -> Result<PathBuf> {
72 path.canonicalize().with_context(|| {
73 format!(
74 "Failed to canonicalize {} path: {}",
75 context,
76 path.display()
77 )
78 })
79}
80
81pub fn ensure_dir_exists_sync(path: &Path) -> Result<()> {
85 if !path.exists() {
86 std::fs::create_dir_all(path)
87 .with_context(|| format!("Failed to create directory: {}", path.display()))?;
88 }
89 Ok(())
90}
91
92pub fn read_file_with_context_sync(path: &Path, context: &str) -> Result<String> {
94 std::fs::read_to_string(path)
95 .with_context(|| format!("Failed to read {}: {}", context, path.display()))
96}
97
98pub fn write_file_with_context_sync(path: &Path, content: &str, context: &str) -> Result<()> {
100 if let Some(parent) = path.parent() {
101 ensure_dir_exists_sync(parent)?;
102 }
103 std::fs::write(path, content)
104 .with_context(|| format!("Failed to write {}: {}", context, path.display()))
105}
106
107pub fn write_json_file_sync<T: Serialize>(path: &Path, data: &T) -> Result<()> {
109 let json = serde_json::to_string_pretty(data)
110 .with_context(|| format!("Failed to serialize data for {}", path.display()))?;
111
112 write_file_with_context_sync(path, &json, "JSON data")
113}
114
115pub fn read_json_file_sync<T: for<'de> Deserialize<'de>>(path: &Path) -> Result<T> {
117 let content = read_file_with_context_sync(path, "JSON file")?;
118
119 serde_json::from_str(&content)
120 .with_context(|| format!("Failed to parse JSON from {}", path.display()))
121}