pi-frame-server/src/api.rs

79 lines
2.1 KiB
Rust

use axum::extract::Multipart;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{extract::State, response::Response, routing::post, Router};
use image::ImageReader;
use std::io::Cursor;
use crate::display::EInkPanel;
use std::sync::{Arc, Mutex};
pub enum ImageFormFields {
DitherType,
ImageFile,
}
#[derive(Clone)]
pub struct Context {
display: Arc<Mutex<Box<dyn EInkPanel + Send>>>,
}
impl Context {
#[must_use]
pub fn new(w: Box<dyn EInkPanel + Send>) -> Self {
Self {
display: Arc::new(Mutex::new(w)),
}
}
}
// Make our own error that wraps `anyhow::Error`.
struct AppError(anyhow::Error);
// Tell axum how to convert `AppError` into a response.
impl IntoResponse for AppError {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", self.0),
)
.into_response()
}
}
// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
// `Result<_, AppError>`. That way you don't need to do that manually.
impl<E> From<E> for AppError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}
/// API routes for axum
/// Start with the basics: Send an image, crop it, dither, and upload.
/// we defer the upload to a separate task.
pub fn router() -> Router<Context> {
Router::new().route("/setimage", post(set_image))
}
async fn set_image(
State(ctx): State<Context>,
mut parts: Multipart,
) -> Result<impl IntoResponse, AppError> {
while let Some(field) = parts.next_field().await? {
let name = field.name().expect("fields always have names").to_string();
let data = field.bytes().await?;
println!("Length of `{}` is {} bytes", name, data.len());
if &name == "image" {
let reader = ImageReader::new(Cursor::new(data))
.with_guessed_format()
.expect("Cursor io never fails");
println!("Guessed format: {:?}", reader.format());
let _image = reader.decode()?;
}
}
Ok(())
}