From 030c489d2bc34f267cadacdec8cd13249672ccb5 Mon Sep 17 00:00:00 2001 From: Jakub Stachurski Date: Sat, 22 Aug 2026 00:59:22 +0200 Subject: Make Capability into CapabilityDyn and Add non-dyn Capability trait --- src/capabilities.rs | 109 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 41 deletions(-) (limited to 'src/capabilities.rs') diff --git a/src/capabilities.rs b/src/capabilities.rs index 63a526c..63aa6e8 100644 --- a/src/capabilities.rs +++ b/src/capabilities.rs @@ -1,6 +1,8 @@ use std::any::Any; use std::collections::BTreeMap; +use serde::{Deserialize, Serialize}; + use crate::session::ISession; use crate::*; @@ -9,7 +11,23 @@ use crate::*; pub const URN_JMAP_CORE: &str = "urn:ietf:params:jmap:core"; // Capabilities {{{ -pub trait Capability { + +pub trait Capability: 'static + Sized { + const URN: &str; + + fn from_object(obj: &JObject) -> Self; + fn account_cap(&self, account_id: JMAPId, obj: &JObject) -> Self; + fn account(&self) -> Option; + fn from_object_dyn(obj: &JObject) -> Box { + Box::new(Self::from_object(obj)) + } + + fn is_account_capability(&self) -> bool { + self.account().is_some() + } +} + +pub trait CapabilityDyn { // Get urn of the capability fn get_urn(&self) -> &'static str; @@ -23,38 +41,61 @@ pub trait Capability { fn to_any(self) -> Box; /// Given a capability and accountCapabilities Object get a capability - fn account_cap(&self, account_id: JMAPId, obj: &JObject) -> Box; + fn account_cap_dyn(&self, account_id: JMAPId, obj: &JObject) -> Box; /// Return the account if this capabilities has been given info for account - fn account(&self) -> Option; + fn account_dyn(&self) -> Option; +} + +impl CapabilityDyn for T { + fn get_urn(&self) -> &'static str { + T::URN + } + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn to_any(self) -> Box { + Box::new(self) + } + + fn account_cap_dyn(&self, account_id: JMAPId, obj: &JObject) -> Box { + Box::new(self.account_cap(account_id, obj)) + } + + fn account_dyn(&self) -> Option { + self.account() + } } /// 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 Box>; +pub type CapabilityParser = Box Box>; /// Collection of CapabilityParser -pub struct CapabilityParsers(pub BTreeMap); +pub struct CapabilityRegistry(pub BTreeMap); -impl Default for CapabilityParsers { +impl Default for CapabilityRegistry { fn default() -> Self { let mut c = Self::new(); - c.insert( - URN_JMAP_CORE.to_string(), - Box::new(JMAPCoreCapability::from_object), - ); + c.add_capability::(); c } } -impl CapabilityParsers { +impl CapabilityRegistry { /// 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); + pub fn add_capability(&mut self) { + self.0 + .insert(T::URN.to_string(), Box::new(T::from_object_dyn)); } /// Use the parsers to create a list of capabilities supported by the server in the session. @@ -62,7 +103,7 @@ impl CapabilityParsers { 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())); + out.insert(urn.clone(), parser(cap)); }; } ServerCapabilities(out) @@ -70,20 +111,20 @@ impl CapabilityParsers { } /// The backing datatype in the CapabilityCollection trait. -pub type CapabilityMap = BTreeMap>; +pub type CapabilityMap = BTreeMap>; /// 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(&self, urn: &str) -> Option<&T> { - self.inner().get(urn)?.as_any().downcast_ref::() + fn get_capability(&self) -> Option<&T> { + self.inner().get(T::URN)?.as_any().downcast_ref::() } - fn get_capability_mut(&mut self, urn: &str) -> Option<&mut T> { + fn get_capability_mut(&mut self) -> Option<&mut T> { self.inner_mut() - .get_mut(urn)? + .get_mut(T::URN)? .as_any_mut() .downcast_mut::() } @@ -122,7 +163,7 @@ impl ServerCapabilities { 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)); + out.insert(urn.clone(), scap.account_cap_dyn(client.clone(), ccap)); } AccountCapabilities(out) }) @@ -167,23 +208,16 @@ pub struct JMAPCoreCapability { } impl Capability for JMAPCoreCapability { - fn get_urn(&self) -> &'static str { - URN_JMAP_CORE - } - fn as_any(&self) -> &dyn Any { - self - } + const URN: &str = URN_JMAP_CORE; - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn to_any(self) -> Box { - Box::new(self) + fn from_object(obj: &JObject) -> Self { + // TODO: Unwrap + let out: Self = serde_json::from_value(JUnknown::Object(obj.clone()).into()).unwrap(); + out } - fn account_cap(&self, account_id: JMAPId, _: &JObject) -> Box { - let mut out: Box = Box::new(self.clone()); + fn account_cap(&self, account_id: JMAPId, _: &JObject) -> Self { + let mut out = self.clone(); out.accountId = Some(account_id); out } @@ -193,11 +227,4 @@ impl Capability for JMAPCoreCapability { } } -impl JMAPCoreCapability { - fn from_object(obj: JObject) -> Box { - // TODO: Unwrap - let out: Self = serde_json::from_value(JUnknown::Object(obj).into()).unwrap(); - Box::new(out) - } -} // }}} -- cgit v1.3.1