From 4a0349413e060b7ecae1cbc6b52cd298f2d85968 Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Mon, 8 Aug 2022 02:06:07 +0100
Subject: [PATCH] Make all pipeline paths relative to the pipeline file

---
 src/commands/pipeline.rs | 345 +++++++++++++++++++++------------------
 src/utils.rs             |  29 ++++
 2 files changed, 217 insertions(+), 157 deletions(-)

diff --git a/src/commands/pipeline.rs b/src/commands/pipeline.rs
index 3d27ac6..bf86883 100644
--- a/src/commands/pipeline.rs
+++ b/src/commands/pipeline.rs
@@ -7,6 +7,7 @@ use thiserror::Error;
 
 use crate::cli_args::CrunchCommand;
 use crate::format::make_paths;
+use crate::utils::normalize_path;
 use crate::{commands, load_image};
 
 #[derive(Error, Debug)]
@@ -59,7 +60,7 @@ pub fn execute_pipeline<IN: ToString, OUT: ToString>(
 		Err(PipelineError::FormatDetection)?;
 	}
 
-	let file_contents = std::fs::read(path)?;
+	let file_contents = std::fs::read(&path)?;
 
 	log::debug!("Found correct file type and read bytes, trying to parse");
 	let pipeline_data: PipelineFile = if path_string.ends_with(".toml") {
@@ -69,188 +70,216 @@ pub fn execute_pipeline<IN: ToString, OUT: ToString>(
 	};
 
 	log::debug!("Expanding pipeline file into targets");
-	get_targets(&pipeline_data).for_each(|(input_path, output_path, actions)| {
-		match make_paths(&output_path) {
-			Ok(_) => {}
-			Err(e) => {
-				log::error!("Failed to create target directory {}; {}", &output_path, e);
-				return;
-			}
-		}
-
-		if actions.is_empty() {
-			match std::fs::copy(&input_path, &output_path) {
+	let base_path = PathBuf::from(path.parent().unwrap());
+	get_targets(base_path.clone(), &pipeline_data).for_each(
+		|(input_path, output_path, actions)| {
+			match make_paths(&output_path) {
 				Ok(_) => {}
 				Err(e) => {
-					log::error!("Failed to copy {} to {}; {}", input_path, output_path, e);
+					log::error!("Failed to create target directory {}; {}", &output_path, e);
+					return;
 				}
-			};
-			return;
-		}
+			}
 
-		let mut file = match load_image(&input_path, None) {
-			Ok(image) => image,
-			Err(e) => {
-				log::error!("Error loading {}; {:?}", &input_path, e);
+			if actions.is_empty() {
+				match std::fs::copy(&input_path, &output_path) {
+					Ok(_) => {}
+					Err(e) => {
+						log::error!("Failed to copy {} to {}; {}", input_path, output_path, e);
+					}
+				};
 				return;
 			}
-		};
 
-		log::debug!(
-			"Loaded {}, Executing {} actions",
-			&input_path,
-			actions.len()
-		);
-		let mut count = 1;
-		for step in actions {
-			match step {
-				CrunchCommand::Extrude {
-					tile_size,
-					space_y,
-					space_x,
-					pad_y,
-					pad_x,
-					extrude,
-				} => {
-					file = match commands::extrude(
-						file, tile_size, pad_x, pad_y, space_x, space_y, extrude,
-					) {
-						Ok(f) => f,
-						Err(e) => {
-							log::error!(
-								"Failed to extrude {} at step {}; {}",
-								input_path,
-								count,
-								e
-							);
-							return;
-						}
-					};
+			let mut file = match load_image(&input_path, None) {
+				Ok(image) => image,
+				Err(e) => {
+					log::error!("Error loading {}; {:?}", &input_path, e);
+					return;
 				}
-				CrunchCommand::Remap { palette_file } => {
-					let palette_data = match load_image(&palette_file, None) {
-						Ok(p) => p,
-						Err(e) => {
-							log::error!("Failed to load {} at step {}; {:?}", input_path, count, e);
-							return;
-						}
-					};
+			};
 
-					let image_palette = match commands::palette(&file) {
-						Ok(ip) => ip,
-						Err(e) => {
-							log::error!(
-								"Failed to extract palette from {} at step {}; {}",
-								input_path,
-								count,
-								e
-							);
-							return;
-						}
-					};
+			log::debug!(
+				"Loaded {}, Executing {} actions",
+				&input_path,
+				actions.len()
+			);
+			let mut count = 1;
+			for step in actions {
+				match step {
+					CrunchCommand::Extrude {
+						tile_size,
+						space_y,
+						space_x,
+						pad_y,
+						pad_x,
+						extrude,
+					} => {
+						file = match commands::extrude(
+							file, tile_size, pad_x, pad_y, space_x, space_y, extrude,
+						) {
+							Ok(f) => f,
+							Err(e) => {
+								log::error!(
+									"Failed to extrude {} at step {}; {}",
+									input_path,
+									count,
+									e
+								);
+								return;
+							}
+						};
+					}
+					CrunchCommand::Remap { palette_file } => {
+						let palette_data = match load_image(join(&base_path, &palette_file), None) {
+							Ok(p) => p,
+							Err(e) => {
+								log::error!(
+									"Failed to load palette {} at step {}; {:?}",
+									&palette_file,
+									count,
+									e
+								);
+								return;
+							}
+						};
 
-					let target_palette = match commands::palette(&palette_data) {
-						Ok(tp) => tp,
-						Err(e) => {
-							log::error!(
-								"Failed to extract palette from {} at step {}; {}",
-								&palette_file,
-								count,
-								e
-							);
-							return;
-						}
-					};
+						let image_palette = match commands::palette(&file) {
+							Ok(ip) => ip,
+							Err(e) => {
+								log::error!(
+									"Failed to extract palette from {} at step {}; {}",
+									input_path,
+									count,
+									e
+								);
+								return;
+							}
+						};
 
-					let mappings = commands::calculate_mapping(&image_palette, &target_palette);
-					file = match commands::remap_image(file, mappings) {
-						Ok(f) => f,
-						Err(e) => {
-							log::error!("Failed to remap {} at step {}; {}", input_path, count, e);
-							return;
-						}
-					};
-				}
-				CrunchCommand::Scale { factor } => {
-					file = match commands::rescale(&file, factor) {
-						Ok(f) => f,
-						Err(e) => {
-							log::error!("Failed to scale {} at step {}; {}", input_path, count, e);
-							return;
-						}
-					};
-				}
-				CrunchCommand::Rotate { amount } => {
-					file = match commands::rotate(&file, amount) {
-						Ok(f) => f,
-						Err(e) => {
-							log::error!(
-								"Failed to rotate {} by {:?} step(s); {}",
-								input_path,
-								amount,
-								e
-							);
-							return;
-						}
-					};
-				}
-				CrunchCommand::Flip { direction } => {
-					file = match commands::flip(&file, direction) {
-						Ok(f) => f,
-						Err(e) => {
-							log::error!(
-								"Failed to flip {} in the following direction: {:?}; {}",
-								input_path,
-								direction,
-								e
-							);
-							return;
-						}
-					};
+						let target_palette = match commands::palette(&palette_data) {
+							Ok(tp) => tp,
+							Err(e) => {
+								log::error!(
+									"Failed to extract palette from {} at step {}; {}",
+									&palette_file,
+									count,
+									e
+								);
+								return;
+							}
+						};
+
+						let mappings = commands::calculate_mapping(&image_palette, &target_palette);
+						file = match commands::remap_image(file, mappings) {
+							Ok(f) => f,
+							Err(e) => {
+								log::error!(
+									"Failed to remap {} at step {}; {}",
+									input_path,
+									count,
+									e
+								);
+								return;
+							}
+						};
+					}
+					CrunchCommand::Scale { factor } => {
+						file = match commands::rescale(&file, factor) {
+							Ok(f) => f,
+							Err(e) => {
+								log::error!(
+									"Failed to scale {} at step {}; {}",
+									input_path,
+									count,
+									e
+								);
+								return;
+							}
+						};
+					}
+					CrunchCommand::Rotate { amount } => {
+						file = match commands::rotate(&file, amount) {
+							Ok(f) => f,
+							Err(e) => {
+								log::error!(
+									"Failed to rotate {} by {:?} step(s); {}",
+									input_path,
+									amount,
+									e
+								);
+								return;
+							}
+						};
+					}
+					CrunchCommand::Flip { direction } => {
+						file = match commands::flip(&file, direction) {
+							Ok(f) => f,
+							Err(e) => {
+								log::error!(
+									"Failed to flip {} in the following direction: {:?}; {}",
+									input_path,
+									direction,
+									e
+								);
+								return;
+							}
+						};
+					}
+					CrunchCommand::Palette { .. } | CrunchCommand::Pipeline => continue,
 				}
-				CrunchCommand::Palette { .. } | CrunchCommand::Pipeline => continue,
-			}
 
-			count += 1;
-		}
+				count += 1;
+			}
 
-		let mut outer_target_path = PathBuf::from(&output_path);
-		outer_target_path.pop();
+			let mut outer_target_path = PathBuf::from(&output_path);
+			outer_target_path.pop();
 
-		if let Err(e) = std::fs::create_dir(&outer_target_path) {
-			match e.kind() {
-				std::io::ErrorKind::AlreadyExists => { /* This is fine */ }
-				_ => log::error!(
-					"Failed to create containing directory {}; {}",
-					outer_target_path.to_string_lossy(),
-					e
-				),
+			if let Err(e) = std::fs::create_dir(&outer_target_path) {
+				match e.kind() {
+					std::io::ErrorKind::AlreadyExists => { /* This is fine */ }
+					_ => log::error!(
+						"Failed to create containing directory {}; {}",
+						outer_target_path.to_string_lossy(),
+						e
+					),
+				}
 			}
-		}
 
-		match file.save(&output_path) {
-			Ok(_) => {}
-			Err(e) => {
-				log::error!("Failed to save to {}; {}", output_path, e);
+			match file.save(&output_path) {
+				Ok(_) => {}
+				Err(e) => {
+					log::error!("Failed to save to {}; {}", output_path, e);
+				}
 			}
-		}
-	});
+		},
+	);
 
 	Ok(())
 }
 
+fn join<T: AsRef<Path>>(root: &Path, rest: &T) -> String {
+	let path = normalize_path(root.join(rest));
+	format!("{}", path.display())
+}
+
 fn get_targets(
+	base_path: PathBuf,
 	pipeline_data: &PipelineFile,
 ) -> impl ParallelIterator<Item = (String, String, Vec<CrunchCommand>)> + '_ {
 	pipeline_data
 		.pipelines
 		.par_iter()
-		.flat_map(|pipe| match pipe {
+		.flat_map(move |pipe| match pipe {
 			PipelineType::Pipeline {
 				input_path,
 				output_path,
 				actions,
-			} => vec![(input_path.clone(), output_path.clone(), actions.clone())],
+			} => vec![(
+				join(&base_path, &input_path),
+				join(&base_path, &output_path),
+				actions.clone(),
+			)],
 			PipelineType::Ref {
 				input_path,
 				output_path,
@@ -261,8 +290,8 @@ fn get_targets(
 				.iter()
 				.map(|value| {
 					(
-						input_path.clone(),
-						output_path.clone(),
+						join(&base_path, &input_path),
+						join(&base_path, &output_path),
 						(*value).actions.clone(),
 					)
 				})
@@ -278,8 +307,10 @@ fn get_targets(
 				.map(|value| (*value).actions.clone())
 				.flat_map(|actions| {
 					let mut paths = Vec::new();
-					log::debug!("Mapping glob paths for '{}'", pattern.as_str());
-					for entry in glob::glob(pattern.as_str()).unwrap() {
+					let target_path = join(&base_path, pattern);
+
+					log::debug!("Mapping glob paths for '{}'", &target_path);
+					for entry in glob::glob(target_path.as_str()).unwrap() {
 						log::debug!("Found a glob match: [{:?}]", entry);
 						paths.push((actions.clone(), entry));
 					}
@@ -292,8 +323,8 @@ fn get_targets(
 						let output_path = output_path.join(filename);
 
 						Some((
-							format!("{}", path.display()),
-							format!("{}", output_path.display()),
+							join(&base_path, &path),
+							join(&base_path, &output_path),
 							actions,
 						))
 					} else {
diff --git a/src/utils.rs b/src/utils.rs
index 6048aa4..deff07c 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,4 +1,5 @@
 use std::fmt::{Formatter, LowerHex, UpperHex};
+use std::path::{Component, Path, PathBuf};
 
 use deltae::LabValue;
 use glam::Vec3;
@@ -249,3 +250,31 @@ pub struct Pipeline {
 	pub output_path: String,
 	pub actions: Vec<crate::cli_args::CrunchCommand>,
 }
+
+pub fn normalize_path<T: AsRef<Path>>(path: T) -> PathBuf {
+	let path = path.as_ref();
+	let mut components = path.components().peekable();
+	let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
+		components.next();
+		PathBuf::from(c.as_os_str())
+	} else {
+		PathBuf::new()
+	};
+
+	for component in components {
+		match component {
+			Component::Prefix(..) => unreachable!(),
+			Component::RootDir => {
+				ret.push(component.as_os_str());
+			}
+			Component::CurDir => {}
+			Component::ParentDir => {
+				ret.pop();
+			}
+			Component::Normal(c) => {
+				ret.push(c);
+			}
+		}
+	}
+	ret
+}
-- 
GitLab