Skip to main content

vtcode_commons/
fs.rs

1//! File utility functions for common operations
2
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5use std::path::{Path, PathBuf};
6use tokio::fs;
7
8/// Ensure a directory exists, creating it if necessary
9pub 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
18/// Read a file with contextual error message
19pub 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
25/// Write a file with contextual error message, ensuring parent directory exists
26pub 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
35/// Write a JSON file
36pub 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
43/// Read and parse a JSON file
44pub 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
51/// Parse JSON with context for better error messages
52pub 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
59/// Serialize JSON with context
60pub 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
64/// Serialize JSON pretty with context
65pub 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
70/// Canonicalize path with context
71pub 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
81// --- Sync Versions ---
82
83/// Ensure a directory exists (sync)
84pub 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
92/// Read a file with contextual error message (sync)
93pub 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
98/// Write a file with contextual error message (sync)
99pub 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
107/// Write a JSON file (sync)
108pub 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
115/// Read and parse a JSON file (sync)
116pub 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}