2026-04-08 23:15:48 +02:00
|
|
|
use libchat::{
|
|
|
|
|
AddressedEnvelope, ChatError, ChatStorage, ContentData, Context, ConversationIdOwned,
|
2026-05-19 11:54:54 -07:00
|
|
|
DeliveryService, Introduction, StorageConfig,
|
2026-04-08 23:15:48 +02:00
|
|
|
};
|
2026-03-24 18:21:00 -07:00
|
|
|
|
2026-05-19 11:54:54 -07:00
|
|
|
use logoschat_components::EphemeralRegistry;
|
|
|
|
|
|
|
|
|
|
use crate::errors::ClientError;
|
2026-04-08 23:15:48 +02:00
|
|
|
|
|
|
|
|
pub struct ChatClient<D: DeliveryService> {
|
2026-05-19 11:54:54 -07:00
|
|
|
ctx: Context<D, EphemeralRegistry, ChatStorage>,
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 11:54:54 -07:00
|
|
|
impl<D: DeliveryService + 'static> ChatClient<D> {
|
2026-04-08 23:15:48 +02:00
|
|
|
/// Create an in-memory, ephemeral client. Identity is lost on drop.
|
|
|
|
|
pub fn new(name: impl Into<String>, delivery: D) -> Self {
|
2026-05-19 11:54:54 -07:00
|
|
|
let registry = EphemeralRegistry::new();
|
2026-04-08 23:15:48 +02:00
|
|
|
let store = ChatStorage::in_memory();
|
2026-03-24 18:21:00 -07:00
|
|
|
Self {
|
2026-05-19 11:54:54 -07:00
|
|
|
ctx: Context::new_with_name(name, delivery, registry, store).unwrap(),
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 23:15:48 +02:00
|
|
|
/// Open or create a persistent client backed by `StorageConfig`.
|
|
|
|
|
///
|
|
|
|
|
/// If an identity already exists in storage it is loaded; otherwise a new
|
|
|
|
|
/// one is created and saved.
|
|
|
|
|
pub fn open(
|
|
|
|
|
name: impl Into<String>,
|
|
|
|
|
config: StorageConfig,
|
|
|
|
|
delivery: D,
|
|
|
|
|
) -> Result<Self, ClientError<D::Error>> {
|
|
|
|
|
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
2026-05-19 11:54:54 -07:00
|
|
|
let registry = EphemeralRegistry::new();
|
|
|
|
|
let ctx = Context::new_from_store(name, delivery, registry, store)?;
|
|
|
|
|
Ok(Self { ctx })
|
2026-04-08 23:15:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the installation name (identity label) of this client.
|
|
|
|
|
pub fn installation_name(&self) -> &str {
|
|
|
|
|
self.ctx.installation_name()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Produce a serialised introduction bundle for sharing out-of-band.
|
|
|
|
|
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ClientError<D::Error>> {
|
|
|
|
|
self.ctx.create_intro_bundle().map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parse intro bundle bytes, initiate a private conversation, and deliver
|
|
|
|
|
/// all outbound envelopes. Returns this side's conversation ID.
|
|
|
|
|
pub fn create_conversation(
|
|
|
|
|
&mut self,
|
|
|
|
|
intro_bundle: &[u8],
|
|
|
|
|
initial_content: &[u8],
|
|
|
|
|
) -> Result<ConversationIdOwned, ClientError<D::Error>> {
|
|
|
|
|
let intro = Introduction::try_from(intro_bundle)?;
|
|
|
|
|
let (convo_id, envelopes) = self.ctx.create_private_convo(&intro, initial_content)?;
|
|
|
|
|
self.dispatch_all(envelopes)?;
|
|
|
|
|
Ok(convo_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// List all conversation IDs known to this client.
|
|
|
|
|
pub fn list_conversations(&self) -> Result<Vec<ConversationIdOwned>, ClientError<D::Error>> {
|
|
|
|
|
self.ctx.list_conversations().map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Encrypt `content` and dispatch all outbound envelopes.
|
|
|
|
|
pub fn send_message(
|
|
|
|
|
&mut self,
|
|
|
|
|
convo_id: &ConversationIdOwned,
|
|
|
|
|
content: &[u8],
|
|
|
|
|
) -> Result<(), ClientError<D::Error>> {
|
|
|
|
|
let envelopes = self.ctx.send_content(convo_id.as_ref(), content)?;
|
|
|
|
|
self.dispatch_all(envelopes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Decrypt an inbound payload. Returns `Some(ContentData)` for user
|
|
|
|
|
/// content, `None` for protocol frames.
|
|
|
|
|
pub fn receive(
|
|
|
|
|
&mut self,
|
|
|
|
|
payload: &[u8],
|
|
|
|
|
) -> Result<Option<ContentData>, ClientError<D::Error>> {
|
|
|
|
|
self.ctx.handle_payload(payload).map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn dispatch_all(
|
|
|
|
|
&mut self,
|
|
|
|
|
envelopes: Vec<AddressedEnvelope>,
|
|
|
|
|
) -> Result<(), ClientError<D::Error>> {
|
|
|
|
|
for env in envelopes {
|
2026-05-19 11:54:54 -07:00
|
|
|
let mut delivery = self.ctx.ds();
|
|
|
|
|
delivery.publish(env).map_err(ClientError::Delivery)?;
|
2026-04-08 23:15:48 +02:00
|
|
|
}
|
|
|
|
|
Ok(())
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
}
|