Getty Ritter 2 лет назад
Родитель
Сommit
b673634ad2
2 измененных файлов с 56 добавлено и 10 удалено
  1. 52 9
      src/data.rs
  2. 4 1
      src/main.rs

+ 52 - 9
src/data.rs

@@ -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]
     }
 }
 

+ 4 - 1
src/main.rs

@@ -11,10 +11,13 @@ use crate::image::Image;
 use crate::opts::Options;
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
+    // read the command-line options
     let opts = Options::parse();
 
+    // load the PNG image for the pattern
     let image = Image::load(opts.image)?;
-    let mapping = Mapping::load(opts.mapping)?;
+    // load the color map file
+    let mapping = Mapping::load(opts.mapping, &image)?;
 
     let config = Config {
         grid_every: opts.grid,