use std::{
collections::HashMap,
sync::{Arc, Mutex, RwLock},
};
use crate::{
app::{SessionRunner, UserError, UserPlugins},
core::{
ConsensusPlugin, ConversationLifecycle, ConversationPluginsFactory, PluginConsensus,
ScoringConfig, StewardListConfig,
},
ds::SharedDeliveryService,
identity::Identity,
mls_crypto::{KeyPackageBytes, MlsError},
};
pub type SessionEntry<P, CP> = Arc<RwLock<SessionRunner<P, CP>>>;
pub(crate) type ConversationRegistry<P, CP> = RwLock<HashMap<String, SessionEntry<P, CP>>>;
pub struct User<P: ConsensusPlugin, CP: ConversationPluginsFactory> {
pub(crate) identity: Box<dyn Identity>,
pub(crate) app_id: Vec<u8>,
pub(crate) transport: SharedDeliveryService,
pub(crate) plugins: UserPlugins<P, CP>,
pub(crate) conversations: ConversationRegistry<P, CP>,
pub(crate) pending_lifecycle_events: Mutex<Vec<ConversationLifecycle>>,
}
impl<P: ConsensusPlugin, CP: ConversationPluginsFactory> User<P, CP> {
pub fn identity_string(&self) -> String {
self.identity.identity_display().to_string()
}
pub fn generate_key_package(&self) -> Result<KeyPackageBytes, MlsError> {
self.plugins.conversation_plugins.generate_key_package()
}
pub fn drain_lifecycle_events(&self) -> Vec<ConversationLifecycle> {
match self.pending_lifecycle_events.lock() {
Ok(mut buf) => std::mem::take(&mut *buf),
Err(_) => Vec::new(),
}
}
pub fn set_default_scoring_config(&mut self, config: ScoringConfig) {
self.plugins.default_scoring_config = config;
}
pub fn set_default_steward_list_config(&mut self, config: StewardListConfig) {
self.plugins.default_steward_list_config = config;
}
}
impl<P: ConsensusPlugin, CP: ConversationPluginsFactory> User<P, CP> {
pub(crate) fn self_identity(&self) -> &[u8] {
self.identity.identity_bytes()
}
pub(crate) fn emit_lifecycle(&self, event: ConversationLifecycle) {
if let Ok(mut buf) = self.pending_lifecycle_events.lock() {
buf.push(event);
}
}
pub(crate) fn build_consensus_service(&self) -> PluginConsensus<P> {
self.plugins.consensus.build_service()
}
pub(crate) async fn cleanup_consensus_scope(
&self,
conversation_name: &str,
) -> Result<(), UserError> {
if let Some(entry_arc) = self.lookup_entry(conversation_name)? {
entry_arc
.write()
.map_err(|_| UserError::LockPoisoned("session"))?
.cancel_all_auto_votes();
}
let scope = P::Scope::from(conversation_name.to_string());
self.plugins.consensus.delete_scope(&scope).await?;
Ok(())
}
pub fn new_with_plugins(
identity: Box<dyn Identity>,
plugins: UserPlugins<P, CP>,
transport: SharedDeliveryService,
) -> Self {
Self {
identity,
app_id: uuid::Uuid::new_v4().as_bytes().to_vec(),
transport,
plugins,
conversations: RwLock::new(HashMap::new()),
pending_lifecycle_events: Mutex::new(Vec::new()),
}
}
}