Skip to content

Instantly share code, notes, and snippets.

@rjbs
Created Aug 29, 2021
Embed
What would you like to do?
use reqwest::blocking::Client;
use reqwest::header::{AUTHORIZATION,CONTENT_TYPE};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::env;
#[derive(Serialize, Deserialize, Debug)]
struct FilterOperator {
operator: String,
conditions: Vec< HashMap<String, JMAPArgValue> >,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JMAPMailboxTiny {
id: String,
name: String,
total_threads: u64,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum JMAPArgValue {
ArgString(String),
ArgInteger(i64),
ArgBool(bool),
ArgFilter(FilterOperator),
ArgSimpleMap(HashMap<String,String>),
ArgSimpleVec(Vec<String>),
ArgMailboxTinyVec(Vec<JMAPMailboxTiny>),
}
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JMAPRequest<'a> {
using: Vec<&'a str>,
method_calls: Vec<JMAPMethodCall>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct JMAPResponse {
method_responses: Vec<(String, HashMap<String, JMAPArgValue>, String)>,
}
fn backref (result_of: &str, name: &str, path: &str) -> JMAPArgValue {
let mut backref = HashMap::new();
backref.insert(String::from("name"), name.to_string());
backref.insert(String::from("path"), path.to_string());
backref.insert(String::from("resultOf"), result_of.to_string());
JMAPArgValue::ArgSimpleMap(backref)
}
#[derive(Serialize, Debug)]
struct JMAPMethodCall(
String,
HashMap<String, JMAPArgValue>,
String,
);
impl JMAPMethodCall {
fn new (cid: &str, name: &str) -> JMAPMethodCall {
JMAPMethodCall(
name.to_string(),
HashMap::new(),
cid.to_string(),
)
}
fn set_arg (&mut self, key: &str, value: JMAPArgValue) {
self.1.insert(key.to_string(), value);
}
fn set_args (&mut self, mut pairs: Vec< (&str, JMAPArgValue) >) {
for (key, value) in pairs.drain(..) {
self.1.insert(key.to_string(), value);
}
}
}
fn jmstr (string: &str) -> JMAPArgValue {
JMAPArgValue::ArgString(string.to_string())
}
fn jstrv (strings: Vec<&str>) -> JMAPArgValue {
JMAPArgValue::ArgSimpleVec(
strings.iter().map(|&s| { s.to_string() }).collect()
)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let account_id = env::var("JMAP_ACCOUNTID")?;
let b64_auth = env::var("JMAP_AUTH")?;
let client = Client::new();
let mut inbox_filter = HashMap::new();
inbox_filter.insert(String::from("role"), jmstr("inbox"));
let mut name_filter = HashMap::new();
name_filter.insert(String::from("name"), jmstr("@"));
let filter = FilterOperator {
operator: String::from("OR"),
conditions: vec![ inbox_filter, name_filter ],
};
let mut query_method = JMAPMethodCall::new("a", "Mailbox/query");
query_method.set_args(vec![
("accountId", JMAPArgValue::ArgString(account_id.clone())),
("filter", JMAPArgValue::ArgFilter(filter)),
]);
let mut get_method = JMAPMethodCall::new("b", "Mailbox/get");
get_method.set_args(vec![
("accountId", JMAPArgValue::ArgString(account_id.clone())),
("#ids", backref("a", "Mailbox/query", "/ids")),
("properties", jstrv(vec![ "name", "totalThreads" ])),
]);
let jreq = JMAPRequest {
using: vec![
"urn:ietf:params:jmap:core",
"urn:ietf:params:jmap:mail"
],
method_calls: vec![
query_method,
get_method,
],
};
let mut auth = String::from("Basic ");
auth.push_str(&b64_auth);
let body = client.post("https://jmap.fastmail.com/api")
.header(AUTHORIZATION, auth)
.header(CONTENT_TYPE, "application/json")
.json(&jreq)
.send()?;
let json = body.text()?;
let response : JMAPResponse = serde_json::from_str(&json).unwrap();
let (_name, arg, _cid) = &response.method_responses[1];
let mailbox_list = match arg.get("list").unwrap() {
JMAPArgValue::ArgMailboxTinyVec(ml) => ml,
_ => panic!("extremely bogus response")
};
for mailbox in mailbox_list.iter() {
println!("mailbox_count{{label=\"{}\"}} {}", mailbox.name, mailbox.total_threads);
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment