use std::{io::BufWriter, path::Path};
use thiserror::Error;
use crate::{Anvil, Forge};
#[derive(Error, Debug)]
pub enum AppendError {
#[error("failed to perform file I/O while appending content: {0}")]
StdIo(#[from] std::io::Error),
#[error("failed to render template during append operation: {0}")]
Template(#[from] Box<dyn std::error::Error>),
}
pub struct Append<A: Anvil> {
template: A,
}
impl<A: Anvil> Forge for Append<A> {
type Error = AppendError;
fn forge(&self, into: impl AsRef<Path>) -> Result<(), Self::Error> {
let path = into.as_ref();
let file = std::fs::OpenOptions::new()
.append(true)
.open(path)
.map_err(AppendError::StdIo)?;
let mut writer = BufWriter::new(file);
self.template
.anvil(&mut writer)
.map_err(|e| AppendError::Template(Box::new(e)))?;
Ok(())
}
}
impl<A: Anvil> Append<A> {
pub fn new(template: A) -> Self {
Self { template }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::{tempdir, NamedTempFile};
struct MockAnvil {
content: String,
}
impl Anvil for MockAnvil {
type Error = std::io::Error;
fn anvil(&self, writer: &mut (impl std::io::Write + Sized)) -> Result<(), Self::Error> {
writer.write_all(self.content.as_bytes())?;
Ok(())
}
}
#[test]
fn test_append_adds_content_to_existing_file() {
let mut temp_file = NamedTempFile::new().unwrap();
let initial_content = "Initial content\n";
temp_file.write_all(initial_content.as_bytes()).unwrap();
let template_content = "Appended content";
let template = MockAnvil {
content: template_content.to_string(),
};
let append = Append::new(template);
let result = append.forge(temp_file.path());
assert!(result.is_ok());
let content = std::fs::read_to_string(temp_file.path()).unwrap();
assert_eq!(content, format!("{}{}", initial_content, template_content));
}
#[test]
fn test_append_fails_on_nonexistent_file() {
let nonexistent_path = tempdir().unwrap().path().join("nonexistent_file.txt");
let template = MockAnvil {
content: "Some content".to_string(),
};
let append = Append::new(template);
let result = append.forge(nonexistent_path);
assert!(result.is_err());
match result {
Err(AppendError::StdIo(err)) => assert_eq!(err.kind(), std::io::ErrorKind::NotFound),
other => unreachable!("Expected AppendError::StdIo but got: {:?}", other),
}
}
}