use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use log::info;
use crate::config::Config;
use crate::exceptions::TopCatError;
use crate::file_dag::TCGraph;
use crate::fs::FileSystem;
fn append_string_to_file_content(file_content: String, append_str: &str) -> String {
let mut content = file_content.trim_end().to_string();
if !content.ends_with(append_str) {
content.push_str(append_str);
}
content.push('\n');
content
}
trait OutputDestination {
fn write_line(&mut self, content: &str) -> std::io::Result<()>;
fn write_str(&mut self, content: &str) -> std::io::Result<()>;
}
struct FileOutput {
writer: Box<dyn Write>,
}
impl FileOutput {
fn new(file_path: &Path) -> Result<Self, TopCatError> {
let file = File::create(file_path)?;
Ok(Self {
writer: Box::new(file),
})
}
}
impl OutputDestination for FileOutput {
fn write_line(&mut self, content: &str) -> std::io::Result<()> {
writeln!(self.writer, "{}", content)
}
fn write_str(&mut self, content: &str) -> std::io::Result<()> {
write!(self.writer, "{}", content)
}
}
struct ConsoleOutput;
impl OutputDestination for ConsoleOutput {
fn write_line(&mut self, content: &str) -> std::io::Result<()> {
println!("{}", content);
Ok(())
}
fn write_str(&mut self, content: &str) -> std::io::Result<()> {
print!("{}", content);
Ok(())
}
}
pub fn generate(
graph: TCGraph,
config: Config,
fs: &mut dyn FileSystem,
) -> Result<(), TopCatError> {
info!("Generating output");
let mut output_dest: Box<dyn OutputDestination>;
if config.dry_run {
info!("Dry run enabled, not writing to file");
output_dest = Box::new(ConsoleOutput {});
} else {
output_dest = Box::new(FileOutput::new(&config.output)?);
}
let command = env::args().skip(1).collect::<Vec<_>>().join(" ");
output_dest.write_line(&format!(
"{0} This file was generated by topcat. To regenerate run:\n{0}\n{0} topcat {1}\n",
config.comment_str, command
))?;
let file_sep_str = config.file_separator_str.as_str();
for file_name in graph.get_sorted_files()? {
let contents = fs.read_to_string(&file_name)?;
output_dest.write_line(file_sep_str)?;
output_dest
.write_line(format!("{} {}", config.comment_str, file_name.display()).as_str())?;
output_dest.write_str(&append_string_to_file_content(
contents,
&config.file_end_str,
))?;
output_dest.write_line("")?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ensure_file_separator() {
let d = append_string_to_file_content("SELECT 1 FROM table".to_string(), ";");
assert_eq!(d, "SELECT 1 FROM table;\n");
let d = append_string_to_file_content("SELECT 1 FROM table;".to_string(), ";");
assert_eq!(d, "SELECT 1 FROM table;\n");
let d = append_string_to_file_content("SELECT 1 FROM table;\n\n\n".to_string(), "\\");
assert_eq!(d, "SELECT 1 FROM table;\n");
}
}