use std::path::{Path, PathBuf};
use tokio::fs;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String, String> {
let path = path.as_ref();
let mut file = fs::File::open(path)
.await
.map_err(|e| format!("Failed to open {}: {}", path.display(), e))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.await
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
Ok(contents)
}
pub async fn read_bytes<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, String> {
let path = path.as_ref();
fs::read(path)
.await
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))
}
pub async fn write_string<P: AsRef<Path>>(path: P, content: &str) -> Result<(), String> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.await
.map_err(|e| format!("Failed to create dir {}: {}", parent.display(), e))?;
}
let mut file = fs::File::create(path)
.await
.map_err(|e| format!("Failed to create {}: {}", path.display(), e))?;
file.write_all(content.as_bytes())
.await
.map_err(|e| format!("Failed to write {}: {}", path.display(), e))
}
pub async fn write_bytes<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<(), String> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.await
.map_err(|e| format!("Failed to create dir {}: {}", parent.display(), e))?;
}
fs::write(path, data)
.await
.map_err(|e| format!("Failed to write {}: {}", path.display(), e))
}
pub async fn exists<P: AsRef<Path>>(path: P) -> bool {
fs::try_exists(path).await.unwrap_or(false)
}
pub fn resolve(base: &Path, relative: &Path) -> PathBuf {
if relative.is_absolute() {
relative.to_path_buf()
} else {
base.join(relative)
}
}
pub fn walk_files(dir: &Path, extensions: &[&str]) -> Vec<PathBuf> {
let mut results = Vec::new();
if !dir.is_dir() {
return results;
}
walk_dir_recursive(dir, extensions, &mut results);
results
}
fn walk_dir_recursive(dir: &Path, extensions: &[&str], results: &mut Vec<PathBuf>) {
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
walk_dir_recursive(&path, extensions, results);
} else if let Some(ext) = path.extension() {
if extensions.iter().any(|e| ext == *e) {
results.push(path);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_write_and_read_string() {
let dir = std::env::temp_dir().join("iris-io-test");
let path = dir.join("hello.txt");
write_string(&path, "Hello Iris!").await.unwrap();
let content = read_to_string(&path).await.unwrap();
assert_eq!(content, "Hello Iris!");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn test_resolve() {
let base = Path::new("/project/src");
let rel = Path::new("../main.rs");
let abs = Path::new("/absolute/path.rs");
assert!(resolve(base, rel).ends_with("main.rs"));
assert_eq!(resolve(base, abs), abs);
}
#[test]
fn test_walk_files() {
let files = walk_files(Path::new("."), &["rs"]);
assert!(!files.is_empty());
}
}