mirror of https://github.com/CympleTech/ESSE.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
785 lines
26 KiB
785 lines
26 KiB
use serde::{Deserialize, Serialize}; |
|
use std::path::PathBuf; |
|
use std::time::{SystemTime, UNIX_EPOCH}; |
|
use tdn::types::{ |
|
group::{EventId, GroupId}, |
|
primitive::{PeerAddr, Result}, |
|
rpc::{json, RpcParam}, |
|
}; |
|
use tdn_storage::local::{DStorage, DsValue}; |
|
|
|
use crate::storage::{ |
|
read_avatar_sync, read_file_sync, read_image_sync, read_record_sync, write_avatar_sync, |
|
write_file_sync, write_image_sync, write_record_sync, |
|
}; |
|
|
|
pub(crate) struct Friend { |
|
pub id: i64, |
|
pub gid: GroupId, |
|
pub addr: PeerAddr, |
|
pub name: String, |
|
pub remark: String, |
|
pub is_top: bool, |
|
pub is_closed: bool, |
|
pub last_message_datetime: i64, |
|
pub last_message_content: String, |
|
pub last_message_readed: bool, |
|
pub online: bool, |
|
pub is_deleted: bool, |
|
} |
|
|
|
#[derive(Clone)] |
|
pub(crate) struct Request { |
|
pub id: i64, |
|
pub gid: GroupId, |
|
pub addr: PeerAddr, |
|
pub name: String, |
|
pub remark: String, |
|
pub is_me: bool, |
|
pub is_ok: bool, |
|
pub is_over: bool, |
|
pub is_delivery: bool, |
|
pub datetime: i64, |
|
pub is_deleted: bool, |
|
} |
|
|
|
/// message type use in network. |
|
#[derive(Serialize, Deserialize, Clone)] |
|
pub(crate) enum NetworkMessage { |
|
String(String), // content |
|
Image(Vec<u8>), // image bytes. |
|
File(String, Vec<u8>), // filename, file bytes. |
|
Contact(String, GroupId, PeerAddr, Vec<u8>), // name, gid, addr, avatar bytes. |
|
Record(Vec<u8>, u32), // record audio bytes. |
|
Emoji, |
|
Phone, |
|
Video, |
|
None, |
|
} |
|
|
|
impl NetworkMessage { |
|
pub(crate) fn handle( |
|
self, |
|
is_me: bool, |
|
gid: GroupId, |
|
base: &PathBuf, |
|
db: &DStorage, |
|
fid: i64, |
|
hash: EventId, |
|
) -> Result<Message> { |
|
// handle event. |
|
let (m_type, raw) = match self { |
|
NetworkMessage::String(content) => (MessageType::String, content), |
|
NetworkMessage::Image(bytes) => { |
|
let image_name = write_image_sync(base, &gid, bytes)?; |
|
(MessageType::Image, image_name) |
|
} |
|
NetworkMessage::File(old_name, bytes) => { |
|
let filename = write_file_sync(base, &gid, &old_name, bytes)?; |
|
(MessageType::File, filename) |
|
} |
|
NetworkMessage::Contact(name, rgid, addr, avatar_bytes) => { |
|
write_avatar_sync(base, &gid, &rgid, avatar_bytes)?; |
|
let tmp_name = name.replace(";", "-;"); |
|
let contact_values = format!("{};;{};;{}", tmp_name, rgid.to_hex(), addr.to_hex()); |
|
(MessageType::Contact, contact_values) |
|
} |
|
NetworkMessage::Emoji => { |
|
// TODO |
|
(MessageType::Emoji, "".to_owned()) |
|
} |
|
NetworkMessage::Record(bytes, time) => { |
|
let record_name = write_record_sync(base, &gid, fid, time, bytes)?; |
|
(MessageType::Record, record_name) |
|
} |
|
NetworkMessage::Phone => { |
|
// TODO |
|
(MessageType::Phone, "".to_owned()) |
|
} |
|
NetworkMessage::Video => { |
|
// TODO |
|
(MessageType::Video, "".to_owned()) |
|
} |
|
NetworkMessage::None => { |
|
return Ok(Message::new_with_id( |
|
hash, |
|
fid, |
|
is_me, |
|
MessageType::String, |
|
"".to_owned(), |
|
true, |
|
)); |
|
} |
|
}; |
|
|
|
let mut msg = Message::new_with_id(hash, fid, is_me, m_type, raw, true); |
|
msg.insert(db)?; |
|
Friend::update_last_message(db, fid, &msg, false)?; |
|
Ok(msg) |
|
} |
|
|
|
pub fn from_model(base: &PathBuf, gid: &GroupId, model: Message) -> Result<NetworkMessage> { |
|
// handle message's type. |
|
match model.m_type { |
|
MessageType::String => Ok(NetworkMessage::String(model.content)), |
|
MessageType::Image => { |
|
let bytes = read_image_sync(base, gid, &model.content)?; |
|
Ok(NetworkMessage::Image(bytes)) |
|
} |
|
MessageType::File => { |
|
let bytes = read_file_sync(base, gid, &model.content)?; |
|
Ok(NetworkMessage::File(model.content, bytes)) |
|
} |
|
MessageType::Contact => { |
|
let v: Vec<&str> = model.content.split(";;").collect(); |
|
if v.len() != 3 { |
|
return Ok(NetworkMessage::None); |
|
} |
|
let cname = v[0].to_owned(); |
|
let cgid = GroupId::from_hex(v[1])?; |
|
let caddr = PeerAddr::from_hex(v[2])?; |
|
let avatar_bytes = read_avatar_sync(base, gid, &cgid)?; |
|
Ok(NetworkMessage::Contact(cname, cgid, caddr, avatar_bytes)) |
|
} |
|
MessageType::Record => { |
|
let (bytes, time) = if let Some(i) = model.content.find('-') { |
|
let time = model.content[0..i].parse().unwrap_or(0); |
|
let bytes = read_record_sync(base, gid, &model.content[i + 1..])?; |
|
(bytes, time) |
|
} else { |
|
(vec![], 0) |
|
}; |
|
Ok(NetworkMessage::Record(bytes, time)) |
|
} |
|
MessageType::Emoji => Ok(NetworkMessage::Emoji), |
|
MessageType::Phone => Ok(NetworkMessage::Phone), |
|
MessageType::Video => Ok(NetworkMessage::Video), |
|
} |
|
} |
|
} |
|
|
|
#[derive(Eq, PartialEq)] |
|
pub(crate) enum MessageType { |
|
String, |
|
Image, |
|
File, |
|
Contact, |
|
Emoji, |
|
Record, |
|
Phone, |
|
Video, |
|
} |
|
|
|
impl MessageType { |
|
pub fn to_int(&self) -> i64 { |
|
match self { |
|
MessageType::String => 0, |
|
MessageType::Image => 1, |
|
MessageType::File => 2, |
|
MessageType::Contact => 3, |
|
MessageType::Emoji => 4, |
|
MessageType::Record => 5, |
|
MessageType::Phone => 6, |
|
MessageType::Video => 7, |
|
} |
|
} |
|
|
|
pub fn from_int(i: i64) -> MessageType { |
|
match i { |
|
0 => MessageType::String, |
|
1 => MessageType::Image, |
|
2 => MessageType::File, |
|
3 => MessageType::Contact, |
|
4 => MessageType::Emoji, |
|
5 => MessageType::Record, |
|
6 => MessageType::Phone, |
|
7 => MessageType::Video, |
|
_ => MessageType::String, |
|
} |
|
} |
|
} |
|
|
|
pub(crate) struct Message { |
|
pub id: i64, |
|
pub hash: EventId, |
|
pub fid: i64, |
|
pub is_me: bool, |
|
pub m_type: MessageType, |
|
pub content: String, |
|
pub is_delivery: bool, |
|
pub datetime: i64, |
|
pub is_deleted: bool, |
|
} |
|
|
|
impl Friend { |
|
pub fn new(gid: GroupId, addr: PeerAddr, name: String, remark: String) -> Friend { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
Friend { |
|
id: 0, |
|
last_message_datetime: datetime, |
|
last_message_content: "".to_owned(), |
|
last_message_readed: true, |
|
gid, |
|
addr, |
|
name, |
|
remark, |
|
online: false, |
|
is_top: false, |
|
is_closed: false, |
|
is_deleted: false, |
|
} |
|
} |
|
|
|
pub fn contains_addr(&self, addr: &PeerAddr) -> bool { |
|
&self.addr == addr |
|
} |
|
|
|
/// here is zero-copy and unwrap is safe. checked. |
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Friend { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Friend { |
|
is_deleted, |
|
last_message_readed: v.pop().unwrap().as_bool(), |
|
last_message_content: v.pop().unwrap().as_string(), |
|
last_message_datetime: v.pop().unwrap().as_i64(), |
|
is_closed: v.pop().unwrap().as_bool(), |
|
is_top: v.pop().unwrap().as_bool(), |
|
remark: v.pop().unwrap().as_string(), |
|
name: v.pop().unwrap().as_string(), |
|
addr: PeerAddr::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerAddr::default()), |
|
gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()), |
|
id: v.pop().unwrap().as_i64(), |
|
online: false, |
|
} |
|
} |
|
|
|
pub fn from_request(db: &DStorage, request: Request) -> Result<Friend> { |
|
if let Some(mut friend) = Friend::get_it(&db, &request.gid)? { |
|
friend.name = request.name; |
|
friend.addr = request.addr; |
|
friend.is_closed = false; |
|
friend.remote_update(&db)?; |
|
Ok(friend) |
|
} else { |
|
let mut friend = request.to_friend(); |
|
friend.insert(&db)?; |
|
Ok(friend) |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
if self.is_top { "1" } else { "0" }, |
|
if self.is_closed { "1" } else { "0" }, |
|
self.last_message_datetime, |
|
self.last_message_content, |
|
self.last_message_readed, |
|
if self.online { "1" } else { "0" }, |
|
]) |
|
} |
|
|
|
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> { |
|
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends WHERE gid = '{}' and is_deleted = false", gid.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), false))); // safe unwrap() |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_it(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> { |
|
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted FROM friends WHERE gid = '{}'", gid.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap() |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Friend>> { |
|
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted FROM friends WHERE id = {}", id); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap() |
|
} |
|
Ok(None) |
|
} |
|
|
|
/// use in rpc when load account friends. |
|
pub fn all(db: &DStorage) -> Result<Vec<Friend>> { |
|
let matrix = db.query("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends where is_deleted = false ORDER BY last_message_datetime DESC")?; |
|
let mut friends = vec![]; |
|
for values in matrix { |
|
friends.push(Friend::from_values(values, false)); |
|
} |
|
Ok(friends) |
|
} |
|
|
|
/// use in rpc when load account friends. |
|
pub fn all_ok(db: &DStorage) -> Result<Vec<Friend>> { |
|
let matrix = db.query("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends where is_closed = false ORDER BY last_message_datetime DESC")?; |
|
let mut friends = vec![]; |
|
for values in matrix { |
|
friends.push(Friend::from_values(values, false)); |
|
} |
|
Ok(friends) |
|
} |
|
|
|
/// use in layer load friends ids. |
|
pub fn all_id(db: &DStorage) -> Result<Vec<(GroupId, i64)>> { |
|
let matrix = |
|
db.query("SELECT id, gid FROM friends where is_closed = false ORDER BY id DESC")?; |
|
let mut friends = vec![]; |
|
for mut values in matrix { |
|
friends.push(( |
|
GroupId::from_hex(values.pop().unwrap().as_str()).unwrap_or(GroupId::default()), |
|
values.pop().unwrap().as_i64(), |
|
)); |
|
} |
|
Ok(friends) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO friends (gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted) VALUES ('{}', '{}', '{}', '{}', {}, {}, {}, '{}', {}, false)", |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
if self.is_top { 1 } else { 0 }, |
|
if self.is_closed { 1 } else { 0 }, |
|
self.last_message_datetime, |
|
self.last_message_content, |
|
if self.last_message_readed { 1 } else { 0 } |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
|
|
pub fn update(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!("UPDATE friends SET addr = '{}', name = '{}', remark = '{}', is_top = {}, is_closed = {}, last_message_datetime = {}, last_message_content = '{}', last_message_readed = {}, is_deleted = {} WHERE id = {}", |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
if self.is_top { 1 } else { 0 }, |
|
if self.is_closed { 1 } else { 0 }, |
|
self.last_message_datetime, |
|
self.last_message_content, |
|
if self.last_message_readed { 1 } else { 0 }, |
|
if self.is_deleted { 1 } else { 0 }, |
|
self.id |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn me_update(&mut self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE friends SET remark='{}', is_top={} WHERE id = {}", |
|
self.remark, self.is_top, self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn addr_update(db: &DStorage, id: i64, addr: &PeerAddr) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE friends SET addr='{}' WHERE id = {}", |
|
addr.to_hex(), |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn remote_update(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE friends SET addr='{}', name='{}', is_closed = false, is_deleted = false WHERE id = {}", |
|
self.addr.to_hex(), |
|
self.name, |
|
self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn update_last_message(db: &DStorage, id: i64, msg: &Message, read: bool) -> Result<usize> { |
|
let sql = format!("UPDATE friends SET last_message_datetime={}, last_message_content='{}', last_message_readed={} WHERE id = {}", |
|
msg.datetime, |
|
msg.content, |
|
if read { 1 } else { 0 }, |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn readed(db: &DStorage, id: i64) -> Result<usize> { |
|
let sql = format!("UPDATE friends SET last_message_readed=1 WHERE id = {}", id); |
|
db.update(&sql) |
|
} |
|
|
|
/// used in rpc, when what to delete a friend. |
|
pub fn close(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", self.id); |
|
db.update(&sql) |
|
} |
|
|
|
/// used in rpc, when what to delete a friend. |
|
pub fn delete(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE friends SET is_deleted = true, is_closed = true WHERE id = {}", |
|
self.id |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn is_friend(db: &DStorage, gid: &GroupId) -> Result<bool> { |
|
let sql = format!( |
|
"SELECT id FROM friends WHERE is_closed = false and gid = '{}'", |
|
gid.to_hex() |
|
); |
|
let matrix = db.query(&sql)?; |
|
Ok(matrix.len() > 0) |
|
} |
|
|
|
/// used in layers, when receive remote had closed. |
|
pub fn id_close(db: &DStorage, id: i64) -> Result<usize> { |
|
let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", id); |
|
db.update(&sql) |
|
} |
|
} |
|
|
|
impl Request { |
|
pub fn new( |
|
gid: GroupId, |
|
addr: PeerAddr, |
|
name: String, |
|
remark: String, |
|
is_me: bool, |
|
is_delivery: bool, |
|
) -> Request { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
Request { |
|
id: 0, |
|
gid, |
|
addr, |
|
name, |
|
remark, |
|
is_me, |
|
is_ok: false, |
|
is_over: false, |
|
is_delivery, |
|
datetime: datetime, |
|
is_deleted: false, |
|
} |
|
} |
|
|
|
pub fn to_friend(self) -> Friend { |
|
let mut friend = Friend::new(self.gid, self.addr, self.name, self.remark); |
|
friend.is_top = true; // default set to top page. |
|
friend |
|
} |
|
|
|
/// here is zero-copy and unwrap is safe. checked. |
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Request { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Request { |
|
is_deleted, |
|
datetime: v.pop().unwrap().as_i64(), |
|
is_delivery: v.pop().unwrap().as_bool(), |
|
is_over: v.pop().unwrap().as_bool(), |
|
is_ok: v.pop().unwrap().as_bool(), |
|
is_me: v.pop().unwrap().as_bool(), |
|
remark: v.pop().unwrap().as_string(), |
|
name: v.pop().unwrap().as_string(), |
|
addr: PeerAddr::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerAddr::default()), |
|
gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
self.is_me, |
|
self.is_ok, |
|
self.is_over, |
|
self.is_delivery, |
|
self.datetime, |
|
]) |
|
} |
|
|
|
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<Request>> { |
|
let sql = format!("SELECT id, gid, addr, name, remark, is_me, is_ok, is_over, is_delivery, datetime FROM requests WHERE gid = '{}' AND is_deleted = false", gid.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Request::from_values(values, false))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Request>> { |
|
let sql = format!("SELECT id, gid, addr, name, remark, is_me, is_ok, is_over, is_delivery, datetime, is_deleted FROM requests WHERE id = {}", id); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Request::from_values(values, true))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn all(db: &DStorage) -> Result<Vec<Request>> { |
|
let matrix = db.query("SELECT id, gid, addr, name, remark, is_me, is_ok, is_over, is_delivery, datetime FROM requests WHERE is_deleted = false ORDER BY id DESC")?; |
|
let mut requests = vec![]; |
|
for values in matrix { |
|
requests.push(Request::from_values(values, false)); |
|
} |
|
Ok(requests) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO requests (gid, addr, name, remark, is_me, is_ok, is_over, is_delivery, datetime, is_deleted) VALUES ('{}', '{}', '{}', '{}', {}, {}, {}, {}, {}, false)", |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
if self.is_me { 1 } else { 0 }, |
|
if self.is_ok { 1 } else { 0 }, |
|
if self.is_over { 1 } else { 0 }, |
|
if self.is_delivery { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
|
|
pub fn update(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!("UPDATE requests SET gid='{}', addr='{}', name='{}', remark='{}', is_me={}, is_ok={}, is_over={}, is_delivery={}, datetime={}, is_deleted = {} WHERE id = {}", |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
if self.is_me { 1 } else { 0 }, |
|
if self.is_ok { 1 } else { 0 }, |
|
if self.is_over { 1 } else { 0 }, |
|
if self.is_delivery { 1 } else { 0 }, |
|
self.datetime, |
|
if self.is_deleted { 1 } else { 0 }, |
|
self.id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn delivery(db: &DStorage, id: i64, is_delivery: bool) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE requests SET is_delivery={} WHERE id = {}", |
|
if is_delivery { 1 } else { 0 }, |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn delete(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE requests SET is_deleted = true WHERE id = {}", |
|
self.id |
|
); |
|
db.delete(&sql) |
|
} |
|
} |
|
|
|
impl Message { |
|
pub fn new( |
|
gid: &GroupId, |
|
fid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
is_delivery: bool, |
|
) -> Message { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
let mut bytes = [0u8; 32]; |
|
bytes[0..8].copy_from_slice(&gid.0[0..8]); |
|
bytes[8..16].copy_from_slice(&(fid as u64).to_le_bytes()); // 8-bytes. |
|
bytes[16..24].copy_from_slice(&(datetime as u64).to_le_bytes()); // 8-bytes. |
|
let content_bytes = content.as_bytes(); |
|
if content_bytes.len() >= 8 { |
|
bytes[24..32].copy_from_slice(&content_bytes[0..8]); |
|
} else { |
|
bytes[24..(24 + content_bytes.len())].copy_from_slice(&content_bytes); |
|
} |
|
|
|
Message { |
|
id: 0, |
|
hash: EventId(bytes), |
|
is_deleted: false, |
|
fid, |
|
is_me, |
|
m_type, |
|
content, |
|
is_delivery, |
|
datetime, |
|
} |
|
} |
|
|
|
pub fn new_with_id( |
|
hash: EventId, |
|
fid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
is_delivery: bool, |
|
) -> Message { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
Message { |
|
id: 0, |
|
is_deleted: false, |
|
hash, |
|
fid, |
|
is_me, |
|
m_type, |
|
content, |
|
is_delivery, |
|
datetime, |
|
} |
|
} |
|
|
|
/// here is zero-copy and unwrap is safe. checked. |
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Message { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Message { |
|
is_deleted, |
|
datetime: v.pop().unwrap().as_i64(), |
|
is_delivery: v.pop().unwrap().as_bool(), |
|
content: v.pop().unwrap().as_string(), |
|
m_type: MessageType::from_int(v.pop().unwrap().as_i64()), |
|
is_me: v.pop().unwrap().as_bool(), |
|
fid: v.pop().unwrap().as_i64(), |
|
hash: EventId::from_hex(v.pop().unwrap().as_str()).unwrap_or(EventId::default()), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.hash.to_hex(), |
|
self.fid, |
|
self.is_me, |
|
self.m_type.to_int(), |
|
self.content, |
|
self.is_delivery, |
|
self.datetime, |
|
]) |
|
} |
|
|
|
pub fn get(db: &DStorage, fid: &i64) -> Result<Vec<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime FROM messages WHERE fid = {} and is_deleted = false ORDER BY id DESC", fid); |
|
let matrix = db.query(&sql)?; |
|
let mut messages = vec![]; |
|
for values in matrix { |
|
messages.push(Message::from_values(values, false)); |
|
} |
|
Ok(messages) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted FROM messages WHERE id = {}", id); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Message::from_values(values, true))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_it(db: &DStorage, hash: &EventId) -> Result<Option<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted FROM messages WHERE hash = {}", hash.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Message::from_values(values, true))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!( |
|
"INSERT INTO messages (hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted) VALUES ('{}',{},{},{},'{}',{},{},false)", |
|
self.hash.to_hex(), |
|
self.fid, |
|
if self.is_me { 1 } else { 0 }, |
|
self.m_type.to_int(), |
|
self.content, |
|
if self.is_delivery { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
self.id = db.insert(&sql)?; |
|
Ok(()) |
|
} |
|
|
|
pub fn delivery(db: &DStorage, id: i64, is_delivery: bool) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE messages SET is_delivery={} WHERE id = {}", |
|
if is_delivery { 1 } else { 0 }, |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn delete(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE messages SET is_deleted = true WHERE id = {}", |
|
self.id |
|
); |
|
db.delete(&sql) |
|
} |
|
|
|
pub fn exist(db: &DStorage, hash: &EventId) -> Result<bool> { |
|
let sql = format!("SELECT id FROM messages WHERE hash = '{}'", hash.to_hex()); |
|
let matrix = db.query(&sql)?; |
|
Ok(matrix.len() > 0) |
|
} |
|
}
|
|
|