enum PwwError {
Unknown(String),
MissingSidecar(String),
+ InvalidImage,
}
impl std::fmt::Display for PwwError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
- PwwError::Unknown(s) => write!(f, "PWW unknown error: {}", s),
- PwwError::MissingSidecar(s) => write!(f, "PWW missing XMP sidecar error: {}", s),
+ PwwError::Unknown(s) => write!(f, "PWW error: {}", s),
+ PwwError::MissingSidecar(s) => write!(f, "missing XMP sidecar: {}", s),
+ PwwError::InvalidImage => write!(f, "input image invalid"),
}
}
}
#[arg(short = 'b', long)]
identifier_bin: Option<PathBuf>,
+ /// Do not actually write metadata to file, but print which files
+ /// would have changed.
+ #[arg(short = 'n', long)]
+ dry_run: bool,
+
/// Print information about which tags are written
#[arg(short = 'v', long)]
verbose: bool,
#[arg(short = 'd', long)]
debug: bool,
+ /// Whether to stop processing after first error, or continue.
+ #[arg(short = 'e', long)]
+ halt_on_error: bool,
+
/// Keep converted image files in temp directory instead of
/// removing them.
#[arg(long)]
/// Only update XMP sidecar (error if XMP file missing). Only
/// sidecar tags are used.
+ #[default]
SidecarOnly,
/// Only update image itself. Only image tags are used.
/// known types with good EXIF support (JPG, PNG, TIFF, WebP).
/// Either sidecar or image tags are used. Error if XMP file
/// missing.
- #[default]
SidecarUnlessCommonImage,
}
}
}
+ fn max_dimension(&self) -> u32 {
+ match self.max_dimension {
+ 0 => std::u32::MAX,
+ _ => self.max_dimension
+ }
+ }
+
+ fn min_dimension(&self) -> u32 {
+ match self.max_dimension {
+ 0 => std::u32::MAX,
+ _ => self.max_dimension
+ }
+ }
+
fn image_paths(&self) -> Vec<PathBuf> {
match &self.cli {
Some(c) => c.image_paths.clone(),
}
}
+ fn dry_run(&self) -> bool {
+ self.cli.as_ref().map(|c| c.dry_run).unwrap_or_default()
+ }
+
fn verbose(&self) -> bool {
self.cli.as_ref().map(|c| c.verbose).unwrap_or_default()
}
self.cli.as_ref().map(|c| c.debug).unwrap_or_default()
}
+ fn halt_on_error(&self) -> bool {
+ self.cli.as_ref().map(|c| c.halt_on_error).unwrap_or_default()
+ }
+
fn keep_converted(&self) -> bool {
self.cli.as_ref().map(|c| c.keep_converted).unwrap_or_default()
}
.stdout(Stdio::piped())
.spawn()
.map_err(|e| PwwError::Unknown(format!("Failed to launch identifier binary: {}", e)))?;
- p.wait()
+ let res = p.wait()
.map_err(|e| PwwError::Unknown(format!("Failed to wait for identifier binary: {}", e)))?;
+ if !res.success() {
+ return Err(PwwError::Unknown("identifier binary failed to execute.".into()));
+ }
let mut s: String = String::new();
let mut stdout = p.stdout.ok_or_else(|| PwwError::Unknown(format!("Failed to get output from identifier binary")))?;
stdout.read_to_string(&mut s).map_err(|x| PwwError::Unknown(x.to_string()))?;
DownscalePolicy::Always |
DownscalePolicy::Large |
DownscalePolicy::LargeOrConverted => {
- (true, max_dimension <= config.max_dimension)
+ (true, max_dimension <= config.max_dimension())
},
}
},
println!(" - updated tag: {}", metatag);
}
}
- meta.save_to_file(&filepath)
- .map_err(|e| PwwError::Unknown(format!("Failed to write metadata to image file: {}", e)))?;
- if config.verbose() {
- println!(" - saved: {}\n", filepath.as_ref().to_string_lossy());
+
+ match config.dry_run() {
+ false => {
+ meta.save_to_file(&filepath)
+ .map_err(|e| PwwError::Unknown(format!("Failed to write metadata to image file: {}", e)))?;
+ if config.verbose() {
+ println!(" - saved: {}\n", filepath.as_ref().to_string_lossy());
+ }
+ },
+ true => {
+ println!("would update: {} (dry run)\n", filepath.as_ref().to_string_lossy());
+ },
}
Ok(())
}
if config.debug() {
println!(" - converting to rgb8 jpg");
}
+ let i = Image::<u8, image2::Rgb>::open(&input_path)
+ .map_err(|_e| PwwError::InvalidImage)?;
let temp_dir = config.temp_dir.clone()
.unwrap_or(PathBuf::from(std::env::temp_dir()));
tmpfile = Some(tempfile::Builder::new()
.map_err(|e| PwwError::Unknown(format!("Failed to create temp file for image conversion: {}", e)))?);
let outpath = tmpfile.as_ref().map(|x| x.path().to_path_buf())
.ok_or_else(|| PwwError::Unknown(format!("Unable to open temp file for image conversion.")))?;
- let i = Image::<u8, image2::Rgb>::open(&input_path)
- .map_err(|e| PwwError::Unknown(format!("Unable to open source image file: {}", e)))?;
// convert to Rgb8
let conv = image2::filter::convert();
let i: Image<u8, image2::Rgb> = i.run(conv, None);
true
}
DownscalePolicy::Large => {
- max_dimension as u32 > config.max_dimension
+ max_dimension as u32 > config.max_dimension()
}
DownscalePolicy::Never => {
false
}
};
- if should_scale && min_dimension as u32 > config.min_dimension {
- let scale = config.min_dimension as f64 / min_dimension as f64;
+ if should_scale && min_dimension as u32 > config.min_dimension() {
+ let scale = config.min_dimension() as f64 / min_dimension as f64;
if config.debug() {
- println!(" - scaling by {:.2} ({} to {})", scale, min_dimension, config.min_dimension);
+ println!(" - scaling by {:.2} ({} to {})", scale, min_dimension, config.min_dimension());
}
i.scale(scale, scale);
}
.map_err(|e| PwwError::Unknown(format!("Error loading config file: {}", e)))?;
config.cli = Some(PwwArgs::parse());
+ let mut count = 0;
for input_path in config.image_paths() {
match process_image(&config, &input_path) {
Err(e) => {
println!("Error processing image ({}): {}", input_path.to_string_lossy(), e);
- if config.halt_on_error {
- break;
+ if config.halt_on_error() {
+ std::process::exit(1);
}
if config.verbose() || config.debug() {
println!();
}
},
- _ => {}
+ _ => {
+ count += 1;
+ }
}
}
+ if count == 0 {
+ println!("Error: no images processed.");
+ std::process::exit(1);
+ }
+
Ok(())
}