api(jsonrpc): Add parse_vcard() (#5202)

Add a function parsing a vCard file at the given path.

Co-authored-by: Hocuri <hocuri@gmx.de>
Co-authored-by: Asiel Díaz Benítez <asieldbenitez@gmail.com>
This commit is contained in:
iequidoo
2024-05-04 23:07:41 -03:00
committed by iequidoo
parent 4a738ebd19
commit 95238b6e17
7 changed files with 62 additions and 8 deletions

1
Cargo.lock generated
View File

@@ -1257,6 +1257,7 @@ dependencies = [
"axum", "axum",
"base64 0.22.1", "base64 0.22.1",
"deltachat", "deltachat",
"deltachat-contact-tools",
"env_logger 0.11.3", "env_logger 0.11.3",
"futures", "futures",
"log", "log",

View File

@@ -44,7 +44,8 @@ use regex::Regex;
pub struct VcardContact { pub struct VcardContact {
/// The email address, vcard property `email` /// The email address, vcard property `email`
pub addr: String, pub addr: String,
/// The contact's display name, vcard property `fn` /// The contact's display name, vcard property `fn`. Can be empty, one should use
/// `display_name()` to obtain the actual value.
pub display_name: String, pub display_name: String,
/// The contact's public PGP key in Base64, vcard property `key` /// The contact's public PGP key in Base64, vcard property `key`
pub key: Option<String>, pub key: Option<String>,
@@ -54,6 +55,16 @@ pub struct VcardContact {
pub timestamp: Result<u64>, pub timestamp: Result<u64>,
} }
impl VcardContact {
/// Returns the contact's display name.
pub fn display_name(&self) -> &str {
match self.display_name.is_empty() {
false => &self.display_name,
true => &self.addr,
}
}
}
/// Returns a vCard containing given contacts. /// Returns a vCard containing given contacts.
/// ///
/// Calling [`parse_vcard()`] on the returned result is a reverse operation. /// Calling [`parse_vcard()`] on the returned result is a reverse operation.
@@ -68,10 +79,7 @@ pub fn make_vcard(contacts: &[VcardContact]) -> String {
let mut res = "".to_string(); let mut res = "".to_string();
for c in contacts { for c in contacts {
let addr = &c.addr; let addr = &c.addr;
let display_name = match c.display_name.is_empty() { let display_name = c.display_name();
false => &c.display_name,
true => &c.addr,
};
res += &format!( res += &format!(
"BEGIN:VCARD\n\ "BEGIN:VCARD\n\
VERSION:4.0\n\ VERSION:4.0\n\

View File

@@ -15,6 +15,7 @@ required-features = ["webserver"]
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
deltachat = { path = ".." } deltachat = { path = ".." }
deltachat-contact-tools = { path = "../deltachat-contact-tools" }
num-traits = "0.2" num-traits = "0.2"
schemars = "0.8.19" schemars = "0.8.19"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@@ -1,5 +1,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::path::Path; use std::path::Path;
use std::str;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::{collections::HashMap, str::FromStr}; use std::{collections::HashMap, str::FromStr};
@@ -42,7 +43,7 @@ pub mod types;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
use types::account::Account; use types::account::Account;
use types::chat::FullChat; use types::chat::FullChat;
use types::contact::ContactObject; use types::contact::{ContactObject, VcardContact};
use types::events::Event; use types::events::Event;
use types::http::HttpResponse; use types::http::HttpResponse;
use types::message::{MessageData, MessageObject, MessageReadReceipt}; use types::message::{MessageData, MessageObject, MessageReadReceipt};
@@ -1426,6 +1427,16 @@ impl CommandApi {
Ok(contact_id.map(|id| id.to_u32())) Ok(contact_id.map(|id| id.to_u32()))
} }
/// Parses a vCard file located at the given path. Returns contacts in their original order.
async fn parse_vcard(&self, path: String) -> Result<Vec<VcardContact>> {
let vcard = tokio::fs::read(Path::new(&path)).await?;
let vcard = str::from_utf8(&vcard)?;
Ok(deltachat_contact_tools::parse_vcard(vcard)?
.into_iter()
.map(|c| c.into())
.collect())
}
// --------------------------------------------- // ---------------------------------------------
// chat // chat
// --------------------------------------------- // ---------------------------------------------

View File

@@ -1,4 +1,5 @@
use anyhow::Result; use anyhow::Result;
use deltachat::color;
use deltachat::context::Context; use deltachat::context::Context;
use serde::Serialize; use serde::Serialize;
use typescript_type_def::TypeDef; use typescript_type_def::TypeDef;
@@ -87,3 +88,34 @@ impl ContactObject {
}) })
} }
} }
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct VcardContact {
/// Email address.
addr: String,
display_name: String,
/// Public PGP key in Base64.
key: Option<String>,
/// Profile image in Base64.
profile_image: Option<String>,
/// Contact color as hex string.
color: String,
/// Last update timestamp.
timestamp: Option<u64>,
}
impl From<deltachat_contact_tools::VcardContact> for VcardContact {
fn from(vc: deltachat_contact_tools::VcardContact) -> Self {
let display_name = vc.display_name().to_string();
let color = color::str_to_color(&vc.addr.to_lowercase());
Self {
addr: vc.addr,
display_name,
key: vc.key,
profile_image: vc.profile_image,
color: color_int_to_hex_string(color),
timestamp: vc.timestamp.ok(),
}
}
}

View File

@@ -31,10 +31,11 @@ fn rgb_to_u32((r, g, b): (f64, f64, f64)) -> u32 {
/// ///
/// Saturation is set to maximum (100.0) to make colors distinguishable, and lightness is set to /// Saturation is set to maximum (100.0) to make colors distinguishable, and lightness is set to
/// half (50.0) to make colors suitable both for light and dark theme. /// half (50.0) to make colors suitable both for light and dark theme.
pub(crate) fn str_to_color(s: &str) -> u32 { pub fn str_to_color(s: &str) -> u32 {
rgb_to_u32(hsluv_to_rgb((str_to_angle(s), 100.0, 50.0))) rgb_to_u32(hsluv_to_rgb((str_to_angle(s), 100.0, 50.0)))
} }
/// Returns color as a "#RRGGBB" `String` where R, G, B are hex digits.
pub fn color_int_to_hex_string(color: u32) -> String { pub fn color_int_to_hex_string(color: u32) -> String {
format!("{color:#08x}").replace("0x", "#") format!("{color:#08x}").replace("0x", "#")
} }

View File

@@ -94,7 +94,7 @@ pub mod webxdc;
#[macro_use] #[macro_use]
mod dehtml; mod dehtml;
mod authres; mod authres;
mod color; pub mod color;
pub mod html; pub mod html;
pub mod net; pub mod net;
pub mod plaintext; pub mod plaintext;