Percent decode
[litoprism.git] / src / main.rs
1 use std::io::Cursor;
2 use std::fmt::Display;
3 use std::fmt;
4 use serde::Deserialize;
5 use percent_encoding::percent_decode_str;
6 use actix_web::{get, web, App, HttpServer, HttpResponse, Responder, ResponseError, Result};
7 use actix_web::body::BoxBody;
8 use actix_web::http::StatusCode;
9 use image::{ImageBuffer, ColorType, Rgb, RgbImage, write_buffer_with_format};
10 use image::ImageOutputFormat::Png;
11 use image::imageops::{FilterType, resize};
12
13 #[derive(Deserialize)]
14 struct Data {
15 data: String
16 }
17
18 #[derive(Debug)]
19 struct ImageError(image::ImageError);
20
21 impl Display for ImageError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "{}", &self.0)
24 }
25 }
26
27 impl ResponseError for ImageError {
28 fn status_code(&self) -> StatusCode {
29 StatusCode::INTERNAL_SERVER_ERROR
30 }
31
32 fn error_response(&self) -> HttpResponse<BoxBody> {
33 HttpResponse::InternalServerError().finish()
34 }
35 }
36
37 struct StringToRgb<'a> {
38 iter: Box<dyn Iterator<Item = char> + 'a>
39 }
40
41 impl<'a> StringToRgb<'a> {
42 fn new(text: &'a str) -> StringToRgb {
43 StringToRgb { iter: Box::new(text.chars()) }
44 }
45 }
46
47 impl Iterator for StringToRgb<'_> {
48 type Item = Rgb<u8>;
49 fn next(&mut self) -> Option<Rgb<u8>> {
50 let r = self.iter.next();
51 let g = self.iter.next();
52 let b = self.iter.next();
53 println!("{:?} {:?} {:?}", r, g, b);
54 if r.is_none() && g.is_none() && b.is_none() {
55 None
56 }
57 else {
58 Some(Rgb([r.unwrap_or('\0') as u8, g.unwrap_or('\0') as u8, b.unwrap_or('\0') as u8]))
59 }
60 }
61 }
62
63 fn to_imageresult<T> (result: Result<T, image::ImageError>) -> Result<T, ImageError> {
64 match result {
65 Ok(v) => Ok(v),
66 Err(e) => Err(ImageError(e))
67 }
68 }
69
70 #[get("/hello/{name}")]
71 async fn greet(name: web::Path<String>) -> impl Responder {
72 format!("Hello {name}!")
73 }
74
75 #[get("/gen/{data}")]
76 async fn img_gen(data: web::Path<Data>) -> Result<impl Responder> {
77 let mut cursor = Cursor::new(Vec::new());
78 let mut img: RgbImage = ImageBuffer::new(32, 32);
79 let mut pixels = img.pixels_mut();
80
81 for sp in StringToRgb::new(&percent_decode_str(&data.data).decode_utf8_lossy()) {
82 let mut dp = pixels.next().unwrap();
83 println!("{:?}", sp);
84 dp.0 = sp.0;
85 }
86
87 let img = resize(&img, 512, 512, FilterType::Nearest);
88 to_imageresult(write_buffer_with_format(&mut cursor, &img, 512, 512, ColorType::Rgb8, Png))?;
89 Ok(HttpResponse::build(StatusCode::OK)
90 .content_type("image/png")
91 .body(cursor.into_inner()))
92 }
93
94 #[actix_web::main] // or #[tokio::main]
95 async fn main() -> std::io::Result<()> {
96 HttpServer::new(|| {
97 App::new().service(greet).service(img_gen)
98 })
99 .bind(("127.0.0.1", 8080))?
100 .run()
101 .await
102 }