|
@@ -1,6 +1,7 @@
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
-use crate::image::Pixel;
|
|
|
+use crate::image::{Image, Pixel};
|
|
|
+use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
struct ColorEntry {
|
|
@@ -9,19 +10,61 @@ struct ColorEntry {
|
|
|
}
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
-pub struct Mapping(Vec<ColorEntry>);
|
|
|
+pub struct Mapping(HashMap<Pixel, String>);
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct IncompleteMappingError {
|
|
|
+ path: std::path::PathBuf,
|
|
|
+ missing_colors: Vec<Pixel>,
|
|
|
+}
|
|
|
+
|
|
|
+impl std::fmt::Display for IncompleteMappingError {
|
|
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
+ writeln!(
|
|
|
+ f,
|
|
|
+ "`{:?}` is missing entries for the following pixel colors:",
|
|
|
+ self.path
|
|
|
+ )?;
|
|
|
+ for color in self.missing_colors.iter() {
|
|
|
+ writeln!(f, " - {:?}", color)?;
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl std::error::Error for IncompleteMappingError {}
|
|
|
|
|
|
impl Mapping {
|
|
|
- pub fn load(path: impl AsRef<std::path::Path>) -> Result<Mapping, Box<dyn std::error::Error>> {
|
|
|
- Ok(serde_yaml::from_reader(std::fs::File::open(path)?)?)
|
|
|
+ pub fn load(
|
|
|
+ path: impl AsRef<std::path::Path>,
|
|
|
+ img: &Image,
|
|
|
+ ) -> Result<Mapping, Box<dyn std::error::Error>> {
|
|
|
+ let path = path.as_ref().to_path_buf();
|
|
|
+ let data: Vec<ColorEntry> = serde_yaml::from_reader(std::fs::File::open(&path)?)?;
|
|
|
+ // do validation to make sure all pixel colors are handled
|
|
|
+ let color_map = data
|
|
|
+ .into_iter()
|
|
|
+ .map(|entry| (entry.color, entry.symbol))
|
|
|
+ .collect::<HashMap<Pixel, String>>();
|
|
|
+ let all_image_colors = img
|
|
|
+ .iter()
|
|
|
+ .map(|(_, color)| color)
|
|
|
+ .collect::<HashSet<Pixel>>();
|
|
|
+ let missing_colors = all_image_colors
|
|
|
+ .into_iter()
|
|
|
+ .filter(|c| !color_map.contains_key(&c))
|
|
|
+ .collect::<Vec<Pixel>>();
|
|
|
+ if !missing_colors.is_empty() {
|
|
|
+ return Err(Box::new(IncompleteMappingError {
|
|
|
+ path,
|
|
|
+ missing_colors,
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ Ok(Mapping(color_map))
|
|
|
}
|
|
|
|
|
|
pub fn lookup(&self, color: Pixel) -> &str {
|
|
|
- let found = self.0.iter().find(|entry| entry.color == color);
|
|
|
- match found {
|
|
|
- Some(entry) => &entry.symbol,
|
|
|
- None => panic!("Unable to find entry for {:?}", color),
|
|
|
- }
|
|
|
+ &self.0[&color]
|
|
|
}
|
|
|
}
|
|
|
|