add image module, config parsing improvements

This commit is contained in:
Fl1tzi 2023-11-01 00:12:56 +01:00
parent a54fc326f9
commit ebc9fd2a62
No known key found for this signature in database
GPG key ID: 06B333727810C686
3 changed files with 75 additions and 7 deletions

View file

@ -77,6 +77,8 @@ fn new_hashmap() -> HashMap<String, String> {
pub enum ButtonConfigError {
/// (Key, Expected)
WrongType(String, &'static str),
/// a required value
Required(&'static str),
/// A general error which gets directly outputed to the user
General(String),
}
@ -90,6 +92,9 @@ impl Display for ButtonConfigError {
"Expected value of type {expected} in option \"{key}\"."
)
}
ButtonConfigError::Required(key) => {
write!(formatter, "A value for the option \"{key}\" is required.")
}
ButtonConfigError::General(message) => {
write!(formatter, "{message}")
}
@ -99,19 +104,35 @@ impl Display for ButtonConfigError {
/// See [parse_button_config()]
pub enum ParseButtonConfigResult<T: FromStr> {
Found(T),
NotFound(T),
/// (user-defined value, key)
Found(T, &'static str),
/// (alternative value, key)
NotFound(T, &'static str),
ParseError(ButtonConfigError),
}
impl<T: FromStr> ParseButtonConfigResult<T> {
/// instead of the enum return Result<Value, ButtonConfigError>.
///
/// [ParseButtonConfigResult::Found] || [ParseButtonConfigResult::NotFound] => Ok(value)
/// [ParseButtonConfigResult::Found] | [ParseButtonConfigResult::NotFound] => Ok(value)
/// [ParseButtonConfigResult::ParseError] => Err(e)
pub fn res(self) -> Result<T, ButtonConfigError> {
match self {
ParseButtonConfigResult::Found(v) | ParseButtonConfigResult::NotFound(v) => Ok(v),
ParseButtonConfigResult::Found(v, _) | ParseButtonConfigResult::NotFound(v, _) => Ok(v),
ParseButtonConfigResult::ParseError(e) => Err(e),
}
}
/// instead of the enum return Result<Value, ButtonConfigError>
///
/// This makes a user-defined value required.
///
/// [ParseButtonConfigResult::Found] => Ok(value)
/// [ParseButtonConfigResult::NotFound] | [ParseButtonConfigResult::ParseError] => Err(e)
pub fn required(self) -> Result<T, ButtonConfigError> {
match self {
ParseButtonConfigResult::Found(v, _) => Ok(v),
ParseButtonConfigResult::NotFound(_, k) => Err(ButtonConfigError::Required(k)),
ParseButtonConfigResult::ParseError(e) => Err(e),
}
}
@ -135,11 +156,11 @@ impl Button {
// try to find value or return None
let parse_result = match self.options.get(key) {
Some(value) => value.parse::<T>(),
_ => return ParseButtonConfigResult::NotFound(if_wrong_type),
_ => return ParseButtonConfigResult::NotFound(if_wrong_type, key),
};
// check if value could be parsed
if let Ok(out) = parse_result {
return ParseButtonConfigResult::Found(out);
return ParseButtonConfigResult::Found(out, key);
}
ParseButtonConfigResult::ParseError(ButtonConfigError::WrongType(
key.to_string(),

View file

@ -1,17 +1,19 @@
mod blank;
mod counter;
mod image;
mod space;
// modules
use self::counter::Counter;
use self::image::Image;
use self::space::Space;
// other things
use crate::config::{Button, ButtonConfigError};
use ::image::DynamicImage;
use async_trait::async_trait;
pub use deck_driver as streamdeck;
use futures_util::Future;
use image::DynamicImage;
use std::{error::Error, pin::Pin, sync::Arc};
use streamdeck::info::ImageFormat;
use streamdeck::info::Kind;
@ -38,6 +40,7 @@ pub fn retrieve_module_from_name(name: &str) -> Option<ModuleInitFunction> {
match name {
"space" => Some(Space::init as ModuleInitFunction),
"counter" => Some(Counter::init as ModuleInitFunction),
"image" => Some(Image::init as ModuleInitFunction),
_ => None,
}
}

44
src/modules/image.rs Normal file
View file

@ -0,0 +1,44 @@
use super::Button;
use super::ButtonConfigError;
use super::ChannelReceiver;
use super::DeviceAccess;
use super::Module;
use super::ModuleObject;
use super::ReturnError;
use crate::image_rendering::{load_image, ImageBuilder};
use async_trait::async_trait;
use image::DynamicImage;
use std::sync::Arc;
use tokio::task;
pub struct Image {
image: DynamicImage,
scale: f32,
}
#[async_trait]
impl Module for Image {
async fn init(config: Arc<Button>) -> Result<ModuleObject, ButtonConfigError> {
let path = config.parse_module("PATH", String::new()).required()?;
let scale = config.parse_module("SCALE", 100.0).res()?;
// TODO: decoding takes really long sometimes. Maybe this can be cached?
let image = task::spawn_blocking(move || {
load_image(path)
.map_err(|_| ButtonConfigError::General("Image was not found.".to_string()))
})
.await
.unwrap()?;
Ok(Box::new(Image { image, scale }))
}
async fn run(
&mut self,
streamdeck: DeviceAccess,
_button_receiver: ChannelReceiver,
) -> Result<(), ReturnError> {
streamdeck.write_img(self.image.clone()).await.unwrap();
Ok(())
}
}