use std::path::{Path, PathBuf};
#[derive(Default)]
pub struct TestFileBuilder {
path: PathBuf,
lines: Vec<String>,
add_final_newline: bool,
line_ending: LineEnding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LineEnding {
#[default]
Lf, Crlf, }
impl TestFileBuilder {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
Self {
path: path.as_ref().to_path_buf(),
lines: Vec::new(),
add_final_newline: true,
line_ending: LineEnding::default(),
}
}
pub fn with_line(mut self, line: &str) -> Self {
self.lines.push(line.to_string());
self
}
pub fn with_lines(mut self, lines: &[&str]) -> Self {
for line in lines {
self.lines.push(line.to_string());
}
self
}
pub fn with_trailing_spaces(mut self) -> Self {
self.lines.push("line with trailing spaces ".to_string());
self
}
pub fn with_trailing_tabs(mut self) -> Self {
self.lines.push("line with trailing tabs\t\t".to_string());
self
}
pub fn with_empty_line(mut self) -> Self {
self.lines.push(String::new());
self
}
pub fn without_final_newline(mut self) -> Self {
self.add_final_newline = false;
self
}
pub fn with_crlf_endings(mut self) -> Self {
self.line_ending = LineEnding::Crlf;
self
}
pub fn with_lf_endings(mut self) -> Self {
self.line_ending = LineEnding::Lf;
self
}
pub fn build(self) -> (PathBuf, String) {
let line_ending = match self.line_ending {
LineEnding::Lf => "\n",
LineEnding::Crlf => "\r\n",
};
let mut content = String::new();
for (index, line) in self.lines.iter().enumerate() {
content.push_str(line);
if index < self.lines.len() - 1 || self.add_final_newline {
content.push_str(line_ending);
}
}
(self.path, content)
}
pub fn with_common_issues(self) -> Self {
self.with_line("normal line")
.with_trailing_spaces()
.with_empty_line()
.with_trailing_tabs()
.with_line("another normal line")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_builder() {
let (path, content) = TestFileBuilder::new("test.txt")
.with_line("line 1")
.with_trailing_spaces()
.build();
assert_eq!(path, PathBuf::from("test.txt"));
assert!(content.contains("line 1\n"));
assert!(content.contains(" \n"));
}
#[test]
fn test_file_builder_basic() {
let (path, content) = TestFileBuilder::new("basic.txt")
.with_line("hello")
.with_line("world")
.build();
assert_eq!(path, PathBuf::from("basic.txt"));
assert_eq!(content, "hello\nworld\n");
}
#[test]
fn test_file_builder_without_final_newline() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_line("line 1")
.with_line("line 2")
.without_final_newline()
.build();
assert_eq!(content, "line 1\nline 2");
}
#[test]
fn test_file_builder_with_crlf() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_line("line 1")
.with_line("line 2")
.with_crlf_endings()
.build();
assert_eq!(content, "line 1\r\nline 2\r\n");
}
#[test]
fn test_file_builder_trailing_whitespace() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_trailing_spaces()
.with_trailing_tabs()
.build();
assert!(content.contains(" \n"));
assert!(content.contains("\t\t\n"));
}
#[test]
fn test_file_builder_empty_lines() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_line("before")
.with_empty_line()
.with_line("after")
.build();
assert_eq!(content, "before\n\nafter\n");
}
#[test]
fn test_file_builder_with_lines() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_lines(&["line 1", "line 2", "line 3"])
.build();
assert_eq!(content, "line 1\nline 2\nline 3\n");
}
#[test]
fn test_file_builder_common_issues() {
let (_, content) = TestFileBuilder::new("test.txt")
.with_common_issues()
.build();
assert!(content.contains("normal line\n"));
assert!(content.contains(" \n"));
assert!(content.contains("\t\t\n"));
assert!(content.contains("\n\n")); }
}