use std::sync::mpsc::{Receiver, Sender, channel};
use std::sync::{Arc, Mutex};
use liminal::durability::{DurableStore, open_ephemeral};
use liminal::protocol::{MessageEnvelope, SchemaId};
use liminal_protocol::wire::{ClientRequest, ServerValue};
use super::conversation::ConnectionConversation;
use super::services::{ConnectionServices, ConnectionSubscription, PublishOutcome};
use super::supervisor::ConnectionSupervisor;
use crate::ServerError;
use crate::server::participant::{
InstalledParticipantService, ParticipantConnectionContext, ParticipantConnectionConversations,
ParticipantSemanticError, ParticipantSemanticHandler,
};
#[derive(Debug)]
pub(in crate::server) struct ParticipantOnlyServices {
participant_service: InstalledParticipantService,
}
impl ParticipantOnlyServices {
pub(in crate::server) const fn new(participant_service: InstalledParticipantService) -> Self {
Self {
participant_service,
}
}
fn unsupported(operation: &str) -> ServerError {
ServerError::ListenerAccept {
message: format!("participant runtime fixture does not support {operation}"),
}
}
}
impl ConnectionServices for ParticipantOnlyServices {
fn participant_service(&self) -> Option<InstalledParticipantService> {
Some(self.participant_service.clone())
}
fn publish(
&self,
_connection: u64,
_channel: &str,
_envelope: &MessageEnvelope,
_idempotency_key: Option<&str>,
) -> Result<PublishOutcome, ServerError> {
Err(Self::unsupported("publish"))
}
fn subscribe(
&self,
_channel: &str,
_accepted_schemas: &[SchemaId],
_install: Option<liminal::channel::InboxInstall>,
) -> Result<ConnectionSubscription, ServerError> {
Err(Self::unsupported("subscribe"))
}
fn unsubscribe(&self, _subscription: ConnectionSubscription) -> Result<(), ServerError> {
Err(Self::unsupported("unsubscribe"))
}
fn open_conversation(
&self,
_conversation_id: u64,
_subject: &str,
) -> Result<ConnectionConversation, ServerError> {
Err(Self::unsupported("conversation open"))
}
fn conversation_message(
&self,
_conversation: &ConnectionConversation,
_envelope: &MessageEnvelope,
_op_id: Option<u64>,
) -> Result<(), ServerError> {
Err(Self::unsupported("conversation message"))
}
fn close_conversation(&self, _conversation: ConnectionConversation) -> Result<(), ServerError> {
Err(Self::unsupported("conversation close"))
}
fn flush_durable_state(&self) -> Result<(), ServerError> {
Ok(())
}
fn supports_channel_operations(&self) -> bool {
false
}
}
fn internal(error: impl std::fmt::Display) -> ParticipantSemanticError {
ParticipantSemanticError::Internal {
message: error.to_string(),
}
}
#[derive(Debug)]
struct HeldRepair {
entered: Mutex<Sender<()>>,
release: Mutex<Receiver<bool>>,
}
impl ParticipantSemanticHandler for HeldRepair {
fn handle(
&self,
_context: ParticipantConnectionContext,
_conversations: &mut ParticipantConnectionConversations,
_request: ClientRequest,
) -> Result<ServerValue, ParticipantSemanticError> {
Err(ParticipantSemanticError::Unavailable)
}
fn repair_unclean_server_restart(
&self,
_current_server_incarnation: u64,
) -> Result<(), ParticipantSemanticError> {
self.entered
.lock()
.map_err(internal)?
.send(())
.map_err(internal)?;
let succeeds = self
.release
.lock()
.map_err(internal)?
.recv()
.map_err(internal)?;
if succeeds {
Ok(())
} else {
Err(internal("injected restart repair refusal"))
}
}
}
pub(in crate::server) struct HeldStart {
pub(in crate::server) supervisor: ConnectionSupervisor,
pub(in crate::server) entered: Receiver<()>,
pub(in crate::server) release: Sender<bool>,
}
pub(in crate::server) fn held_start_supervisor() -> Result<HeldStart, Box<dyn std::error::Error>> {
let store: Arc<dyn DurableStore> = Arc::new(open_ephemeral(1)?);
let (entered_send, entered) = channel();
let (release, released) = channel();
let participant_service = InstalledParticipantService::new(
Arc::new(HeldRepair {
entered: Mutex::new(entered_send),
release: Mutex::new(released),
}),
store,
u64::MAX,
)
.map_err(|error| format!("invalid participant test wire-frame limit: {error:?}"))?;
let services: Arc<dyn ConnectionServices> =
Arc::new(ParticipantOnlyServices::new(participant_service));
Ok(HeldStart {
supervisor: ConnectionSupervisor::with_services(services)?,
entered,
release,
})
}
#[derive(Debug)]
struct Absorbing;
impl ParticipantSemanticHandler for Absorbing {
fn handle(
&self,
_context: ParticipantConnectionContext,
_conversations: &mut ParticipantConnectionConversations,
_request: ClientRequest,
) -> Result<ServerValue, ParticipantSemanticError> {
Err(ParticipantSemanticError::Unavailable)
}
}
pub(in crate::server) fn supervisor_over(
store: Arc<dyn DurableStore>,
) -> Result<ConnectionSupervisor, Box<dyn std::error::Error>> {
let participant_service =
InstalledParticipantService::new(Arc::new(Absorbing), store, u64::MAX)
.map_err(|error| format!("invalid participant test wire-frame limit: {error:?}"))?;
let services: Arc<dyn ConnectionServices> =
Arc::new(ParticipantOnlyServices::new(participant_service));
Ok(ConnectionSupervisor::with_services(services)?)
}