use std::any;
use tracing::info_span;
use super::future_data::AsyncData;
use super::{AsyncFactoryComponent, AsyncFactoryHandle};
use crate::channel::AsyncFactorySender;
use crate::factory::{DataGuard, DynamicIndex, FactoryView};
use crate::runtime_util::GuardedReceiver;
use crate::shutdown::ShutdownSender;
use crate::{Receiver, Sender, shutdown};
pub(super) struct AsyncFactoryBuilder<C: AsyncFactoryComponent> {
init: C::Init,
pub(super) root_widget: C::Root,
pub(super) component_sender: AsyncFactorySender<C>,
input_receiver: Receiver<C::Input>,
cmd_receiver: Receiver<C::CommandOutput>,
shutdown_notifier: ShutdownSender,
}
impl<C: AsyncFactoryComponent> AsyncFactoryBuilder<C>
where
<C::ParentWidget as FactoryView>::ReturnedWidget: Clone,
{
pub(super) fn new(init: C::Init, output_sender: Sender<C::Output>) -> Self {
let (input_sender, input_receiver) = crate::channel::<C::Input>();
let (cmd_sender, cmd_receiver) = crate::channel::<C::CommandOutput>();
let (shutdown_notifier, shutdown_receiver) = shutdown::channel();
let component_sender =
AsyncFactorySender::new(input_sender, output_sender, cmd_sender, shutdown_receiver);
let root_widget = C::init_root();
Self {
init,
root_widget,
component_sender,
input_receiver,
cmd_receiver,
shutdown_notifier,
}
}
pub(super) fn launch(
self,
index: &DynamicIndex,
returned_widget: <C::ParentWidget as FactoryView>::ReturnedWidget,
) -> AsyncFactoryHandle<C> {
let Self {
root_widget,
component_sender,
input_receiver,
cmd_receiver,
shutdown_notifier,
init,
} = self;
let (notifier, notifier_receiver) = crate::channel();
let input_sender = component_sender.input_sender().clone();
let loading_widgets = C::init_loading_widgets(root_widget.clone());
let future_receiver = {
let index = index.clone();
let (future_sender, future_receiver) = crate::channel();
let future_data = FutureData {
shutdown_notifier,
index: index.clone(),
component_sender: component_sender.clone(),
root: root_widget.clone(),
returned_widget: returned_widget.clone(),
input_receiver,
cmd_receiver,
notifier_receiver,
};
crate::spawn_local(async move {
let data = C::init_model(init, &index, component_sender).await;
drop(loading_widgets);
let data_guard = future_data.start_runtime(data);
let _ = future_sender.send(data_guard);
});
future_receiver
};
let data = AsyncData::new(future_receiver);
AsyncFactoryHandle {
data,
root_widget,
returned_widget,
input: input_sender,
notifier,
}
}
}
impl<C: AsyncFactoryComponent> std::fmt::Debug for AsyncFactoryBuilder<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncFactoryBuilder")
.field("init", &"<C::Init>")
.field("root_widget", &self.root_widget)
.field("component_sender", &"<AsyncComponentSender<C>>")
.field("input_receiver", &self.input_receiver)
.field("cmd_receiver", &self.cmd_receiver)
.field("shutdown_notifier", &self.shutdown_notifier)
.finish()
}
}
struct FutureData<C: AsyncFactoryComponent> {
shutdown_notifier: ShutdownSender,
index: DynamicIndex,
component_sender: AsyncFactorySender<C>,
root: C::Root,
returned_widget: <C::ParentWidget as FactoryView>::ReturnedWidget,
input_receiver: Receiver<C::Input>,
cmd_receiver: Receiver<C::CommandOutput>,
notifier_receiver: Receiver<()>,
}
impl<C: AsyncFactoryComponent> FutureData<C> {
fn start_runtime(self, data: C) -> DataGuard<C, C::Widgets, C::Output> {
let Self {
shutdown_notifier,
index,
component_sender,
root,
returned_widget,
cmd_receiver,
input_receiver,
notifier_receiver,
} = self;
let mut data = Box::new(data);
let widgets =
Box::new(data.init_widgets(&index, root, &returned_widget, component_sender.clone()));
let output_sender = component_sender.output_sender().clone();
DataGuard::new(
data,
widgets,
shutdown_notifier,
output_sender,
|mut model, mut widgets| async move {
let mut notifier = GuardedReceiver::new(notifier_receiver);
let mut cmd = GuardedReceiver::new(cmd_receiver);
let mut input = GuardedReceiver::new(input_receiver);
loop {
futures::select!(
message = input => {
let span = info_span!(
"update_with_view",
input=?message,
component=any::type_name::<C>(),
id=model.id(),
);
let _enter = span.enter();
model.update_with_view(&mut widgets, message, component_sender.clone()).await;
}
message = cmd => {
let span = info_span!(
"update_cmd_with_view",
cmd_output=?message,
component=any::type_name::<C>(),
id=model.id(),
);
let _enter = span.enter();
model.update_cmd_with_view(&mut widgets, message, component_sender.clone()).await;
}
_ = notifier => {
model.update_view(&mut widgets, component_sender.clone());
}
);
}
},
C::shutdown,
)
}
}