llm_coding_tools_core/
fs.rs

1//! Filesystem abstraction layer.
2//!
3//! Provides unified APIs that work with both sync and async runtimes.
4//! When the `blocking` feature is disabled (default), async operations use tokio.
5//! When `blocking` is enabled, all operations are synchronous.
6
7use crate::error::ToolResult;
8use std::path::Path;
9
10// ============================================================================
11// Async implementations (blocking feature disabled)
12// ============================================================================
13
14/// Reads a file to string.
15#[cfg(not(feature = "blocking"))]
16pub async fn read_to_string(path: impl AsRef<Path>) -> ToolResult<String> {
17    Ok(tokio::fs::read_to_string(path).await?)
18}
19
20/// Writes content to a file.
21#[cfg(not(feature = "blocking"))]
22pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> ToolResult<()> {
23    Ok(tokio::fs::write(path, contents).await?)
24}
25
26/// Creates a directory and all parent directories.
27#[cfg(not(feature = "blocking"))]
28pub async fn create_dir_all(path: impl AsRef<Path>) -> ToolResult<()> {
29    Ok(tokio::fs::create_dir_all(path).await?)
30}
31
32/// Opens a file for buffered reading.
33#[cfg(not(feature = "blocking"))]
34pub async fn open_buffered(
35    path: impl AsRef<Path>,
36    capacity: usize,
37) -> ToolResult<tokio::io::BufReader<tokio::fs::File>> {
38    let file = tokio::fs::File::open(path).await?;
39    Ok(tokio::io::BufReader::with_capacity(capacity, file))
40}
41
42// ============================================================================
43// Sync implementations (blocking feature enabled)
44// ============================================================================
45
46/// Reads a file to string.
47#[cfg(feature = "blocking")]
48pub fn read_to_string(path: impl AsRef<Path>) -> ToolResult<String> {
49    Ok(std::fs::read_to_string(path)?)
50}
51
52/// Writes content to a file.
53#[cfg(feature = "blocking")]
54pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> ToolResult<()> {
55    Ok(std::fs::write(path, contents)?)
56}
57
58/// Creates a directory and all parent directories.
59#[cfg(feature = "blocking")]
60pub fn create_dir_all(path: impl AsRef<Path>) -> ToolResult<()> {
61    Ok(std::fs::create_dir_all(path)?)
62}
63
64/// Opens a file for buffered reading.
65#[cfg(feature = "blocking")]
66pub fn open_buffered(
67    path: impl AsRef<Path>,
68    capacity: usize,
69) -> ToolResult<std::io::BufReader<std::fs::File>> {
70    let file = std::fs::File::open(path)?;
71    Ok(std::io::BufReader::with_capacity(capacity, file))
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use std::io::Write as _;
78    use tempfile::NamedTempFile;
79
80    #[maybe_async::test(feature = "blocking", async(not(feature = "blocking"), tokio::test))]
81    async fn read_to_string_works() {
82        let mut file = NamedTempFile::new().unwrap();
83        file.write_all(b"hello world").unwrap();
84        let content = read_to_string(file.path()).await.unwrap();
85        assert_eq!(content, "hello world");
86    }
87
88    #[maybe_async::test(feature = "blocking", async(not(feature = "blocking"), tokio::test))]
89    async fn write_works() {
90        let dir = tempfile::tempdir().unwrap();
91        let path = dir.path().join("test.txt");
92        write(&path, b"hello").await.unwrap();
93        assert_eq!(std::fs::read_to_string(&path).unwrap(), "hello");
94    }
95}