use super::run_command::*;
use super::list_commands::*;
use super::read_command::*;
use super::error::*;
use super::describe_command::*;
use crate::host::input_stream::*;
use crate::host::scene_context::*;
use crate::host::scene_message::*;
use crate::host::subprogram_id::*;
use crate::host::programs::*;
use futures::prelude::*;
use serde::*;
use std::collections::{HashMap, HashSet};
use std::iter;
pub async fn command_dispatcher_subprogram<TParameter, TResponse>(input: InputStream<RunCommand<TParameter, TResponse>>, context: SceneContext)
where
TParameter: 'static + Unpin + Send + From<()> + Serialize + Clone,
TParameter: TryInto<DescribeCommandRequest>,
TResponse: 'static + Unpin + Send + SceneMessage,
TResponse: TryInto<ListCommandResponse> + From<ListCommandResponse>,
TResponse: TryInto<DescribeCommandResponse> + From<DescribeCommandResponse>,
TResponse: From<CommandError>,
for<'de> TParameter: Deserialize<'de>,
{
let our_program_id = context.current_program_id().unwrap();
let mut commands = HashMap::<String, (CommandDescription, SubProgramId)>::new();
let mut subprograms = HashSet::<SubProgramId>::new();
let mut input = input;
while let Some(next_command) = input.next().await {
let mut command_owner_stream = commands.get(next_command.name())
.and_then(|(_, command_owner)| {
context.send::<RunCommand<TParameter, TResponse>>(*command_owner).ok()
});
if next_command.name() == LIST_COMMANDS || next_command.name() == DESCRIBE_COMMAND || command_owner_stream.is_none() {
let scene_status = context.spawn_query(ReadCommand::default(), Query::<SceneUpdate>::with_no_target(), ());
let scene_status = if let Ok(scene_status) = scene_status {
scene_status
} else {
if let Ok(mut response) = context.send(next_command.target())
{
response.send(TResponse::from(CommandError::CannotQueryScene)).await.ok();
}
continue;
};
let scene_status = scene_status.collect::<Vec<_>>().await;
let active_subprograms = scene_status.iter().flat_map(|update| match update {
SceneUpdate::Started(program_id, _) => Some(*program_id),
_ => None,
}).collect::<HashSet<SubProgramId>>();
let removed_subprograms = subprograms.iter()
.filter(|old_program| !active_subprograms.contains(old_program))
.copied()
.collect::<HashSet<_>>();
let added_subprograms = active_subprograms.iter()
.filter(|new_program| !subprograms.contains(new_program))
.copied()
.collect::<HashSet<_>>();
if !removed_subprograms.is_empty() {
commands.retain(|_, (_, program_id)| !removed_subprograms.contains(program_id));
}
for added_program_id in added_subprograms.iter() {
if *added_program_id == our_program_id { continue; }
if let Ok(supported_commands) = context.spawn_query(ReadCommand::default(), RunCommand::<TParameter, TResponse>::new((), LIST_COMMANDS, ()), added_program_id) {
let mut supported_commands = supported_commands;
while let Some(cmd) = supported_commands.next().await {
let cmd: ListCommandResponse = if let Ok(cmd) = cmd.try_into() { cmd } else { continue; };
for description in cmd.0.into_iter() {
if !commands.contains_key(&description.name) {
let command_name = description.name.clone();
commands.insert(command_name, (description, *added_program_id));
}
}
}
}
}
subprograms = active_subprograms;
command_owner_stream = commands.get(next_command.name())
.and_then(|(_, command_owner)| {
context.send::<RunCommand<TParameter, TResponse>>(*command_owner).ok()
});
}
if next_command.name() == LIST_COMMANDS {
if let Ok(mut response_stream) = context.send::<QueryResponse<TResponse>>(next_command.target()) {
let command_descriptions = commands.iter()
.map(|(_, (description, _))| description.clone())
.chain(iter::once(CommandDescription { name: (*LIST_COMMANDS).to_string() }))
.collect::<Vec<_>>();
let list_commands_response = ListCommandResponse(command_descriptions);
response_stream.send(QueryResponse::with_data(list_commands_response.into())).await.ok();
}
} else if next_command.name() == DESCRIBE_COMMAND {
if let Ok(request) = next_command.parameter().clone().try_into() {
let request: DescribeCommandRequest = request;
let mut sent_ok = false;
let command_target = next_command.target();
let command_name = next_command.name().to_string();
if let Some((_, command_owner)) = commands.get(&request.0) {
let command_owner_stream = context.send::<RunCommand<TParameter, TResponse>>(*command_owner).ok();
if let Some(mut command_owner_stream) = command_owner_stream {
if let Ok(()) = command_owner_stream.send(next_command).await {
sent_ok = true;
}
}
} else {
let response = DescribeCommandResponse {
summary: "No such command".into(),
help: format!("# {:?}\n\nThis command is not defined\n", request.0).into(),
};
let description_command_response = QueryResponse::with_data(response.into());
let response_stream = context.send::<QueryResponse<TResponse>>(command_target.clone());
if let Ok(mut response_stream) = response_stream {
response_stream.send(description_command_response).await.ok();
sent_ok = true;
}
}
if !sent_ok {
if let Ok(mut response_stream) = context.send::<QueryResponse<TResponse>>(command_target) {
response_stream.send(QueryResponse::with_data(CommandError::CommandFailedToRespond(command_name).into())).await.ok();
}
}
}
} else if let Some(command_owner_stream) = command_owner_stream {
let mut command_owner_stream = command_owner_stream;
let command_target = next_command.target();
let command_name = next_command.name().to_string();
if let Ok(()) = command_owner_stream.send(next_command).await {
} else {
if let Ok(mut response_stream) = context.send::<QueryResponse<TResponse>>(command_target) {
response_stream.send(QueryResponse::with_data(CommandError::CommandFailedToRespond(command_name).into())).await.ok();
}
}
} else {
if let Ok(mut response_stream) = context.send::<QueryResponse<TResponse>>(next_command.target()) {
let command_name = next_command.name().to_string();
response_stream.send(QueryResponse::with_data(CommandError::CommandNotFound(command_name).into())).await.ok();
}
}
}
}