use std::num::NonZeroUsize;
use std::sync::Arc;
use frame_core::registry::ComponentRegistry;
use frame_fragments::{AssemblyPublisher, AssemblyUpdate, LiveAssembly};
use liminal_sdk::remote::PushClient;
use liminal_server::config::{ChannelDef, ServerConfig};
use crate::error::HostError;
pub const ASSEMBLY_CHANNEL: &str = "frame.assembly";
const ASSEMBLY_EVENT_BUFFER: usize = 256;
#[must_use]
pub fn bus_with_assembly_channel(bus: &ServerConfig) -> ServerConfig {
let mut bus = bus.clone();
if bus
.channels
.iter()
.any(|channel| channel.name == ASSEMBLY_CHANNEL)
{
return bus;
}
tracing::info!(
channel = ASSEMBLY_CHANNEL,
"declaring the host-owned assembly channel on the embedded bus"
);
bus.channels.push(ChannelDef {
name: ASSEMBLY_CHANNEL.to_owned(),
schema_ref: None,
durable: false,
loaded_schema: None,
});
bus
}
struct ChannelSink {
client: PushClient,
}
impl AssemblyPublisher for ChannelSink {
type Error = std::io::Error;
fn publish(&mut self, update: &AssemblyUpdate) -> Result<(), Self::Error> {
let payload = serde_json::to_vec(update)
.map_err(|error| std::io::Error::other(format!("encoding the update: {error}")))?;
self.client
.publish(ASSEMBLY_CHANNEL, payload)
.map_err(|error| std::io::Error::other(error.to_string()))
}
}
pub struct AssemblyAuthority {
live: Arc<LiveAssembly>,
}
impl AssemblyAuthority {
pub fn boot(
bus_tcp: &str,
auth_token: Option<&[u8]>,
registry: Arc<ComponentRegistry>,
) -> Result<Self, HostError> {
let client = match auth_token {
Some(token) => PushClient::connect_with_auth(bus_tcp, token),
None => PushClient::connect(bus_tcp),
}
.map_err(|error| HostError::AssemblyAuthority {
detail: format!("connecting the assembly publisher: {error}"),
})?;
let capacity =
NonZeroUsize::new(ASSEMBLY_EVENT_BUFFER).ok_or(HostError::SynchronizationPoisoned)?;
let live =
LiveAssembly::start(registry, capacity, ChannelSink { client }).map_err(|error| {
HostError::AssemblyAuthority {
detail: format!("starting the live assembly worker: {error}"),
}
})?;
Ok(Self {
live: Arc::new(live),
})
}
#[must_use]
pub const fn channel(&self) -> &'static str {
ASSEMBLY_CHANNEL
}
#[must_use]
pub fn live(&self) -> Arc<LiveAssembly> {
Arc::clone(&self.live)
}
pub fn current_update(
live: &LiveAssembly,
) -> Result<Option<AssemblyUpdate>, frame_fragments::AssemblyError> {
Ok(live.current()?.map(|published| AssemblyUpdate {
revision: published.revision,
assembly: (*published.assembly).clone(),
}))
}
}