summaryrefslogtreecommitdiff
path: root/src/capabilities.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/capabilities.rs203
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)
+ }
+}
+// }}}