liminal_server/services/
keys.rs1use super::{KeySender, ServiceEffectError};
16use crate::server::connection::LiminalConnectionServices;
17use liminal::protocol::{CausalContext, MessageEnvelope, SchemaId};
18use std::sync::Arc;
19
20pub const SESSION_KEYS_SCHEMA: &[u8] = b"liminal.services.session-keys/1";
24
25pub trait ServicePublisher: Send + Sync + std::fmt::Debug {
27 fn publish(
33 &self,
34 subject: &str,
35 envelope: &MessageEnvelope,
36 operation: &str,
37 ) -> Result<(), ServiceEffectError>;
38}
39
40#[derive(Debug)]
42pub struct ConnectionPublisher {
43 services: Arc<LiminalConnectionServices>,
44 host: String,
45}
46
47impl ConnectionPublisher {
48 #[must_use]
50 pub const fn new(services: Arc<LiminalConnectionServices>, host: String) -> Self {
51 Self { services, host }
52 }
53}
54
55impl ServicePublisher for ConnectionPublisher {
56 fn publish(
57 &self,
58 subject: &str,
59 envelope: &MessageEnvelope,
60 operation: &str,
61 ) -> Result<(), ServiceEffectError> {
62 self.services
63 .publish_embedded(&self.host, subject, envelope, Some(operation))
64 .map(|_outcome| ())
65 .map_err(|error| ServiceEffectError(format!("publish on {subject} refused: {error}")))
66 }
67}
68
69#[must_use]
71pub fn service_envelope(schema: &[u8], payload: &[u8]) -> MessageEnvelope {
72 MessageEnvelope::new(
73 SchemaId::from_schema_bytes(schema),
74 CausalContext::independent(),
75 payload.to_vec(),
76 )
77}
78
79#[derive(Debug)]
81pub struct ChannelKeySender {
82 publisher: Arc<dyn ServicePublisher>,
83}
84
85impl ChannelKeySender {
86 #[must_use]
88 pub const fn new(publisher: Arc<dyn ServicePublisher>) -> Self {
89 Self { publisher }
90 }
91}
92
93#[must_use]
95pub fn keys_subject(session: &str) -> String {
96 format!("sessions.{session}.keys")
97}
98
99impl KeySender for ChannelKeySender {
100 fn send(&self, session: &str, keys: &[u8], operation: &str) -> Result<(), ServiceEffectError> {
101 if keys.is_empty() {
102 return Err(ServiceEffectError(format!(
103 "no keys to send to session {session}"
104 )));
105 }
106 self.publisher.publish(
107 &keys_subject(session),
108 &service_envelope(SESSION_KEYS_SCHEMA, keys),
109 operation,
110 )
111 }
112}