Skip to content

Commit ffdc4ac

Browse files
committed
feat(api): Return priorities of current instance in JSON API
1 parent 8bd3fe9 commit ffdc4ac

File tree

5 files changed

+106
-11
lines changed

5 files changed

+106
-11
lines changed

src/api/json.rs

+52-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
component::ComponentName,
99
global::{Global, InputMessage, InputMessageData, InputSourceHandle},
1010
image::{RawImage, RawImageError},
11+
instance::InstanceHandle,
1112
};
1213

1314
/// Schema definitions as Serde serializable structures and enums
@@ -31,15 +32,42 @@ pub enum JsonApiError {
3132
/// A client connected to the JSON endpoint
3233
pub struct ClientConnection {
3334
source: InputSourceHandle<InputMessage>,
35+
current_instance: Option<i32>,
3436
}
3537

3638
impl ClientConnection {
3739
pub fn new(source: InputSourceHandle<InputMessage>) -> Self {
38-
Self { source }
40+
Self {
41+
source,
42+
current_instance: None,
43+
}
44+
}
45+
46+
async fn current_instance(&mut self, global: &Global) -> Option<InstanceHandle> {
47+
if let Some(current_instance) = self.current_instance {
48+
if let Some(instance) = global.get_instance(current_instance).await {
49+
return Some(instance);
50+
} else {
51+
// Instance id now invalid, reset
52+
self.current_instance = None;
53+
}
54+
}
55+
56+
if let Some((id, inst)) = global.default_instance().await {
57+
self.set_current_instance(id);
58+
return Some(inst);
59+
}
60+
61+
None
62+
}
63+
64+
fn set_current_instance(&mut self, id: i32) {
65+
debug!("{}: switch to instance {}", &self.source.name(), id);
66+
self.current_instance = Some(id);
3967
}
4068

4169
pub async fn handle_request(
42-
&self,
70+
&mut self,
4371
request: HyperionMessage,
4472
global: &Global,
4573
) -> Result<Option<HyperionResponse>, JsonApiError> {
@@ -105,6 +133,12 @@ impl ClientConnection {
105133
HyperionCommand::ServerInfo(message::ServerInfoRequest { subscribe: _ }) => {
106134
// TODO: Handle subscribe field
107135

136+
let priorities = if let Some(handle) = self.current_instance(global).await {
137+
handle.current_priorities().await
138+
} else {
139+
vec![]
140+
};
141+
108142
// Just answer the serverinfo request, no need to update state
109143
return Ok(Some(
110144
global
@@ -117,8 +151,7 @@ impl ClientConnection {
117151

118152
HyperionResponse::server_info(
119153
request.tan,
120-
// TODO: Priorities only for current instance
121-
vec![],
154+
priorities,
122155
// TODO: Fill adjustments
123156
vec![],
124157
// TODO: Fill effects
@@ -147,6 +180,21 @@ impl ClientConnection {
147180
)));
148181
}
149182

183+
HyperionCommand::Instance(message::Instance {
184+
subcommand: message::InstanceCommand::SwitchTo,
185+
instance: Some(id),
186+
..
187+
}) => {
188+
if global.get_instance(id).await.is_some() {
189+
self.set_current_instance(id);
190+
return Ok(Some(HyperionResponse::switch_to(request.tan, Some(id))));
191+
} else {
192+
// Note: it's an "Ok" but should be an Err. Find out how to represent errors
193+
// better
194+
return Ok(Some(HyperionResponse::switch_to(request.tan, None)));
195+
}
196+
}
197+
150198
_ => return Err(JsonApiError::NotImplemented),
151199
};
152200

src/api/json/message.rs

+36-3
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ pub enum InstanceCommand {
230230
#[derive(Debug, Deserialize, Validate)]
231231
pub struct Instance {
232232
pub subcommand: InstanceCommand,
233-
#[validate(range(max = 255))]
234-
pub instance: Option<u32>,
233+
#[validate(range(min = 0, max = 255))]
234+
pub instance: Option<i32>,
235235
#[validate(length(min = 5))]
236236
pub name: Option<String>,
237237
}
@@ -675,6 +675,12 @@ pub enum HyperionResponseInfo {
675675
/// SysInfo response
676676
#[serde(rename = "sysinfo")]
677677
SysInfo(SysInfo),
678+
/// SwitchTo response
679+
#[serde(rename = "instance-switchTo")]
680+
SwitchTo {
681+
#[serde(skip_serializing_if = "Option::is_none")]
682+
instance: Option<i32>,
683+
},
678684
}
679685

680686
impl HyperionResponse {
@@ -698,7 +704,7 @@ impl HyperionResponse {
698704
}
699705

700706
/// Return an error response
701-
pub fn error(tan: Option<i32>, error: &impl std::fmt::Display) -> Self {
707+
pub fn error(tan: Option<i32>, error: impl std::fmt::Display) -> Self {
702708
Self {
703709
success: false,
704710
tan,
@@ -707,6 +713,20 @@ impl HyperionResponse {
707713
}
708714
}
709715

716+
/// Return an error response
717+
pub fn error_info(
718+
tan: Option<i32>,
719+
error: impl std::fmt::Display,
720+
info: HyperionResponseInfo,
721+
) -> Self {
722+
Self {
723+
success: false,
724+
tan,
725+
error: Some(error.to_string()),
726+
info: Some(info),
727+
}
728+
}
729+
710730
/// Return a server information response
711731
pub fn server_info(
712732
tan: Option<i32>,
@@ -741,6 +761,19 @@ impl HyperionResponse {
741761
// TODO: Properly fill out this response
742762
Self::success_info(tan, HyperionResponseInfo::SysInfo(SysInfo::new(id)))
743763
}
764+
765+
pub fn switch_to(tan: Option<i32>, id: Option<i32>) -> Self {
766+
if let Some(id) = id {
767+
// Switch successful
768+
Self::success_info(tan, HyperionResponseInfo::SwitchTo { instance: Some(id) })
769+
} else {
770+
Self::error_info(
771+
tan,
772+
"selected hyperion instance not found",
773+
HyperionResponseInfo::SwitchTo { instance: None },
774+
)
775+
}
776+
}
744777
}
745778

746779
fn hostname() -> String {

src/global.rs

+10
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ impl Global {
8383
self.0.read().await.instances.get(&id).cloned()
8484
}
8585

86+
pub async fn default_instance(&self) -> Option<(i32, InstanceHandle)> {
87+
self.0
88+
.read()
89+
.await
90+
.instances
91+
.iter()
92+
.next()
93+
.map(|(k, v)| (*k, v.clone()))
94+
}
95+
8696
pub async fn read_config<T>(&self, f: impl FnOnce(&Config) -> T) -> T {
8797
let data = self.0.read().await;
8898
f(&data.config)

src/instance.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl Instance {
6565
}
6666

6767
let receiver = global.subscribe_input().await;
68-
let (tx, local_receiver) = mpsc::channel(4);
68+
let (local_tx, local_receiver) = mpsc::channel(4);
6969

7070
let muxer = PriorityMuxer::new(global.clone()).await;
7171
let core = Core::new(&config).await;
@@ -78,8 +78,10 @@ impl Instance {
7878
global.clone(),
7979
{
8080
let instance = config.clone();
81+
let local_tx = local_tx.clone();
82+
8183
move |tcp, global| {
82-
servers::boblight::handle_client(tcp, tx.clone(), instance.clone(), global)
84+
servers::boblight::handle_client(tcp, local_tx.clone(), instance.clone(), global)
8385
}
8486
},
8587
)
@@ -111,7 +113,7 @@ impl Instance {
111113
core,
112114
_boblight_server,
113115
},
114-
InstanceHandle { id, tx },
116+
InstanceHandle { id, tx, local_tx },
115117
)
116118
}
117119

@@ -225,6 +227,7 @@ enum InstanceMessage {
225227
pub struct InstanceHandle {
226228
id: i32,
227229
tx: mpsc::Sender<InstanceMessage>,
230+
local_tx: mpsc::Sender<InputMessage>,
228231
}
229232

230233
impl InstanceHandle {
@@ -240,6 +243,7 @@ impl InstanceHandle {
240243
.send(InstanceMessage::PriorityInfo(tx))
241244
.await
242245
.unwrap();
246+
243247
// unwrap: if the previous didn't fail, the instance will be there to answer
244248
rx.await.unwrap()
245249
}

src/servers/json.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub async fn handle_client(
3636
let (mut writer, mut reader) = framed.split();
3737

3838
// unwrap: cannot fail because the priority is None
39-
let client_connection = json::ClientConnection::new(
39+
let mut client_connection = json::ClientConnection::new(
4040
global
4141
.register_input_source(InputSourceName::Json { peer_addr }, None)
4242
.await

0 commit comments

Comments
 (0)