Implement apply_to_canvas to avoid redundant code
authorMegaBrutal <code+git@megabrutal.com>
Sun, 17 Jul 2022 22:30:00 +0000 (00:30 +0200)
committerMegaBrutal <code+git@megabrutal.com>
Sun, 17 Jul 2022 22:30:00 +0000 (00:30 +0200)
It takes a closure and applies it to the canvas.

src/main.rs

index 302fb3a7f7d82853dea59ff172a47a2550e69c0b..46185508532bc9120a598df2cbf42128ca89426e 100644 (file)
@@ -1,3 +1,4 @@
+use std::convert::From;
 use std::io::Cursor;
 use std::fmt::Display;
 use std::fmt;
@@ -43,7 +44,8 @@ impl ResponseError for ImageError {
 #[derive(Debug)]
 enum CanvasError {
     NotExists,
-    DimensionMismatch
+    DimensionMismatch,
+    ImageError(ImageError)
 }
 
 impl Display for CanvasError {
@@ -56,18 +58,26 @@ impl ResponseError for CanvasError {
     fn status_code(&self) -> StatusCode {
         match &self {
             CanvasError::NotExists => StatusCode::NOT_FOUND,
-            CanvasError::DimensionMismatch => StatusCode::BAD_REQUEST
+            CanvasError::DimensionMismatch => StatusCode::BAD_REQUEST,
+            CanvasError::ImageError(e) => e.status_code()
         }
     }
 
     fn error_response(&self) -> HttpResponse<BoxBody> {
         match &self {
             CanvasError::NotExists => HttpResponse::NotFound().body("Canvas does not exist."),
-            CanvasError::DimensionMismatch => HttpResponse::BadRequest().body("Supplied dimensions don't much the dimensions of existing canvas.")
+            CanvasError::DimensionMismatch => HttpResponse::BadRequest().body("Supplied dimensions don't much the dimensions of existing canvas."),
+            CanvasError::ImageError(e) => e.error_response()
         }
     }
 }
 
+impl From<ImageError> for CanvasError {
+    fn from(e: ImageError) -> CanvasError {
+        CanvasError::ImageError(e)
+    }
+}
+
 struct ToRgbIter<'a, T> where T: AsPrimitive<u8> {
     iter: Box<&'a mut dyn Iterator<Item = T>>
 }
@@ -99,6 +109,15 @@ fn to_imageresult<T> (result: Result<T, image::ImageError>) -> Result<T, ImageEr
     }
 }
 
+fn to_canvasresult<T: std::fmt::Debug> (result: Result<T, ImageError>) -> Result<T, CanvasError> {
+    println!("Converting to canvasresult: {:?}", result);
+    match result {
+        Ok(v) => Ok(v),
+        Err(e) => Err(CanvasError::ImageError(e))
+    }
+}
+
+#[derive(Debug)]
 struct Canvas {
     pen: usize,
     img: RgbImage
@@ -157,6 +176,34 @@ fn rgb_encode(img: RgbImage, data: &mut dyn Iterator<Item = u8>) -> Result<RgbIm
     Ok(rgb_encode_to_canvas(Canvas::from_image(img), data)?.img)
 }
 
+fn apply_to_canvas<'a, F>(f: F, canvas_option: &'a mut Option<Canvas>, dimensions: Option<(u32, u32)>, data: &str) -> Result<&'a mut Canvas, CanvasError>
+    where F: Fn(Canvas, &mut dyn Iterator<Item = u8>) -> Result<Canvas, CanvasError>
+{
+    let canvas = match canvas_option.take() {
+        Some(canvas) => match dimensions {
+            Some(dimensions) => {
+                if dimensions == canvas.img.dimensions() {
+                    Ok(canvas)
+                }
+                else {
+                    _ = canvas_option.insert(canvas);
+                    Err(CanvasError::DimensionMismatch)
+                }
+            },
+            None =>
+                Ok(canvas)
+        },
+        None => match dimensions {
+            Some((dim_x, dim_y)) =>
+                Ok(Canvas::new(dim_x, dim_y)),
+            None =>
+                Err(CanvasError::NotExists)
+        }
+    }?;
+
+    Ok(canvas_option.insert(f(canvas, percent_decode_str(&data).into_iter().borrow_mut())?))
+}
+
 fn make_png(dim_x: u32, dim_y: u32, scale: u32, data: &mut dyn Iterator<Item = u8>) -> Result<Cursor<Vec<u8>>, ImageError> {
     // Image must not be larger than 1 megapixel
     if dim_x * dim_y * scale > 1048576 {
@@ -197,12 +244,10 @@ async fn img_pgen0(req: HttpRequest, canvas0: web::Data<Arc<Mutex<Option<Canvas>
     let data = req.uri().path().split("/").skip(3).next().unwrap();
     let mut cursor = Cursor::new(Vec::new());
     let scale = 16;
-    let img: RgbImage = {
+    let img: RgbImage = ({
         let canvas_option = &mut *canvas0.lock().unwrap();
-        let canvas = canvas_option.take().ok_or(CanvasError::NotExists)?;
-        let canvas = rgb_encode_to_canvas(canvas, percent_decode_str(&data).into_iter().borrow_mut())?;
-        canvas_option.insert(canvas).img.clone()
-    };
+        Ok(apply_to_canvas( |c, d| to_canvasresult(rgb_encode_to_canvas(c, d)), canvas_option, None, data)?.img.clone())
+    } as Result<RgbImage, CanvasError>)?;
     let (tdim_x, tdim_y) = img.dimensions();
     let (tdim_x, tdim_y) = (tdim_x * scale, tdim_y * scale);
     let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
@@ -216,18 +261,10 @@ async fn img_pgen0(req: HttpRequest, canvas0: web::Data<Arc<Mutex<Option<Canvas>
 async fn img_pgen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>, canvas1: web::Data<Arc<Mutex<Option<Canvas>>>>) -> Result<impl Responder> {
     let (dim_x, dim_y, scale) = path.into_inner();
     let data = req.uri().path().split("/").skip(6).next().unwrap();
-    let img: RgbImage = {
+    let img: RgbImage = ({
         let canvas_option = &mut *canvas1.lock().unwrap();
-        let canvas = canvas_option.take().unwrap_or_else( || Canvas::new(dim_x, dim_y) );
-        if (dim_x, dim_y) == canvas.img.dimensions() {
-            let canvas = rgb_encode_to_canvas(canvas, percent_decode_str(&data).into_iter().borrow_mut())?;
-            Ok(canvas_option.insert(canvas).img.clone())
-        }
-        else {
-            _ = canvas_option.insert(canvas);
-            Err(CanvasError::DimensionMismatch)
-        }
-    }?;
+        Ok(apply_to_canvas( |c, d| to_canvasresult(rgb_encode_to_canvas(c, d)), canvas_option, Some((dim_x, dim_y)), data)?.img.clone())
+    } as Result<RgbImage, CanvasError>)?;
     let (tdim_x, tdim_y) = img.dimensions();
     let (tdim_x, tdim_y) = (tdim_x * scale, tdim_y * scale);
     let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);