use crate::host::*;
use futures::prelude::*;
use futures::{pin_mut};
use once_cell::sync::{Lazy};
use std::io::*;
use serde::*;
pub static STDOUT_PROGRAM: StaticSubProgramId = StaticSubProgramId::called("flo_scene::stdout");
pub static STDERR_PROGRAM: StaticSubProgramId = StaticSubProgramId::called("flo_scene::stderr");
static ERROR_TO_TEXT_FILTER: Lazy<FilterHandle> = Lazy::new(|| FilterHandle::for_filter(|stream: InputStream<ErrorOutput>| stream.map(|err| TextOutput::from(err))));
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[derive(Serialize, Deserialize)]
pub enum TextOutput {
Character(char),
Text(String),
Line(String),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[derive(Serialize, Deserialize)]
pub enum ErrorOutput {
Character(char),
Text(String),
Line(String),
}
impl From<ErrorOutput> for TextOutput {
fn from(error_output: ErrorOutput) -> TextOutput {
match error_output {
ErrorOutput::Character(chr) => TextOutput::Character(chr),
ErrorOutput::Text(txt) => TextOutput::Text(txt),
ErrorOutput::Line(line) => TextOutput::Line(line),
}
}
}
impl SceneMessage for TextOutput {
fn default_target() -> StreamTarget { (*STDOUT_PROGRAM).into() }
fn allow_thread_stealing_by_default() -> bool { true }
fn message_type_name() -> String { "flo_scene::TextOutput".into() }
}
impl SceneMessage for ErrorOutput {
fn default_target() -> StreamTarget { (*STDERR_PROGRAM).into() }
fn allow_thread_stealing_by_default() -> bool { true }
fn message_type_name() -> String { "flo_scene::ErrorOutput".into() }
fn initialise(scene: &impl SceneInitialisationContext) {
scene.connect_programs((), StreamTarget::Filtered(ERROR_TO_TEXT_FILTER.clone(), *STDERR_PROGRAM), StreamId::with_message_type::<ErrorOutput>()).ok();
}
}
pub async fn text_io_subprogram(target: impl Send + Write, messages: impl Stream<Item=TextOutput>, _: SceneContext) {
pin_mut!(messages);
let mut target = target;
let mut at_start_of_line = false;
while let Some(output) = messages.next().await {
use TextOutput::*;
match output {
Character(chr) => { write!(target, "{}", chr).ok(); at_start_of_line = chr == '\n'; },
Text(text) => { write!(target, "{}", text).ok(); at_start_of_line = text.ends_with('\n'); },
Line(text) => {
if at_start_of_line {
write!(target, "{}", text).ok();
} else {
write!(target, "\n{}", text).ok();
}
at_start_of_line = text.ends_with('\n');
},
}
target.flush().ok();
}
}