1use std::sync::Arc;
2
3use thiserror::Error;
4
5use crate::{
6 global::{InputMessage, InputMessageData, InputSourceHandle, Message, PriorityGuard},
7 instance::{InstanceHandle, InstanceHandleError},
8 models::Color,
9};
10
11pub mod message;
12use message::{BoblightRequest, BoblightResponse};
13
14#[derive(Debug, Error)]
15pub enum BoblightApiError {
16 #[error("error broadcasting update: {0}")]
17 Broadcast(#[from] tokio::sync::mpsc::error::SendError<InputMessage>),
18 #[error("missing command data in protobuf frame")]
19 MissingCommand,
20 #[error("invalid instance")]
21 InvalidInstance(#[from] InstanceHandleError),
22}
23
24pub struct ClientConnection {
25 handle: InputSourceHandle<InputMessage>,
26 priority_guard: PriorityGuard,
27 led_colors: Vec<Color>,
28 priority: i32,
29 instance: InstanceHandle,
30}
31
32impl ClientConnection {
33 pub fn new(
34 handle: InputSourceHandle<InputMessage>,
35 led_count: usize,
36 instance: InstanceHandle,
37 ) -> Self {
38 let priority_guard = PriorityGuard::new_mpsc(instance.input_channel().clone(), &handle);
39
40 Self {
41 handle,
42 priority_guard,
43 led_colors: vec![Color::default(); led_count],
44 priority: 128,
45 instance,
46 }
47 }
48
49 async fn set_priority(&mut self, priority: i32) {
50 let new_priority = if !(128..254).contains(&priority) {
51 self.instance
52 .current_priorities()
53 .await
54 .map(|priorities| {
55 let mut used_priorities = priorities
56 .iter()
57 .map(|p| p.priority)
58 .skip_while(|p| *p <= 128)
59 .peekable();
60
61 for i in 128..255 {
62 loop {
63 match used_priorities.peek().cloned() {
64 Some(used) if used == i => {
65 used_priorities.next();
67 break;
68 }
69 Some(used) if used < i => {
70 used_priorities.next();
71 continue;
72 }
73 _ => {
74 return i;
75 }
76 }
77 }
78 }
79
80 128
81 })
82 .unwrap_or(128)
83 } else {
84 priority
85 };
86
87 self.priority = new_priority;
88 self.priority_guard.set_priority(Some(new_priority));
89 }
90
91 async fn sync(&self) -> Result<(), BoblightApiError> {
92 Ok(self
93 .instance
94 .send(InputMessage::new(
95 self.handle.id(),
96 crate::component::ComponentName::BoblightServer,
97 InputMessageData::LedColors {
98 priority: self.priority,
99 duration: None,
100 led_colors: Arc::new(self.led_colors.clone()),
101 },
102 ))
103 .await?)
104 }
105
106 #[instrument(skip(request))]
107 pub async fn handle_request(
108 &mut self,
109 request: BoblightRequest,
110 ) -> Result<Option<BoblightResponse>, BoblightApiError> {
111 match request {
112 BoblightRequest::Hello => Ok(Some(BoblightResponse::Hello)),
113 BoblightRequest::Ping => Ok(Some(BoblightResponse::Ping)),
114 BoblightRequest::Get(get) => match get {
115 message::GetArg::Version => Ok(Some(BoblightResponse::Version)),
116 message::GetArg::Lights => Ok(Some(BoblightResponse::Lights {
117 leds: self.instance.config().await?.leds.leds.clone(),
118 })),
119 },
120 BoblightRequest::Set(set) => {
121 match set {
122 message::SetArg::Light(message::LightParam { index, data }) => {
123 if let message::LightParamData::Color(color) = data {
124 if let Some(color_mut) = self.led_colors.get_mut(index) {
125 *color_mut = color;
126
127 if index == self.led_colors.len() - 1 {
128 self.sync().await?;
129 }
130 }
131 }
132 }
133 message::SetArg::Priority(priority) => {
134 self.set_priority(priority).await;
135 }
136 }
137
138 Ok(None)
139 }
140 BoblightRequest::Sync => {
141 self.sync().await?;
142
143 Ok(None)
144 }
145 }
146 }
147}
148
149impl std::fmt::Debug for ClientConnection {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 f.debug_struct("ClientConnection")
152 .field("instance", &self.instance.id())
153 .field("source", &format!("{}", &*self.handle))
154 .finish()
155 }
156}