use crate::{
error::{ImageOpenError, IoError, Result},
input_paths,
output_spec::OutputSpec,
run_pipeline, save_images,
};
use image::open;
use snafu::ResultExt;
use std::fmt::Write;
use std::path::Path;
const DEFAULT_INPUTS: &[&str] = &["robin.png", "robin_gray.png", "yellow.png"];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Example {
pub num_inputs: usize,
pub num_outputs: usize,
pub pipeline: String,
pub override_input_names: Option<Vec<String>>,
pub output_file_extension: Option<String>,
pub variable_outputs: bool,
pub style: Style,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Style {
LongForm,
ShortForm,
}
impl Example {
pub fn new(num_inputs: usize, num_outputs: usize, pipeline: &'static str) -> Example {
Example {
num_inputs,
num_outputs,
pipeline: pipeline.into(),
override_input_names: None,
output_file_extension: None,
variable_outputs: false,
style: Style::ShortForm,
}
}
pub fn instantiate(
&self,
input_dir: String,
output_dir: String,
output_root_name: String,
) -> InstantiatedExample {
let input_file_names = match &self.override_input_names {
Some(n) => n.clone(),
None => DEFAULT_INPUTS[..self.num_inputs]
.iter()
.map(|f| f.to_string())
.collect(),
};
let ext = self
.output_file_extension
.as_ref()
.map(|e| e.as_str())
.unwrap_or("png");
let output_file_names = if self.variable_outputs {
vec![format!("{}_{{n}}.{}", output_root_name, ext)]
} else {
(0..self.num_outputs)
.map(|c| format!("{}_{}.{}", output_root_name, c, ext))
.collect()
};
InstantiatedExample {
input_dir,
input_file_names,
output_dir,
output_file_names,
pipeline: self.pipeline.clone(),
style: self.style,
variable_outputs: self.variable_outputs,
num_outputs: self.num_outputs,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstantiatedExample {
pub input_dir: String,
pub input_file_names: Vec<String>,
pub output_dir: String,
pub output_file_names: Vec<String>,
pub pipeline: String,
pub style: Style,
pub variable_outputs: bool,
pub num_outputs: usize,
}
impl InstantiatedExample {
pub fn render_for_documentation(&self) -> String {
let mut result = String::new();
writeln!(
result,
"<pre>{}</pre>",
self.command_line_for_documentation()
)
.unwrap();
if self.variable_outputs {
for i in 0..self.num_outputs {
writeln!(
result,
"<img src='{}'/>",
self.output_paths()[0].replace("{n}", &i.to_string())
)
.unwrap();
}
} else {
for link in &self.output_paths() {
writeln!(result, "<img src='{}'/>", link).unwrap();
}
}
result
}
pub fn run(&self, verbose: bool) -> Result<()> {
let input_patterns: Vec<_> = self
.input_file_names
.iter()
.map(|f| Path::new(&self.input_dir).join(f).display().to_string())
.collect();
let input_paths = input_paths(&input_patterns)?;
if verbose {
println!("Input paths:\n\t{:?}", input_paths);
}
let inputs = input_paths
.iter()
.map(|p| open(&p).context(ImageOpenError { path: &p }))
.collect::<Result<Vec<_>>>()?;
let output_paths: Vec<_> = self
.output_file_names
.iter()
.map(|f| Path::new(&self.output_dir).join(f).display().to_string())
.collect();
if verbose {
println!("Output paths:\n\t{:?}", output_paths);
}
let output_spec = OutputSpec::parse(&output_paths)?;
let outputs = run_pipeline(&self.pipeline, inputs, verbose)?;
if !Path::new(&self.output_dir).is_dir() {
std::fs::create_dir(&self.output_dir).context(IoError {
context: format!("Unable to create directory '{}", &self.output_dir),
})?;
}
save_images(&output_spec, &outputs, verbose)?;
Ok(())
}
pub fn command_line_for_documentation(&self) -> String {
let (input_opt, output_opt, pipeline_opt) = match self.style {
Style::LongForm => ("--input", "--output", "--pipeline"),
Style::ShortForm => ("-i", "-o", "-p"),
};
let input = if !self.input_file_names.is_empty() {
format!(" {} {}", input_opt, self.input_file_names.join(" "))
} else {
String::new()
};
let pipeline = if !self.pipeline.is_empty() {
format!(" {} '{}'", pipeline_opt, self.pipeline)
} else {
String::new()
};
format!(
"imagecli{} {} {}{}",
input,
output_opt,
self.output_file_names.join(" "),
pipeline,
)
}
fn output_paths(&self) -> Vec<String> {
self.output_file_names
.iter()
.map(|f| Path::new(&self.output_dir).join(f).display().to_string())
.collect()
}
}