mirror of
https://codeberg.org/Fl1tzi/microdeck.git
synced 2024-05-08 22:50:42 +00:00
add image module, config parsing improvements
This commit is contained in:
parent
a54fc326f9
commit
ebc9fd2a62
|
@ -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(),
|
||||
|
|
|
@ -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
44
src/modules/image.rs
Normal 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(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue