use std::sync::Arc;
use std::{convert::TryFrom, net::SocketAddr};
use thiserror::Error;
use super::types::i32_to_duration;
use crate::{
component::ComponentName,
global::{InputMessage, InputMessageData, InputSourceHandle, PriorityGuard},
image::{RawImage, RawImageError},
models::Color,
};
pub mod message;
use message::HyperionRequest;
#[derive(Debug, Error)]
pub enum ProtoApiError {
#[error("error decoding image: {0}")]
RawImageError(#[from] RawImageError),
#[error("error broadcasting update: {0}")]
Broadcast(#[from] tokio::sync::broadcast::error::SendError<InputMessage>),
#[error("missing command data in protobuf frame")]
MissingCommand,
#[error("the priority {0} is not in the valid range between 100 and 199")]
InvalidPriority(i32),
}
fn validate_priority(
priority: i32,
source: &InputSourceHandle<InputMessage>,
priority_guard: &mut PriorityGuard,
) -> Result<i32, ProtoApiError> {
if !(100..200).contains(&priority) {
return Err(ProtoApiError::InvalidPriority(priority));
}
*priority_guard = PriorityGuard::new_broadcast(source);
Ok(priority)
}
#[instrument(skip(request, source, priority_guard))]
pub fn handle_request(
peer_addr: SocketAddr,
request: HyperionRequest,
source: &InputSourceHandle<InputMessage>,
priority_guard: &mut PriorityGuard,
) -> Result<(), ProtoApiError> {
match request.command() {
message::hyperion_request::Command::Clearall => {
source.send(ComponentName::ProtoServer, InputMessageData::ClearAll)?;
}
message::hyperion_request::Command::Clear => {
let clear_request = request
.clear_request
.ok_or_else(|| ProtoApiError::MissingCommand)?;
source.send(
ComponentName::ProtoServer,
InputMessageData::Clear {
priority: clear_request.priority,
},
)?;
}
message::hyperion_request::Command::Color => {
let color_request = request
.color_request
.ok_or_else(|| ProtoApiError::MissingCommand)?;
let color = color_request.rgb_color;
let color = (
(color & 0x000_000FF) as u8,
((color & 0x0000_FF00) >> 8) as u8,
((color & 0x00FF_0000) >> 16) as u8,
);
let priority = validate_priority(color_request.priority, source, priority_guard)?;
source.send(
ComponentName::ProtoServer,
InputMessageData::SolidColor {
priority,
duration: i32_to_duration(color_request.duration),
color: Color::from_components(color),
},
)?;
}
message::hyperion_request::Command::Image => {
let image_request = request
.image_request
.ok_or_else(|| ProtoApiError::MissingCommand)?;
let width =
u32::try_from(image_request.imagewidth).map_err(|_| RawImageError::InvalidWidth)?;
let height = u32::try_from(image_request.imageheight)
.map_err(|_| RawImageError::InvalidHeight)?;
let raw_image = RawImage::try_from((image_request.imagedata.to_vec(), width, height))?;
let priority = validate_priority(image_request.priority, source, priority_guard)?;
source.send(
ComponentName::ProtoServer,
InputMessageData::Image {
priority,
duration: i32_to_duration(image_request.duration),
image: Arc::new(raw_image),
},
)?;
}
}
Ok(())
}