diff options
| author | Jakub Stachurski <jakub@wilkuu.nl> | 2026-08-14 23:05:12 +0200 |
|---|---|---|
| committer | Jakub Stachurski <jakub@wilkuu.nl> | 2026-08-14 23:05:12 +0200 |
| commit | 1047933fcb9e5b80401eae5e7e188dfd1fe32a91 (patch) | |
| tree | f9dd9b87e8687f8c56f40e11a86574213a9b8805 /src/capabilities.rs | |
| parent | af0b5d6ce4e5b9901afe50656f0016e1db72f2c5 (diff) | |
refactor: split into different files
Diffstat (limited to 'src/capabilities.rs')
| -rw-r--r-- | src/capabilities.rs | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/capabilities.rs b/src/capabilities.rs new file mode 100644 index 0000000..63a526c --- /dev/null +++ b/src/capabilities.rs @@ -0,0 +1,203 @@ +use std::any::Any; +use std::collections::BTreeMap; + +use crate::session::ISession; +use crate::*; + +// JMAP Core capability {{{ +/// JMAP Core URN +pub const URN_JMAP_CORE: &str = "urn:ietf:params:jmap:core"; + +// Capabilities {{{ +pub trait Capability { + // Get urn of the capability + fn get_urn(&self) -> &'static str; + + // Convert to any for downcasting, immutable + fn as_any(&self) -> &dyn Any; + + /// Convert to any for downcasting, mutable + fn as_any_mut(&mut self) -> &mut dyn Any; + + /// Convert to any for downcasting, consumes object + fn to_any(self) -> Box<dyn Any>; + + /// Given a capability and accountCapabilities Object get a capability + fn account_cap(&self, account_id: JMAPId, obj: &JObject) -> Box<dyn Capability>; + + /// Return the account if this capabilities has been given info for account + fn account(&self) -> Option<JMAPId>; +} + +/// A function that parses a JObject into a Capability Trait object. +/// This capability will be able to implement functionality to the JMAPSession object. +pub type CapabilityParser = Box<dyn Fn(JObject) -> Box<dyn Capability>>; +/// Collection of CapabilityParser +pub struct CapabilityParsers(pub BTreeMap<String, CapabilityParser>); + +impl Default for CapabilityParsers { + fn default() -> Self { + let mut c = Self::new(); + c.insert( + URN_JMAP_CORE.to_string(), + Box::new(JMAPCoreCapability::from_object), + ); + c + } +} + +impl CapabilityParsers { + /// Create a collection of parsers + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + /// Insert a parser + pub fn insert(&mut self, urn: String, parser: CapabilityParser) { + self.0.insert(urn, parser); + } + + /// Use the parsers to create a list of capabilities supported by the server in the session. + pub fn server_capabilities(&self, session: &ISession) -> ServerCapabilities { + let mut out = BTreeMap::new(); + for (urn, cap) in session.capabilities.iter() { + if let Some(parser) = self.0.get(urn) { + out.insert(urn.clone(), parser(cap.clone())); + }; + } + ServerCapabilities(out) + } +} + +/// The backing datatype in the CapabilityCollection trait. +pub type CapabilityMap = BTreeMap<String, Box<dyn Capability>>; + +/// Trait for list of capabilities, or any URN -> Capabilty collection +pub trait CapabilityCollection { + fn inner(&self) -> &CapabilityMap; + fn inner_mut(&mut self) -> &mut CapabilityMap; + + fn get_capability<T: 'static + Capability>(&self, urn: &str) -> Option<&T> { + self.inner().get(urn)?.as_any().downcast_ref::<T>() + } + + fn get_capability_mut<T: 'static + Capability>(&mut self, urn: &str) -> Option<&mut T> { + self.inner_mut() + .get_mut(urn)? + .as_any_mut() + .downcast_mut::<T>() + } +} + +/// Collection +pub struct ServerCapabilities(pub CapabilityMap); + +impl CapabilityCollection for ServerCapabilities { + fn inner(&self) -> &CapabilityMap { + &self.0 + } + + fn inner_mut(&mut self) -> &mut CapabilityMap { + &mut self.0 + } +} + +impl std::fmt::Debug for ServerCapabilities { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ServerCapabilities") + .field(&self.0.keys().collect::<Vec<&String>>()) + .finish() + } +} + +impl ServerCapabilities { + /// Evaluate AccountCapabilities using this list of capabilities and the intermediate session + pub fn account_capabilities( + &self, + client: &JMAPId, + session: &ISession, + ) -> Option<AccountCapabilities> { + session.accounts.get(client).map(|acc| { + let mut out = BTreeMap::new(); + let empty = JObj::new(); + for (urn, scap) in self.0.iter() { + let ccap = acc.accountCapabilities.get(urn).unwrap_or(&empty); + out.insert(urn.clone(), scap.account_cap(client.clone(), ccap)); + } + AccountCapabilities(out) + }) + } +} + +/// List of capabilities, specific to an account. +pub struct AccountCapabilities(pub CapabilityMap); + +impl std::fmt::Debug for AccountCapabilities { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("AccountCapabilities") + .field(&self.0.keys().collect::<Vec<&String>>()) + .finish() + } +} +impl CapabilityCollection for AccountCapabilities { + fn inner(&self) -> &CapabilityMap { + &self.0 + } + + fn inner_mut(&mut self) -> &mut CapabilityMap { + &mut self.0 + } +} + +// }}} +/// JMAP Core capability as outlined in RFC 8620 +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Clone)] +pub struct JMAPCoreCapability { + pub maxSizeUpload: JUint, + pub maxConcurrentUpload: JUint, + pub maxSizeRequest: JUint, + pub maxConcurrentRequests: JUint, + pub maxCallsInRequest: JUint, + pub maxObjectsInGet: JUint, + pub maxObjectsInSet: JUint, + pub collationAlgorithms: JArr<String>, + #[serde(skip)] + accountId: Option<JMAPId>, +} + +impl Capability for JMAPCoreCapability { + fn get_urn(&self) -> &'static str { + URN_JMAP_CORE + } + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn to_any(self) -> Box<dyn Any> { + Box::new(self) + } + + fn account_cap(&self, account_id: JMAPId, _: &JObject) -> Box<dyn Capability> { + let mut out: Box<JMAPCoreCapability> = Box::new(self.clone()); + out.accountId = Some(account_id); + out + } + + fn account(&self) -> Option<JMAPId> { + self.accountId.clone() + } +} + +impl JMAPCoreCapability { + fn from_object(obj: JObject) -> Box<dyn Capability> { + // TODO: Unwrap + let out: Self = serde_json::from_value(JUnknown::Object(obj).into()).unwrap(); + Box::new(out) + } +} +// }}} |
