mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
Change the JSON API function to be from a serialised struct
This is the first experiment towards using structs to define the API. It adds it as a new method on the existing Chat struct. The types in this struct could improve as well as many other things. But this is a straight forward translation of the existing json! macro into a struct.
This commit is contained in:
committed by
Floris Bruynooghe
parent
f2f8898004
commit
ec40dd1b6f
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -743,6 +743,7 @@ dependencies = [
|
|||||||
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ libc = "0.2"
|
|||||||
human-panic = "1.0.1"
|
human-panic = "1.0.1"
|
||||||
num-traits = "0.2.6"
|
num-traits = "0.2.6"
|
||||||
failure = "0.1.6"
|
failure = "0.1.6"
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["vendored", "nightly", "ringbuf"]
|
default = ["vendored", "nightly", "ringbuf"]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate human_panic;
|
extern crate human_panic;
|
||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
@@ -2390,12 +2391,27 @@ pub unsafe extern "C" fn dc_chat_get_info_json(
|
|||||||
}
|
}
|
||||||
let ffi_context = &*context;
|
let ffi_context = &*context;
|
||||||
ffi_context
|
ffi_context
|
||||||
.with_inner(|ctx| match chat::get_info_json(ctx, chat_id) {
|
.with_inner(|ctx| {
|
||||||
Ok(s) => s.strdup(),
|
let chat = match chat::Chat::load_from_db(ctx, chat_id) {
|
||||||
Err(err) => {
|
Ok(chat) => chat,
|
||||||
error!(ctx, "get_info_json({}) returned: {}", chat_id, err);
|
Err(err) => {
|
||||||
return "".strdup();
|
error!(ctx, "dc_get_chat_info_json() failed to load chat: {}", err);
|
||||||
}
|
return "".strdup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let info = match chat.get_info(ctx) {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
ctx,
|
||||||
|
"dc_get_chat_info_json() failed to get chat info: {}", err
|
||||||
|
);
|
||||||
|
return "".strdup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
serde_json::to_string(&info)
|
||||||
|
.unwrap_or_log_default(ctx, "dc_get_chat_info_json() failed to serialise to json")
|
||||||
|
.strdup()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|_| "".strdup())
|
.unwrap_or_else(|_| "".strdup())
|
||||||
}
|
}
|
||||||
|
|||||||
169
src/chat.rs
169
src/chat.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use serde_json::json;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::blob::{BlobError, BlobObject};
|
use crate::blob::{BlobError, BlobObject};
|
||||||
use crate::chatlist::*;
|
use crate::chatlist::*;
|
||||||
@@ -240,6 +240,30 @@ impl Chat {
|
|||||||
color
|
color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a struct describing the current state of the chat.
|
||||||
|
///
|
||||||
|
/// This is somewhat experimental, even more so than the rest of
|
||||||
|
/// deltachat, and the data returned is still subject to change.
|
||||||
|
pub fn get_info(&self, context: &Context) -> Result<ChatInfo, Error> {
|
||||||
|
let draft = match get_draft(context, self.id)? {
|
||||||
|
Some(message) => message.text.unwrap_or_else(String::new),
|
||||||
|
_ => String::new(),
|
||||||
|
};
|
||||||
|
Ok(ChatInfo {
|
||||||
|
id: self.id,
|
||||||
|
type_: self.typ as u32,
|
||||||
|
name: self.name.clone(),
|
||||||
|
archived: self.archived,
|
||||||
|
param: self.param.to_string(),
|
||||||
|
gossiped_timestamp: self.get_gossiped_timestamp(context),
|
||||||
|
is_sending_locations: self.is_sending_locations,
|
||||||
|
color: self.get_color(context),
|
||||||
|
profile_image: self.get_profile_image(context).unwrap_or_else(PathBuf::new),
|
||||||
|
subtitle: self.get_subtitle(context),
|
||||||
|
draft,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the chat is archived.
|
/// Returns true if the chat is archived.
|
||||||
pub fn is_archived(&self) -> bool {
|
pub fn is_archived(&self) -> bool {
|
||||||
self.archived
|
self.archived
|
||||||
@@ -497,6 +521,68 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The current state of a chat.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct ChatInfo {
|
||||||
|
/// The chat ID.
|
||||||
|
pub id: u32,
|
||||||
|
|
||||||
|
/// The type of chat as a `u32` representation of [Chattype].
|
||||||
|
///
|
||||||
|
/// On the C API this number is one of the
|
||||||
|
/// `DC_CHAT_TYPE_UNDEFINED`, `DC_CHAT_TYPE_SINGLE`,
|
||||||
|
/// `DC_CHAT_TYPE_GROUP` or `DC_CHAT_TYPE_VERIFIED_GROUP`
|
||||||
|
/// constants.
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: u32,
|
||||||
|
|
||||||
|
/// The name of the chat.
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// Whether the chat is archived.
|
||||||
|
pub archived: bool,
|
||||||
|
|
||||||
|
/// The "params" of the chat.
|
||||||
|
///
|
||||||
|
/// This is the string-serialised version of [Params] currently.
|
||||||
|
pub param: String,
|
||||||
|
|
||||||
|
/// Something to do with gossiping and timestamps?
|
||||||
|
pub gossiped_timestamp: i64,
|
||||||
|
|
||||||
|
/// Whether this chat is currently sending location-stream messages.
|
||||||
|
pub is_sending_locations: bool,
|
||||||
|
|
||||||
|
/// Colour this chat should be represented in by the UI.
|
||||||
|
///
|
||||||
|
/// Yes, spelling colour is hard.
|
||||||
|
pub color: u32,
|
||||||
|
|
||||||
|
/// The path to the profile image.
|
||||||
|
///
|
||||||
|
/// If there is no profile image set this will be an empty string
|
||||||
|
/// currently.
|
||||||
|
pub profile_image: PathBuf,
|
||||||
|
|
||||||
|
/// Subtitle for the chat.
|
||||||
|
pub subtitle: String,
|
||||||
|
|
||||||
|
/// The draft message text.
|
||||||
|
///
|
||||||
|
/// If the chat has not draft this is an empty string.
|
||||||
|
///
|
||||||
|
/// TODO: This doesn't seem rich enough, it can not handle drafts
|
||||||
|
/// which contain non-text parts. Perhaps it should be a
|
||||||
|
/// simple `has_draft` bool instead.
|
||||||
|
pub draft: String,
|
||||||
|
// ToDo:
|
||||||
|
// - [ ] deaddrop,
|
||||||
|
// - [ ] summary,
|
||||||
|
// - [ ] lastUpdated,
|
||||||
|
// - [ ] freshMessageCounter,
|
||||||
|
// - [ ] email
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a normal chat or a group chat by a messages ID that comes typically
|
/// Create a normal chat or a group chat by a messages ID that comes typically
|
||||||
/// from the deaddrop, DC_CHAT_ID_DEADDROP (1).
|
/// from the deaddrop, DC_CHAT_ID_DEADDROP (1).
|
||||||
///
|
///
|
||||||
@@ -1990,54 +2076,6 @@ pub fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: u32) -> Resul
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_info_json(context: &Context, chat_id: u32) -> Result<String, Error> {
|
|
||||||
let chat = Chat::load_from_db(context, chat_id).unwrap();
|
|
||||||
|
|
||||||
// ToDo:
|
|
||||||
// - [x] id
|
|
||||||
// - [x] type
|
|
||||||
// - [x] name
|
|
||||||
// - [x] archived
|
|
||||||
// - [x] color
|
|
||||||
// - [x] profileImage
|
|
||||||
// - [x] subtitle
|
|
||||||
// - [x] draft,
|
|
||||||
// - [ ] deaddrop,
|
|
||||||
// - [ ] summary,
|
|
||||||
// - [ ] lastUpdated,
|
|
||||||
// - [ ] freshMessageCounter,
|
|
||||||
// - [ ] email
|
|
||||||
|
|
||||||
let profile_image = match chat.get_profile_image(context) {
|
|
||||||
Some(path) => path.into_os_string().into_string().unwrap(),
|
|
||||||
None => "".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let draft = match get_draft(context, chat_id) {
|
|
||||||
Ok(message) => match message {
|
|
||||||
Some(m) => m.text.unwrap_or_else(|| "".to_string()),
|
|
||||||
None => "".to_string(),
|
|
||||||
},
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = json!({
|
|
||||||
"id": chat.id,
|
|
||||||
"type": chat.typ as u32,
|
|
||||||
"name": chat.name,
|
|
||||||
"archived": chat.archived,
|
|
||||||
"param": chat.param.to_string(),
|
|
||||||
"gossiped_timestamp": chat.get_gossiped_timestamp(context),
|
|
||||||
"is_sending_locations": chat.is_sending_locations,
|
|
||||||
"color": chat.get_color(context),
|
|
||||||
"profile_image": profile_image,
|
|
||||||
"subtitle": chat.get_subtitle(context),
|
|
||||||
"draft": draft
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(s.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> usize {
|
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> usize {
|
||||||
context
|
context
|
||||||
.sql
|
.sql
|
||||||
@@ -2192,8 +2230,41 @@ pub fn add_info_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::contact::Contact;
|
||||||
use crate::test_utils::*;
|
use crate::test_utils::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chat_info() {
|
||||||
|
let t = dummy_context();
|
||||||
|
let bob = Contact::create(&t.ctx, "bob", "bob@example.com").unwrap();
|
||||||
|
let chat_id = create_by_contact_id(&t.ctx, bob).unwrap();
|
||||||
|
let chat = Chat::load_from_db(&t.ctx, chat_id).unwrap();
|
||||||
|
let info = chat.get_info(&t.ctx).unwrap();
|
||||||
|
|
||||||
|
// Ensure we can serialise this.
|
||||||
|
println!("{}", serde_json::to_string_pretty(&info).unwrap());
|
||||||
|
|
||||||
|
let expected = r#"
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"type": 100,
|
||||||
|
"name": "bob",
|
||||||
|
"archived": false,
|
||||||
|
"param": "",
|
||||||
|
"gossiped_timestamp": 0,
|
||||||
|
"is_sending_locations": false,
|
||||||
|
"color": 15895624,
|
||||||
|
"profile_image": "",
|
||||||
|
"subtitle": "bob@example.com",
|
||||||
|
"draft": ""
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Ensure we can deserialise this.
|
||||||
|
let loaded: ChatInfo = serde_json::from_str(expected).unwrap();
|
||||||
|
assert_eq!(info, loaded);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_draft_no_draft() {
|
fn test_get_draft_no_draft() {
|
||||||
let t = dummy_context();
|
let t = dummy_context();
|
||||||
|
|||||||
Reference in New Issue
Block a user