Browse Source

JMAP WIP #5

tags/alpha-0.6.0
Manos Pitsidianakis 1 year ago
parent
commit
a41dc6c38a
Signed by: epilys GPG Key ID: 73627C2F690DF710
6 changed files with 200 additions and 23 deletions
  1. +4
    -6
      melib/src/backends/jmap.rs
  2. +7
    -4
      melib/src/backends/jmap/objects/email.rs
  3. +6
    -1
      melib/src/backends/jmap/objects/mailbox.rs
  4. +64
    -8
      melib/src/backends/jmap/protocol.rs
  5. +65
    -4
      melib/src/backends/jmap/rfc8620.rs
  6. +54
    -0
      melib/src/backends/jmap/rfc8620/argument.rs

+ 4
- 6
melib/src/backends/jmap.rs View File

@ -190,12 +190,10 @@ impl MailBackend for JmapType {
let handle = {
let tx = w.tx();
let closure = move |_work_context| {
tx.send(AsyncStatus::Payload(
protocol::get_message_list(&connection, &folders.read().unwrap()[&folder_hash])
.and_then(|ids| {
protocol::get_message(&connection, std::dbg!(&ids).as_slice())
}),
))
tx.send(AsyncStatus::Payload(protocol::get(
&connection,
&folders.read().unwrap()[&folder_hash],
)))
.unwrap();
tx.send(AsyncStatus::Finished).unwrap();
};

+ 7
- 4
melib/src/backends/jmap/objects/email.rs View File

@ -399,7 +399,6 @@ impl EmailGet {
}
}
_impl!(get_call: Get<EmailObject>);
_impl!(body_properties: Vec<String>);
_impl!(fetch_text_body_values: bool);
_impl!(fetch_html_body_values: bool);
@ -410,8 +409,8 @@ impl EmailGet {
#[derive(Serialize, Deserialize, Default, Debug)]
#[serde(rename_all = "camelCase")]
pub struct EmailFilterCondition {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub in_mailboxes: Vec<Id>,
#[serde(skip_serializing_if = "Option::is_none")]
pub in_mailbox: Option<Id>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub in_mailbox_other_than: Vec<Id>,
#[serde(skip_serializing_if = "String::is_empty")]
@ -455,7 +454,11 @@ pub struct EmailFilterCondition {
}
impl EmailFilterCondition {
_impl!(in_mailboxes: Vec<Id>);
pub fn new() -> Self {
Self::default()
}
_impl!(in_mailbox: Option<Id>);
_impl!(in_mailbox_other_than: Vec<Id>);
_impl!(before: UtcDate);
_impl!(after: UtcDate);

+ 6
- 1
melib/src/backends/jmap/objects/mailbox.rs View File

@ -60,7 +60,12 @@ pub struct MailboxGet {
#[serde(flatten)]
pub get_call: Get<MailboxObject>,
}
impl MailboxGet {
pub fn new(get_call: Get<MailboxObject>) -> Self {
MailboxGet { get_call }
}
}
impl Method<MailboxObject> for MailboxGet {
const NAME: &'static str = "Mailbox/query";
const NAME: &'static str = "Mailbox/get";
}

+ 64
- 8
melib/src/backends/jmap/protocol.rs View File

@ -80,7 +80,7 @@ impl Request {
}
}
pub fn add_call<M: Method<O>, O: Object>(&mut self, call: M) -> usize {
pub fn add_call<M: Method<O>, O: Object>(&mut self, call: &M) -> usize {
let seq = get_request_no!(self.request_no);
self.method_calls
.push(serde_json::to_value((M::NAME, call, &format!("m{}", seq))).unwrap());
@ -177,10 +177,7 @@ pub struct JsonResponse<'a> {
pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<String>> {
let seq = get_request_no!(conn.request_no);
let email_call: EmailQueryCall = EmailQueryCall {
filter: EmailFilterCondition {
in_mailboxes: vec![folder.id.clone()],
..Default::default()
},
filter: EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
collapse_threads: false,
position: 0,
fetch_threads: true,
@ -202,7 +199,7 @@ pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result
};
let mut req = Request::new(conn.request_no.clone());
req.add_call(email_call);
req.add_call(&email_call);
/*
{
@ -291,12 +288,14 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result
let seq = get_request_no!(conn.request_no);
let email_call: EmailGet = EmailGet::new(
Get::new()
.ids(Some(ids.iter().cloned().collect::<Vec<String>>()))
.ids(Some(JmapArgument::value(
ids.iter().cloned().collect::<Vec<String>>(),
)))
.account_id(conn.account_id.lock().unwrap().clone()),
);
let mut req = Request::new(conn.request_no.clone());
req.add_call(email_call);
req.add_call(&email_call);
let res = conn
.client
.lock()
@ -331,3 +330,60 @@ pub fn get_message(conn: &JmapConnection, ids: &[String]) -> Result
}))
*/
pub fn get(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<Envelope>> {
let email_query_call: EmailQueryCall = EmailQueryCall {
filter: EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
collapse_threads: false,
position: 0,
fetch_threads: true,
fetch_messages: true,
fetch_message_properties: vec![
MessageProperty::ThreadId,
MessageProperty::MailboxId,
MessageProperty::IsUnread,
MessageProperty::IsFlagged,
MessageProperty::IsAnswered,
MessageProperty::IsDraft,
MessageProperty::HasAttachment,
MessageProperty::From,
MessageProperty::To,
MessageProperty::Subject,
MessageProperty::Date,
MessageProperty::Preview,
],
};
let mut req = Request::new(conn.request_no.clone());
let prev_seq = req.add_call(&email_query_call);
let email_call: EmailGet = EmailGet::new(
Get::new()
.ids(Some(JmapArgument::reference(
prev_seq,
&email_query_call,
"/ids",
)))
.account_id(conn.account_id.lock().unwrap().clone()),
);
req.add_call(&email_call);
let res = conn
.client
.lock()
.unwrap()
.post("https://jmap-proxy.local/jmap/fc32dffe-14e7-11ea-a277-2477037a1804/")
.json(&req)
.send();
let res_text = res?.text()?;
let mut v: MethodResponse = serde_json::from_str(&res_text).unwrap();
let e = GetResponse::<EmailObject>::try_from(v.method_responses.pop().unwrap())?;
let GetResponse::<EmailObject> { list, .. } = e;
Ok(list
.into_iter()
.map(std::convert::Into::into)
.collect::<Vec<Envelope>>())
}

+ 65
- 4
melib/src/backends/jmap/rfc8620.rs View File

@ -21,13 +21,16 @@
use super::Id;
use core::marker::PhantomData;
use serde::{de::DeserializeOwned, Serialize};
use serde::de::DeserializeOwned;
use serde::ser::{Serialize, SerializeStruct, Serializer};
use serde_json::{value::RawValue, Value};
mod filters;
pub use filters::*;
mod comparator;
pub use comparator::*;
mod argument;
pub use argument::*;
use super::protocol::{Method, Response};
use std::collections::HashMap;
@ -102,7 +105,7 @@ pub struct Account {
extra_properties: HashMap<String, Value>,
}
#[derive(Deserialize, Serialize, Debug)]
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Get<OBJ: Object>
where
@ -111,7 +114,8 @@ where
#[serde(skip_serializing_if = "String::is_empty")]
pub account_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ids: Option<Vec<String>>,
#[serde(flatten)]
pub ids: Option<JmapArgument<Vec<String>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<Vec<String>>,
#[serde(skip)]
@ -131,9 +135,65 @@ where
}
}
_impl!(account_id: String);
_impl!(ids: Option<Vec<String>>);
_impl!(ids: Option<JmapArgument<Vec<String>>>);
_impl!(properties: Option<Vec<String>>);
}
impl<OBJ: Object + Serialize + std::fmt::Debug> Serialize for Get<OBJ> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut fields_no = 0;
if !self.account_id.is_empty() {
fields_no += 1;
}
if !self.ids.is_none() {
fields_no += 1;
}
if !self.properties.is_none() {
fields_no += 1;
}
let mut state = serializer.serialize_struct("Get", fields_no)?;
if !self.account_id.is_empty() {
state.serialize_field("accountId", &self.account_id)?;
}
match self.ids.as_ref() {
None => {}
Some(JmapArgument::Value(ref v)) => state.serialize_field("ids", v)?,
Some(JmapArgument::ResultReference {
ref result_of,
ref name,
ref path,
}) => {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct A<'a> {
result_of: &'a str,
name: &'a str,
path: &'a str,
}
state.serialize_field(
"#ids",
&A {
result_of,
name,
path,
},
)?;
}
}
if !self.properties.is_none() {
state.serialize_field("properties", self.properties.as_ref().unwrap());
}
state.end()
}
}
// The response has the following arguments:
//
// o accountId: "Id"
@ -192,6 +252,7 @@ pub struct MethodResponse<'a> {
pub struct GetResponse<OBJ: Object> {
#[serde(skip_serializing_if = "String::is_empty")]
pub account_id: String,
#[serde(default)]
pub state: String,
pub list: Vec<OBJ>,
pub not_found: Vec<String>,

+ 54
- 0
melib/src/backends/jmap/rfc8620/argument.rs View File

@ -0,0 +1,54 @@
/*
* meli - jmap module.
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::backends::jmap::protocol::Method;
use crate::backends::jmap::rfc8620::Object;
use serde::de::DeserializeOwned;
use serde::ser::{Serialize, SerializeStruct, Serializer};
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum JmapArgument<T> {
Value(T),
ResultReference {
result_of: String,
name: String,
path: String,
},
}
impl<T> JmapArgument<T> {
pub fn value(v: T) -> Self {
JmapArgument::Value(v)
}
pub fn reference<M, OBJ>(result_of: usize, method: &M, path: &str) -> Self
where
M: Method<OBJ>,
OBJ: Object,
{
JmapArgument::ResultReference {
result_of: format!("m{}", result_of),
name: M::NAME.to_string(),
path: path.to_string(),
}
}
}

Loading…
Cancel
Save