use super::{KeySender, ServiceEffectError};
use crate::server::connection::LiminalConnectionServices;
use liminal::protocol::{CausalContext, MessageEnvelope, SchemaId};
use std::sync::Arc;
pub const SESSION_KEYS_SCHEMA: &[u8] = b"liminal.services.session-keys/1";
pub trait ServicePublisher: Send + Sync + std::fmt::Debug {
fn publish(
&self,
subject: &str,
envelope: &MessageEnvelope,
operation: &str,
) -> Result<(), ServiceEffectError>;
}
#[derive(Debug)]
pub struct ConnectionPublisher {
services: Arc<LiminalConnectionServices>,
host: String,
}
impl ConnectionPublisher {
#[must_use]
pub const fn new(services: Arc<LiminalConnectionServices>, host: String) -> Self {
Self { services, host }
}
}
impl ServicePublisher for ConnectionPublisher {
fn publish(
&self,
subject: &str,
envelope: &MessageEnvelope,
operation: &str,
) -> Result<(), ServiceEffectError> {
self.services
.publish_embedded(&self.host, subject, envelope, Some(operation))
.map(|_outcome| ())
.map_err(|error| ServiceEffectError(format!("publish on {subject} refused: {error}")))
}
}
#[must_use]
pub fn service_envelope(schema: &[u8], payload: &[u8]) -> MessageEnvelope {
MessageEnvelope::new(
SchemaId::from_schema_bytes(schema),
CausalContext::independent(),
payload.to_vec(),
)
}
#[derive(Debug)]
pub struct ChannelKeySender {
publisher: Arc<dyn ServicePublisher>,
}
impl ChannelKeySender {
#[must_use]
pub const fn new(publisher: Arc<dyn ServicePublisher>) -> Self {
Self { publisher }
}
}
#[must_use]
pub fn keys_subject(session: &str) -> String {
format!("sessions.{session}.keys")
}
impl KeySender for ChannelKeySender {
fn send(&self, session: &str, keys: &[u8], operation: &str) -> Result<(), ServiceEffectError> {
if keys.is_empty() {
return Err(ServiceEffectError(format!(
"no keys to send to session {session}"
)));
}
self.publisher.publish(
&keys_subject(session),
&service_envelope(SESSION_KEYS_SCHEMA, keys),
operation,
)
}
}