Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/forge_app/src/infra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ pub trait FileWriterInfra: Send + Sync {
/// Writes the content of a file at the specified path.
async fn write(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>;

/// Appends content to a file at the specified path, creating it if it does
/// not exist.
async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()>;

/// Writes content to a temporary file with the given prefix and extension,
/// and returns its path. The file will be kept (not deleted) after
/// creation.
Expand Down
19 changes: 19 additions & 0 deletions crates/forge_fs/src/write.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::Path;

use anyhow::{Context, Result};
use tokio::io::AsyncWriteExt as _;

impl crate::ForgeFS {
pub async fn create_dir_all<T: AsRef<Path>>(path: T) -> Result<()> {
Expand All @@ -15,6 +16,24 @@ impl crate::ForgeFS {
.with_context(|| format!("Failed to write file {}", path.as_ref().display()))
}

/// Appends content to an existing file, or creates it if it does not exist.
pub async fn append<T: AsRef<Path>, U: AsRef<[u8]>>(path: T, contents: U) -> Result<()> {
let mut file = tokio::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path.as_ref())
.await
.with_context(|| {
format!(
"Failed to open file for appending {}",
path.as_ref().display()
)
})?;
file.write_all(contents.as_ref())
.await
.with_context(|| format!("Failed to append to file {}", path.as_ref().display()))
}

pub async fn remove_file<T: AsRef<Path>>(path: T) -> Result<()> {
tokio::fs::remove_file(path.as_ref())
.await
Expand Down
4 changes: 4 additions & 0 deletions crates/forge_infra/src/forge_infra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ impl FileWriterInfra for ForgeInfra {
self.file_write_service.write(path, contents).await
}

async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> {
self.file_write_service.append(path, contents).await
}

async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result<PathBuf> {
self.file_write_service
.write_temp(prefix, ext, content)
Expand Down
5 changes: 5 additions & 0 deletions crates/forge_infra/src/fs_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ impl FileWriterInfra for ForgeFileWriteService {
Ok(forge_fs::ForgeFS::write(path, contents.to_vec()).await?)
}

async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> {
self.create_parent_dirs(path).await?;
Ok(forge_fs::ForgeFS::append(path, contents.to_vec()).await?)
}

async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result<PathBuf> {
let path = tempfile::Builder::new()
.disable_cleanup(true)
Expand Down
10 changes: 9 additions & 1 deletion crates/forge_infra/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ impl<F: forge_app::FileWriterInfra + 'static> ForgeHttpInfra<F> {
let body_clone = body.clone();
let debug_path = debug_path.clone();
tokio::spawn(async move {
let _ = file_writer.write(&debug_path, body_clone).await;
let _ = file_writer.append(&debug_path, body_clone).await;
});
}
}
Expand Down Expand Up @@ -332,6 +332,14 @@ mod tests {
Ok(())
}

async fn append(&self, path: &std::path::Path, contents: Bytes) -> anyhow::Result<()> {
self.writes
.lock()
.await
.push((path.to_path_buf(), contents));
Ok(())
}

async fn write_temp(
&self,
_prefix: &str,
Expand Down
3 changes: 3 additions & 0 deletions crates/forge_repo/src/forge_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ where
async fn write(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> {
self.infra.write(path, contents).await
}
async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> {
self.infra.append(path, contents).await
}
async fn write_temp(&self, prefix: &str, ext: &str, content: &str) -> anyhow::Result<PathBuf> {
self.infra.write_temp(prefix, ext, content).await
}
Expand Down
8 changes: 8 additions & 0 deletions crates/forge_repo/src/provider/provider_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,10 @@ mod env_tests {
Ok(())
}

async fn append(&self, _path: &std::path::Path, _content: Bytes) -> anyhow::Result<()> {
Ok(())
}

async fn write_temp(
&self,
_prefix: &str,
Expand Down Expand Up @@ -1343,6 +1347,10 @@ mod env_tests {
Ok(())
}

async fn append(&self, _path: &std::path::Path, _content: Bytes) -> anyhow::Result<()> {
Ok(())
}

async fn write_temp(
&self,
_prefix: &str,
Expand Down
15 changes: 15 additions & 0 deletions crates/forge_services/src/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,21 @@ pub mod tests {
Ok(())
}

async fn append(&self, path: &Path, contents: Bytes) -> anyhow::Result<()> {
let mut existing = bytes::Bytes::new();
let index = self.files.lock().unwrap().iter().position(|v| v.0 == path);
if let Some(index) = index {
existing = self.files.lock().unwrap().remove(index).1;
}
let mut combined = existing.to_vec();
combined.extend_from_slice(&contents);
self.files
.lock()
.unwrap()
.push((path.to_path_buf(), combined.into()));
Ok(())
}

async fn write_temp(&self, _: &str, _: &str, content: &str) -> anyhow::Result<PathBuf> {
let temp_dir = crate::utils::TempDir::new().unwrap();
let path = temp_dir.path();
Expand Down
Loading