mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-02-24 23:53:12 +00:00
chore: refactor context to be ffi specific, add chat.rs for manage chat experience.
This commit is contained in:
parent
a5943ae9ff
commit
67073a13de
171
conversations/src/chat.rs
Normal file
171
conversations/src/chat.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
common::{Chat, ChatStore, HasChatId},
|
||||||
|
errors::ChatError,
|
||||||
|
identity::Identity,
|
||||||
|
inbox::{Inbox, Introduction},
|
||||||
|
types::{AddressedEnvelope, ContentData},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ChatManager is the main entry point for the conversations API.
|
||||||
|
/// It manages identity, inbox, and active chats.
|
||||||
|
///
|
||||||
|
/// This is a pure Rust API - for FFI bindings, use `Context` which wraps this
|
||||||
|
/// with handle-based access.
|
||||||
|
pub struct ChatManager {
|
||||||
|
identity: Rc<Identity>,
|
||||||
|
store: ChatStore,
|
||||||
|
inbox: Inbox,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatManager {
|
||||||
|
/// Create a new ChatManager with a fresh identity.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let identity = Rc::new(Identity::new());
|
||||||
|
let inbox = Inbox::new(Rc::clone(&identity));
|
||||||
|
Self {
|
||||||
|
identity,
|
||||||
|
store: ChatStore::new(),
|
||||||
|
inbox,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new ChatManager with an existing identity.
|
||||||
|
pub fn with_identity(identity: Identity) -> Self {
|
||||||
|
let identity = Rc::new(identity);
|
||||||
|
let inbox = Inbox::new(Rc::clone(&identity));
|
||||||
|
Self {
|
||||||
|
identity,
|
||||||
|
store: ChatStore::new(),
|
||||||
|
inbox,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the local identity's public address.
|
||||||
|
pub fn local_address(&self) -> String {
|
||||||
|
self.identity.address()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an introduction bundle that can be shared with others.
|
||||||
|
/// They can use this to initiate a chat with you.
|
||||||
|
pub fn create_intro_bundle(&mut self) -> Result<Introduction, ChatError> {
|
||||||
|
let pkb = self.inbox.create_bundle();
|
||||||
|
Ok(Introduction::from(pkb))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a new private conversation with someone using their introduction bundle.
|
||||||
|
///
|
||||||
|
/// Returns the chat ID and envelopes that must be delivered to the remote party.
|
||||||
|
pub fn start_private_chat(
|
||||||
|
&mut self,
|
||||||
|
remote_bundle: &Introduction,
|
||||||
|
initial_message: &str,
|
||||||
|
) -> Result<(String, Vec<AddressedEnvelope>), ChatError> {
|
||||||
|
let (convo, payloads) = self
|
||||||
|
.inbox
|
||||||
|
.invite_to_private_convo(remote_bundle, initial_message.to_string())?;
|
||||||
|
|
||||||
|
let chat_id = convo.id().to_string();
|
||||||
|
|
||||||
|
let envelopes: Vec<AddressedEnvelope> = payloads
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.to_envelope(chat_id.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.store.insert_chat(convo);
|
||||||
|
|
||||||
|
Ok((chat_id, envelopes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a message to an existing chat.
|
||||||
|
///
|
||||||
|
/// Returns envelopes that must be delivered to chat participants.
|
||||||
|
pub fn send_message(
|
||||||
|
&mut self,
|
||||||
|
chat_id: &str,
|
||||||
|
content: &[u8],
|
||||||
|
) -> Result<Vec<AddressedEnvelope>, ChatError> {
|
||||||
|
let chat = self
|
||||||
|
.store
|
||||||
|
.get_mut_chat(chat_id)
|
||||||
|
.ok_or_else(|| ChatError::NoChatId(chat_id.to_string()))?;
|
||||||
|
|
||||||
|
let payloads = chat.send_message(content)?;
|
||||||
|
|
||||||
|
Ok(payloads
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.to_envelope(chat.remote_id()))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle an incoming payload from the network.
|
||||||
|
///
|
||||||
|
/// Returns the decrypted content if successful.
|
||||||
|
pub fn handle_incoming(&mut self, _payload: &[u8]) -> Result<ContentData, ChatError> {
|
||||||
|
// TODO: Implement proper payload handling
|
||||||
|
// 1. Determine if this is an inbox message or a chat message
|
||||||
|
// 2. Route to appropriate handler
|
||||||
|
// 3. Return decrypted content
|
||||||
|
Ok(ContentData {
|
||||||
|
conversation_id: "convo_id".into(),
|
||||||
|
data: vec![1, 2, 3, 4, 5, 6],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to an active chat.
|
||||||
|
pub fn get_chat(&self, chat_id: &str) -> Option<&dyn Chat> {
|
||||||
|
self.store.get_chat(chat_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to an active chat.
|
||||||
|
pub fn get_chat_mut(&mut self, chat_id: &str) -> Option<&mut dyn Chat> {
|
||||||
|
self.store.get_mut_chat(chat_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all active chat IDs.
|
||||||
|
pub fn list_chats(&self) -> Vec<String> {
|
||||||
|
self.store.chat_ids().map(|id| id.to_string()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChatManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_chat_manager() {
|
||||||
|
let manager = ChatManager::new();
|
||||||
|
assert!(!manager.local_address().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_intro_bundle() {
|
||||||
|
let mut manager = ChatManager::new();
|
||||||
|
let bundle = manager.create_intro_bundle();
|
||||||
|
assert!(bundle.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_private_chat() {
|
||||||
|
let mut alice = ChatManager::new();
|
||||||
|
let mut bob = ChatManager::new();
|
||||||
|
|
||||||
|
// Bob creates an intro bundle
|
||||||
|
let bob_intro = bob.create_intro_bundle().unwrap();
|
||||||
|
|
||||||
|
// Alice starts a chat with Bob
|
||||||
|
let result = alice.start_private_chat(&bob_intro, "Hello Bob!");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
let (chat_id, envelopes) = result.unwrap();
|
||||||
|
assert!(!chat_id.is_empty());
|
||||||
|
assert!(!envelopes.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,113 +1,146 @@
|
|||||||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
//! FFI-oriented context that wraps ChatManager with handle-based access.
|
||||||
|
//!
|
||||||
|
//! For pure Rust usage, prefer using `ChatManager` directly from `chat.rs`.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{Chat, ChatStore, HasChatId},
|
chat::ChatManager,
|
||||||
errors::ChatError,
|
errors::ChatError,
|
||||||
identity::Identity,
|
|
||||||
inbox::Inbox,
|
|
||||||
types::{AddressedEnvelope, ContentData},
|
types::{AddressedEnvelope, ContentData},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::inbox::Introduction;
|
pub use crate::inbox::Introduction;
|
||||||
|
|
||||||
//Offset handles to make debuging easier
|
// Offset handles to make debugging easier
|
||||||
const INITIAL_CONVO_HANDLE: u32 = 0xF5000001;
|
const INITIAL_CONVO_HANDLE: u32 = 0xF5000001;
|
||||||
|
|
||||||
/// Used to identify a conversation on the othersize of the FFI.
|
/// Used to identify a conversation across the FFI boundary.
|
||||||
|
/// This is an opaque integer handle that maps to an internal ChatId string.
|
||||||
pub type ConvoHandle = u32;
|
pub type ConvoHandle = u32;
|
||||||
|
|
||||||
// This is the main entry point to the conversations api.
|
/// Context is the FFI-oriented wrapper around ChatManager.
|
||||||
// Ctx manages lifetimes of objects to process and generate payloads.
|
///
|
||||||
|
/// It provides handle-based access to chats, suitable for FFI consumers
|
||||||
|
/// that can't work with Rust strings directly.
|
||||||
|
///
|
||||||
|
/// For pure Rust usage, prefer using `ChatManager` directly.
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
_identity: Rc<Identity>,
|
manager: ChatManager,
|
||||||
store: ChatStore,
|
/// Maps FFI handles to internal chat IDs
|
||||||
inbox: Inbox,
|
handle_to_chat_id: HashMap<ConvoHandle, String>,
|
||||||
convo_handle_map: HashMap<u32, Arc<str>>,
|
/// Maps chat IDs back to FFI handles for lookup
|
||||||
next_convo_handle: ConvoHandle,
|
chat_id_to_handle: HashMap<String, ConvoHandle>,
|
||||||
|
next_handle: ConvoHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let identity = Rc::new(Identity::new());
|
|
||||||
let inbox = Inbox::new(Rc::clone(&identity)); //
|
|
||||||
Self {
|
Self {
|
||||||
_identity: identity,
|
manager: ChatManager::new(),
|
||||||
store: ChatStore::new(),
|
handle_to_chat_id: HashMap::new(),
|
||||||
inbox,
|
chat_id_to_handle: HashMap::new(),
|
||||||
convo_handle_map: HashMap::new(),
|
next_handle: INITIAL_CONVO_HANDLE,
|
||||||
next_convo_handle: INITIAL_CONVO_HANDLE,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a Context wrapping an existing ChatManager.
|
||||||
|
pub fn with_manager(manager: ChatManager) -> Self {
|
||||||
|
Self {
|
||||||
|
manager,
|
||||||
|
handle_to_chat_id: HashMap::new(),
|
||||||
|
chat_id_to_handle: HashMap::new(),
|
||||||
|
next_handle: INITIAL_CONVO_HANDLE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying ChatManager for direct Rust API usage.
|
||||||
|
pub fn manager(&self) -> &ChatManager {
|
||||||
|
&self.manager
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying ChatManager mutably.
|
||||||
|
pub fn manager_mut(&mut self) -> &mut ChatManager {
|
||||||
|
&mut self.manager
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an introduction bundle for sharing with other users.
|
||||||
|
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> {
|
||||||
|
let intro = self.manager.create_intro_bundle()?;
|
||||||
|
Ok(intro.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new private conversation using a remote party's introduction bundle.
|
||||||
|
///
|
||||||
|
/// Returns an FFI handle and addressed envelopes to be delivered.
|
||||||
pub fn create_private_convo(
|
pub fn create_private_convo(
|
||||||
&mut self,
|
&mut self,
|
||||||
remote_bundle: &Introduction,
|
remote_bundle: &Introduction,
|
||||||
content: String,
|
content: String,
|
||||||
) -> (ConvoHandle, Vec<AddressedEnvelope>) {
|
) -> (ConvoHandle, Vec<AddressedEnvelope>) {
|
||||||
let (convo, payloads) = self
|
let (chat_id, envelopes) = self
|
||||||
.inbox
|
.manager
|
||||||
.invite_to_private_convo(remote_bundle, content)
|
.start_private_chat(remote_bundle, &content)
|
||||||
.unwrap_or_else(|_| todo!("Log/Surface Error"));
|
.unwrap_or_else(|_| todo!("Log/Surface Error"));
|
||||||
|
|
||||||
let payload_bytes = payloads
|
let handle = self.register_chat_id(chat_id);
|
||||||
.into_iter()
|
(handle, envelopes)
|
||||||
.map(|p| p.to_envelope(convo.id().to_string()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let convo_handle = self.add_convo(convo);
|
|
||||||
(convo_handle, payload_bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send content to an existing conversation identified by handle.
|
||||||
pub fn send_content(
|
pub fn send_content(
|
||||||
&mut self,
|
&mut self,
|
||||||
convo_handle: ConvoHandle,
|
convo_handle: ConvoHandle,
|
||||||
content: &[u8],
|
content: &[u8],
|
||||||
) -> Result<Vec<AddressedEnvelope>, ChatError> {
|
) -> Result<Vec<AddressedEnvelope>, ChatError> {
|
||||||
// Lookup convo from handle
|
let chat_id = self.resolve_handle(convo_handle)?;
|
||||||
let convo = self.get_convo_mut(convo_handle)?;
|
self.manager.send_message(&chat_id, content)
|
||||||
|
|
||||||
// Generate encrypted payloads
|
|
||||||
let payloads = convo.send_message(content)?;
|
|
||||||
|
|
||||||
// Attach conversation_ids to Envelopes
|
|
||||||
Ok(payloads
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| p.to_envelope(convo.remote_id()))
|
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_payload(&mut self, _payload: &[u8]) -> Option<ContentData> {
|
/// Handle an incoming payload.
|
||||||
// !TODO Replace Mock
|
pub fn handle_payload(&mut self, payload: &[u8]) -> Option<ContentData> {
|
||||||
Some(ContentData {
|
self.manager.handle_incoming(payload).ok()
|
||||||
conversation_id: "convo_id".into(),
|
|
||||||
data: vec![1, 2, 3, 4, 5, 6],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> {
|
/// Get the chat ID for a given handle.
|
||||||
let pkb = self.inbox.create_bundle();
|
pub fn get_chat_id(&self, handle: ConvoHandle) -> Option<&str> {
|
||||||
Ok(Introduction::from(pkb).into())
|
self.handle_to_chat_id.get(&handle).map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_convo(&mut self, convo: impl Chat + HasChatId + 'static) -> ConvoHandle {
|
/// Get the handle for a given chat ID.
|
||||||
let handle = self.next_convo_handle;
|
pub fn get_handle(&self, chat_id: &str) -> Option<ConvoHandle> {
|
||||||
self.next_convo_handle += 1;
|
self.chat_id_to_handle.get(chat_id).copied()
|
||||||
let convo_id = self.store.insert_chat(convo);
|
}
|
||||||
self.convo_handle_map.insert(handle, convo_id);
|
|
||||||
|
// --- Internal helpers ---
|
||||||
|
|
||||||
|
/// Register a chat ID and return its FFI handle.
|
||||||
|
fn register_chat_id(&mut self, chat_id: String) -> ConvoHandle {
|
||||||
|
// Check if already registered
|
||||||
|
if let Some(&handle) = self.chat_id_to_handle.get(&chat_id) {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle = self.next_handle;
|
||||||
|
self.next_handle += 1;
|
||||||
|
|
||||||
|
self.handle_to_chat_id.insert(handle, chat_id.clone());
|
||||||
|
self.chat_id_to_handle.insert(chat_id, handle);
|
||||||
|
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a mutable reference to a Convo for a given ConvoHandle
|
/// Resolve a handle to its chat ID.
|
||||||
fn get_convo_mut(&mut self, handle: ConvoHandle) -> Result<&mut dyn Chat, ChatError> {
|
fn resolve_handle(&self, handle: ConvoHandle) -> Result<String, ChatError> {
|
||||||
let convo_id = self
|
self.handle_to_chat_id
|
||||||
.convo_handle_map
|
|
||||||
.get(&handle)
|
.get(&handle)
|
||||||
.ok_or_else(|| ChatError::NoConvo(handle))?
|
.cloned()
|
||||||
.clone();
|
|
||||||
|
|
||||||
self.store
|
|
||||||
.get_mut_chat(&convo_id)
|
|
||||||
.ok_or_else(|| ChatError::NoConvo(handle))
|
.ok_or_else(|| ChatError::NoConvo(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@ pub enum ChatError {
|
|||||||
BadParsing(&'static str),
|
BadParsing(&'static str),
|
||||||
#[error("convo with handle: {0} was not found")]
|
#[error("convo with handle: {0} was not found")]
|
||||||
NoConvo(u32),
|
NoConvo(u32),
|
||||||
|
#[error("chat with id '{0}' was not found")]
|
||||||
|
NoChatId(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|||||||
@ -12,7 +12,7 @@ pub enum ErrorCode {
|
|||||||
UnknownError = -6,
|
UnknownError = -6,
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::context::{Context, ConvoHandle, Introduction};
|
use super::context::{Context, ConvoHandle, Introduction};
|
||||||
|
|
||||||
/// Opaque wrapper for Context
|
/// Opaque wrapper for Context
|
||||||
#[derive_ReprC]
|
#[derive_ReprC]
|
||||||
|
|||||||
146
conversations/src/ffi/context.rs
Normal file
146
conversations/src/ffi/context.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
//! FFI-oriented context that wraps ChatManager with handle-based access.
|
||||||
|
//!
|
||||||
|
//! For pure Rust usage, prefer using `ChatManager` directly from `chat.rs`.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
chat::ChatManager,
|
||||||
|
errors::ChatError,
|
||||||
|
types::{AddressedEnvelope, ContentData},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use crate::inbox::Introduction;
|
||||||
|
|
||||||
|
// Offset handles to make debugging easier
|
||||||
|
const INITIAL_CONVO_HANDLE: u32 = 0xF5000001;
|
||||||
|
|
||||||
|
/// Used to identify a conversation across the FFI boundary.
|
||||||
|
/// This is an opaque integer handle that maps to an internal ChatId string.
|
||||||
|
pub type ConvoHandle = u32;
|
||||||
|
|
||||||
|
/// Context is the FFI-oriented wrapper around ChatManager.
|
||||||
|
///
|
||||||
|
/// It provides handle-based access to chats, suitable for FFI consumers
|
||||||
|
/// that can't work with Rust strings directly.
|
||||||
|
///
|
||||||
|
/// For pure Rust usage, prefer using `ChatManager` directly.
|
||||||
|
pub struct Context {
|
||||||
|
manager: ChatManager,
|
||||||
|
/// Maps FFI handles to internal chat IDs
|
||||||
|
handle_to_chat_id: HashMap<ConvoHandle, String>,
|
||||||
|
/// Maps chat IDs back to FFI handles for lookup
|
||||||
|
chat_id_to_handle: HashMap<String, ConvoHandle>,
|
||||||
|
next_handle: ConvoHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
manager: ChatManager::new(),
|
||||||
|
handle_to_chat_id: HashMap::new(),
|
||||||
|
chat_id_to_handle: HashMap::new(),
|
||||||
|
next_handle: INITIAL_CONVO_HANDLE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a Context wrapping an existing ChatManager.
|
||||||
|
pub fn with_manager(manager: ChatManager) -> Self {
|
||||||
|
Self {
|
||||||
|
manager,
|
||||||
|
handle_to_chat_id: HashMap::new(),
|
||||||
|
chat_id_to_handle: HashMap::new(),
|
||||||
|
next_handle: INITIAL_CONVO_HANDLE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying ChatManager for direct Rust API usage.
|
||||||
|
pub fn manager(&self) -> &ChatManager {
|
||||||
|
&self.manager
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the underlying ChatManager mutably.
|
||||||
|
pub fn manager_mut(&mut self) -> &mut ChatManager {
|
||||||
|
&mut self.manager
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an introduction bundle for sharing with other users.
|
||||||
|
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> {
|
||||||
|
let intro = self.manager.create_intro_bundle()?;
|
||||||
|
Ok(intro.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new private conversation using a remote party's introduction bundle.
|
||||||
|
///
|
||||||
|
/// Returns an FFI handle and addressed envelopes to be delivered.
|
||||||
|
pub fn create_private_convo(
|
||||||
|
&mut self,
|
||||||
|
remote_bundle: &Introduction,
|
||||||
|
content: String,
|
||||||
|
) -> (ConvoHandle, Vec<AddressedEnvelope>) {
|
||||||
|
let (chat_id, envelopes) = self
|
||||||
|
.manager
|
||||||
|
.start_private_chat(remote_bundle, &content)
|
||||||
|
.unwrap_or_else(|_| todo!("Log/Surface Error"));
|
||||||
|
|
||||||
|
let handle = self.register_chat_id(chat_id);
|
||||||
|
(handle, envelopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send content to an existing conversation identified by handle.
|
||||||
|
pub fn send_content(
|
||||||
|
&mut self,
|
||||||
|
convo_handle: ConvoHandle,
|
||||||
|
content: &[u8],
|
||||||
|
) -> Result<Vec<AddressedEnvelope>, ChatError> {
|
||||||
|
let chat_id = self.resolve_handle(convo_handle)?;
|
||||||
|
self.manager.send_message(&chat_id, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle an incoming payload.
|
||||||
|
pub fn handle_payload(&mut self, payload: &[u8]) -> Option<ContentData> {
|
||||||
|
self.manager.handle_incoming(payload).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the chat ID for a given handle.
|
||||||
|
pub fn get_chat_id(&self, handle: ConvoHandle) -> Option<&str> {
|
||||||
|
self.handle_to_chat_id.get(&handle).map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the handle for a given chat ID.
|
||||||
|
pub fn get_handle(&self, chat_id: &str) -> Option<ConvoHandle> {
|
||||||
|
self.chat_id_to_handle.get(chat_id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Internal helpers ---
|
||||||
|
|
||||||
|
/// Register a chat ID and return its FFI handle.
|
||||||
|
fn register_chat_id(&mut self, chat_id: String) -> ConvoHandle {
|
||||||
|
// Check if already registered
|
||||||
|
if let Some(&handle) = self.chat_id_to_handle.get(&chat_id) {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle = self.next_handle;
|
||||||
|
self.next_handle += 1;
|
||||||
|
|
||||||
|
self.handle_to_chat_id.insert(handle, chat_id.clone());
|
||||||
|
self.chat_id_to_handle.insert(chat_id, handle);
|
||||||
|
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a handle to its chat ID.
|
||||||
|
fn resolve_handle(&self, handle: ConvoHandle) -> Result<String, ChatError> {
|
||||||
|
self.handle_to_chat_id
|
||||||
|
.get(&handle)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| ChatError::NoConvo(handle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,4 @@
|
|||||||
pub mod api;
|
pub mod api;
|
||||||
|
mod context;
|
||||||
|
|
||||||
|
pub use context::{Context, ConvoHandle, Introduction};
|
||||||
|
|||||||
@ -8,7 +8,6 @@ use std::rc::Rc;
|
|||||||
use crypto::{PrekeyBundle, SecretKey};
|
use crypto::{PrekeyBundle, SecretKey};
|
||||||
|
|
||||||
use crate::common::{Chat, ChatId, HasChatId, InboundMessageHandler};
|
use crate::common::{Chat, ChatId, HasChatId, InboundMessageHandler};
|
||||||
use crate::context::Introduction;
|
|
||||||
use crate::dm::privatev1::PrivateV1Convo;
|
use crate::dm::privatev1::PrivateV1Convo;
|
||||||
use crate::errors::ChatError;
|
use crate::errors::ChatError;
|
||||||
use crate::identity::Identity;
|
use crate::identity::Identity;
|
||||||
@ -17,6 +16,8 @@ use crate::inbox::handshake::InboxHandshake;
|
|||||||
use crate::proto::{self, CopyBytes};
|
use crate::proto::{self, CopyBytes};
|
||||||
use crate::types::{AddressedEncryptedPayload, ContentData};
|
use crate::types::{AddressedEncryptedPayload, ContentData};
|
||||||
|
|
||||||
|
use super::Introduction;
|
||||||
|
|
||||||
/// Compute the deterministic Delivery_address for an installation
|
/// Compute the deterministic Delivery_address for an installation
|
||||||
fn delivery_address_for_installation(_: PublicKey) -> String {
|
fn delivery_address_for_installation(_: PublicKey) -> String {
|
||||||
// TODO: Implement Delivery Address
|
// TODO: Implement Delivery Address
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
|
pub mod chat;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod dm;
|
pub mod dm;
|
||||||
pub mod ffi;
|
pub mod ffi;
|
||||||
pub mod group;
|
pub mod group;
|
||||||
pub mod inbox;
|
pub mod inbox;
|
||||||
|
|
||||||
mod context;
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod identity;
|
mod identity;
|
||||||
mod proto;
|
mod proto;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user