extern crate clap;
use clap::Parser;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Error, Read, Write};
use std::path::PathBuf;
use html_streaming_editor::{report, HtmlRenderable, HtmlStreamingEditor};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[arg(short, long, value_name = "INPUT")]
input: Option<PathBuf>,
#[arg(short, long, value_name = "OUTPUT")]
output: Option<PathBuf>,
pipeline: String,
}
fn main() {
pretty_env_logger::init();
let cli = Cli::parse();
let input_path = cli.input.unwrap_or_else(|| PathBuf::from("-"));
let output_path = cli.output.unwrap_or_else(|| PathBuf::from("-"));
let mut pipeline_definition = cli.pipeline;
if pipeline_definition.starts_with('@') {
pipeline_definition = read_pipeline_from_file(&pipeline_definition);
}
let mut input_reader = open_input(input_path);
let editor = HtmlStreamingEditor::new(&mut input_reader);
match editor.run(&pipeline_definition) {
Ok(result) => {
let mut output_writer = open_output(output_path);
if let Err(e) = render_result(&result, &mut output_writer) {
eprintln!("[ERROR] {}", e);
}
}
Err(e) => report(&e),
}
}
fn render_result(
result: &Vec<Box<dyn HtmlRenderable>>,
output_writer: &mut Box<dyn Write>,
) -> Result<(), Error> {
for node in result {
let html = node.outer_html();
match output_writer.write((*html).as_bytes()) {
Ok(_) => {}
Err(e) => return Err(e),
}
match output_writer.write(b"\n") {
Ok(_) => {}
Err(e) => return Err(e),
}
}
output_writer.flush()?;
Ok(())
}
fn open_output(output_path: PathBuf) -> Box<dyn Write> {
let output_writer: Box<dyn Write> = if output_path.to_str() == Some("-") {
Box::new(std::io::stdout().lock())
} else {
let output_file = if let Ok(file) = File::create(output_path) {
file
} else {
eprintln!("[ERROR] Could not open output file");
std::process::exit(exitcode::CANTCREAT);
};
Box::new(BufWriter::new(output_file))
};
output_writer
}
fn open_input(input_path: PathBuf) -> Box<dyn BufRead> {
let input_reader: Box<dyn BufRead> = if input_path.to_str() == Some("-") {
Box::new(std::io::stdin().lock())
} else {
let input_file = if let Ok(file) = File::open(input_path) {
file
} else {
eprintln!("[ERROR] Could not open input file");
std::process::exit(exitcode::NOINPUT);
};
Box::new(BufReader::new(input_file))
};
input_reader
}
fn read_pipeline_from_file(file_definition: &String) -> String {
let filename = file_definition.get(1..).unwrap().to_owned();
let mut pipeline_definition = String::new();
if let Ok(mut file) = File::open(filename) {
if let Err(e) = file.read_to_string(&mut pipeline_definition) {
eprintln!("[ERROR] Could not read pipeline definition: {}", e);
std::process::exit(exitcode::NOINPUT);
}
} else {
eprintln!("[ERROR] Could not open pipeline definition");
std::process::exit(exitcode::NOINPUT);
};
return pipeline_definition;
}