Private
Public Access
1
0

cargo fmt

This commit is contained in:
2025-08-24 16:20:14 -07:00
parent 28738a1e92
commit f239d1de19
4 changed files with 183 additions and 47 deletions

View File

@@ -27,7 +27,10 @@ pub struct XpcAgent {
impl XpcAgent { impl XpcAgent {
pub fn new(event_sink: mpsc::Sender<Event>, signal_receiver: mpsc::Receiver<Signal>) -> Self { pub fn new(event_sink: mpsc::Sender<Event>, signal_receiver: mpsc::Receiver<Signal>) -> Self {
Self { event_sink, signal_receiver: Arc::new(Mutex::new(Some(signal_receiver))) } Self {
event_sink,
signal_receiver: Arc::new(Mutex::new(Some(signal_receiver))),
}
} }
pub async fn run(self) { pub async fn run(self) {
@@ -179,4 +182,3 @@ impl XpcAgent {
rx.await.map_err(|_| "Failed to receive reply".into()) rx.await.map_err(|_| "Failed to receive reply".into())
} }
} }

View File

@@ -10,7 +10,7 @@ use super::util::*;
pub async fn dispatch( pub async fn dispatch(
agent: &XpcAgent, agent: &XpcAgent,
subscribers: &std::sync::Mutex<Vec<XpcConn>>, subscribers: &std::sync::Mutex<Vec<XpcConn>>,
current_client: xpc_sys::xpc_connection_t, current_client: xpc_sys::xpc_connection_t,
root: &HashMap<CString, Message>, root: &HashMap<CString, Message>,
) -> Message { ) -> Message {
@@ -18,7 +18,12 @@ pub async fn dispatch(
let method = match dict_get_str(root, "method").or_else(|| dict_get_str(root, "type")) { let method = match dict_get_str(root, "method").or_else(|| dict_get_str(root, "type")) {
Some(m) => m, Some(m) => m,
None => return attach_request_id(make_error_reply("InvalidRequest", "Missing method/type"), request_id), None => {
return attach_request_id(
make_error_reply("InvalidRequest", "Missing method/type"),
request_id,
)
}
}; };
let _arguments = get_dictionary_field(root, "arguments"); let _arguments = get_dictionary_field(root, "arguments");
@@ -40,19 +45,38 @@ pub async fn dispatch(
let mut limit: i32 = 100; let mut limit: i32 = 100;
let mut offset: i32 = 0; let mut offset: i32 = 0;
if let Some(args) = get_dictionary_field(root, "arguments") { if let Some(args) = get_dictionary_field(root, "arguments") {
if let Some(v) = dict_get_i64_from_str(args, "limit") { limit = v as i32; } if let Some(v) = dict_get_i64_from_str(args, "limit") {
if let Some(v) = dict_get_i64_from_str(args, "offset") { offset = v as i32; } limit = v as i32;
}
if let Some(v) = dict_get_i64_from_str(args, "offset") {
offset = v as i32;
}
} }
match agent.send_event(|r| Event::GetAllConversations(limit, offset, r)).await { match agent
.send_event(|r| Event::GetAllConversations(limit, offset, r))
.await
{
Ok(conversations) => { Ok(conversations) => {
let mut items: Vec<Message> = Vec::with_capacity(conversations.len()); let mut items: Vec<Message> = Vec::with_capacity(conversations.len());
for conv in conversations { for conv in conversations {
let mut m: XpcMap = HashMap::new(); let mut m: XpcMap = HashMap::new();
dict_put_str(&mut m, "guid", &conv.guid); dict_put_str(&mut m, "guid", &conv.guid);
dict_put_str(&mut m, "display_name", &conv.display_name.unwrap_or_default()); dict_put_str(
&mut m,
"display_name",
&conv.display_name.unwrap_or_default(),
);
dict_put_i64_as_str(&mut m, "unread_count", conv.unread_count as i64); dict_put_i64_as_str(&mut m, "unread_count", conv.unread_count as i64);
dict_put_str(&mut m, "last_message_preview", &conv.last_message_preview.unwrap_or_default()); dict_put_str(
let participant_names: Vec<String> = conv.participants.into_iter().map(|p| p.display_name()).collect(); &mut m,
"last_message_preview",
&conv.last_message_preview.unwrap_or_default(),
);
let participant_names: Vec<String> = conv
.participants
.into_iter()
.map(|p| p.display_name())
.collect();
m.insert(cstr("participants"), array_from_strs(participant_names)); m.insert(cstr("participants"), array_from_strs(participant_names));
dict_put_i64_as_str(&mut m, "date", conv.date.and_utc().timestamp()); dict_put_i64_as_str(&mut m, "date", conv.date.and_utc().timestamp());
items.push(Message::Dictionary(m)); items.push(Message::Dictionary(m));
@@ -76,11 +100,16 @@ pub async fn dispatch(
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
}, },
"SyncConversation" => { "SyncConversation" => {
let conversation_id = match get_dictionary_field(root, "arguments").and_then(|m| dict_get_str(m, "conversation_id")) { let conversation_id = match get_dictionary_field(root, "arguments")
.and_then(|m| dict_get_str(m, "conversation_id"))
{
Some(id) => id, Some(id) => id,
None => return make_error_reply("InvalidRequest", "Missing conversation_id"), None => return make_error_reply("InvalidRequest", "Missing conversation_id"),
}; };
match agent.send_event(|r| Event::SyncConversation(conversation_id, r)).await { match agent
.send_event(|r| Event::SyncConversation(conversation_id, r))
.await
{
Ok(()) => make_ok_reply(), Ok(()) => make_ok_reply(),
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
} }
@@ -88,11 +117,16 @@ pub async fn dispatch(
// Mark as read // Mark as read
"MarkConversationAsRead" => { "MarkConversationAsRead" => {
let conversation_id = match get_dictionary_field(root, "arguments").and_then(|m| dict_get_str(m, "conversation_id")) { let conversation_id = match get_dictionary_field(root, "arguments")
.and_then(|m| dict_get_str(m, "conversation_id"))
{
Some(id) => id, Some(id) => id,
None => return make_error_reply("InvalidRequest", "Missing conversation_id"), None => return make_error_reply("InvalidRequest", "Missing conversation_id"),
}; };
match agent.send_event(|r| Event::MarkConversationAsRead(conversation_id, r)).await { match agent
.send_event(|r| Event::MarkConversationAsRead(conversation_id, r))
.await
{
Ok(()) => make_ok_reply(), Ok(()) => make_ok_reply(),
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
} }
@@ -100,10 +134,19 @@ pub async fn dispatch(
// GetMessages // GetMessages
"GetMessages" => { "GetMessages" => {
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
let conversation_id = match dict_get_str(args, "conversation_id") { Some(id) => id, None => return make_error_reply("InvalidRequest", "Missing conversation_id") }; Some(a) => a,
None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let conversation_id = match dict_get_str(args, "conversation_id") {
Some(id) => id,
None => return make_error_reply("InvalidRequest", "Missing conversation_id"),
};
let last_message_id = dict_get_str(args, "last_message_id"); let last_message_id = dict_get_str(args, "last_message_id");
match agent.send_event(|r| Event::GetMessages(conversation_id, last_message_id, r)).await { match agent
.send_event(|r| Event::GetMessages(conversation_id, last_message_id, r))
.await
{
Ok(messages) => { Ok(messages) => {
let mut items: Vec<Message> = Vec::with_capacity(messages.len()); let mut items: Vec<Message> = Vec::with_capacity(messages.len());
for msg in messages { for msg in messages {
@@ -131,14 +174,29 @@ pub async fn dispatch(
// SendMessage // SendMessage
"SendMessage" => { "SendMessage" => {
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
let conversation_id = match dict_get_str(args, "conversation_id") { Some(v) => v, None => return make_error_reply("InvalidRequest", "Missing conversation_id") }; Some(a) => a,
None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let conversation_id = match dict_get_str(args, "conversation_id") {
Some(v) => v,
None => return make_error_reply("InvalidRequest", "Missing conversation_id"),
};
let text = dict_get_str(args, "text").unwrap_or_default(); let text = dict_get_str(args, "text").unwrap_or_default();
let attachment_guids: Vec<String> = match args.get(&cstr("attachment_guids")) { let attachment_guids: Vec<String> = match args.get(&cstr("attachment_guids")) {
Some(Message::Array(arr)) => arr.iter().filter_map(|m| match m { Message::String(s) => Some(s.to_string_lossy().into_owned()), _ => None }).collect(), Some(Message::Array(arr)) => arr
.iter()
.filter_map(|m| match m {
Message::String(s) => Some(s.to_string_lossy().into_owned()),
_ => None,
})
.collect(),
_ => Vec::new(), _ => Vec::new(),
}; };
match agent.send_event(|r| Event::SendMessage(conversation_id, text, attachment_guids, r)).await { match agent
.send_event(|r| Event::SendMessage(conversation_id, text, attachment_guids, r))
.await
{
Ok(uuid) => { Ok(uuid) => {
let mut reply: XpcMap = HashMap::new(); let mut reply: XpcMap = HashMap::new();
dict_put_str(&mut reply, "type", "SendMessageResponse"); dict_put_str(&mut reply, "type", "SendMessageResponse");
@@ -151,16 +209,41 @@ pub async fn dispatch(
// GetAttachmentInfo // GetAttachmentInfo
"GetAttachmentInfo" => { "GetAttachmentInfo" => {
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
let attachment_id = match dict_get_str(args, "attachment_id") { Some(v) => v, None => return make_error_reply("InvalidRequest", "Missing attachment_id") }; Some(a) => a,
match agent.send_event(|r| Event::GetAttachment(attachment_id, r)).await { None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let attachment_id = match dict_get_str(args, "attachment_id") {
Some(v) => v,
None => return make_error_reply("InvalidRequest", "Missing attachment_id"),
};
match agent
.send_event(|r| Event::GetAttachment(attachment_id, r))
.await
{
Ok(attachment) => { Ok(attachment) => {
let mut reply: XpcMap = HashMap::new(); let mut reply: XpcMap = HashMap::new();
dict_put_str(&mut reply, "type", "GetAttachmentInfoResponse"); dict_put_str(&mut reply, "type", "GetAttachmentInfoResponse");
dict_put_str(&mut reply, "path", &attachment.get_path_for_preview(false).to_string_lossy()); dict_put_str(
dict_put_str(&mut reply, "preview_path", &attachment.get_path_for_preview(true).to_string_lossy()); &mut reply,
dict_put_str(&mut reply, "downloaded", &attachment.is_downloaded(false).to_string()); "path",
dict_put_str(&mut reply, "preview_downloaded", &attachment.is_downloaded(true).to_string()); &attachment.get_path_for_preview(false).to_string_lossy(),
);
dict_put_str(
&mut reply,
"preview_path",
&attachment.get_path_for_preview(true).to_string_lossy(),
);
dict_put_str(
&mut reply,
"downloaded",
&attachment.is_downloaded(false).to_string(),
);
dict_put_str(
&mut reply,
"preview_downloaded",
&attachment.is_downloaded(true).to_string(),
);
Message::Dictionary(reply) Message::Dictionary(reply)
} }
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
@@ -169,10 +252,21 @@ pub async fn dispatch(
// DownloadAttachment // DownloadAttachment
"DownloadAttachment" => { "DownloadAttachment" => {
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
let attachment_id = match dict_get_str(args, "attachment_id") { Some(v) => v, None => return make_error_reply("InvalidRequest", "Missing attachment_id") }; Some(a) => a,
let preview = dict_get_str(args, "preview").map(|s| s == "true").unwrap_or(false); None => return make_error_reply("InvalidRequest", "Missing arguments"),
match agent.send_event(|r| Event::DownloadAttachment(attachment_id, preview, r)).await { };
let attachment_id = match dict_get_str(args, "attachment_id") {
Some(v) => v,
None => return make_error_reply("InvalidRequest", "Missing attachment_id"),
};
let preview = dict_get_str(args, "preview")
.map(|s| s == "true")
.unwrap_or(false);
match agent
.send_event(|r| Event::DownloadAttachment(attachment_id, preview, r))
.await
{
Ok(()) => make_ok_reply(), Ok(()) => make_ok_reply(),
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
} }
@@ -181,9 +275,18 @@ pub async fn dispatch(
// UploadAttachment // UploadAttachment
"UploadAttachment" => { "UploadAttachment" => {
use std::path::PathBuf; use std::path::PathBuf;
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
let path = match dict_get_str(args, "path") { Some(v) => v, None => return make_error_reply("InvalidRequest", "Missing path") }; Some(a) => a,
match agent.send_event(|r| Event::UploadAttachment(PathBuf::from(path), r)).await { None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let path = match dict_get_str(args, "path") {
Some(v) => v,
None => return make_error_reply("InvalidRequest", "Missing path"),
};
match agent
.send_event(|r| Event::UploadAttachment(PathBuf::from(path), r))
.await
{
Ok(upload_guid) => { Ok(upload_guid) => {
let mut reply: XpcMap = HashMap::new(); let mut reply: XpcMap = HashMap::new();
dict_put_str(&mut reply, "type", "UploadAttachmentResponse"); dict_put_str(&mut reply, "type", "UploadAttachmentResponse");
@@ -199,18 +302,36 @@ pub async fn dispatch(
Ok(settings) => { Ok(settings) => {
let mut reply: XpcMap = HashMap::new(); let mut reply: XpcMap = HashMap::new();
dict_put_str(&mut reply, "type", "GetAllSettingsResponse"); dict_put_str(&mut reply, "type", "GetAllSettingsResponse");
dict_put_str(&mut reply, "server_url", &settings.server_url.unwrap_or_default()); dict_put_str(
dict_put_str(&mut reply, "username", &settings.username.unwrap_or_default()); &mut reply,
"server_url",
&settings.server_url.unwrap_or_default(),
);
dict_put_str(
&mut reply,
"username",
&settings.username.unwrap_or_default(),
);
Message::Dictionary(reply) Message::Dictionary(reply)
} }
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
}, },
"UpdateSettings" => { "UpdateSettings" => {
let args = match get_dictionary_field(root, "arguments") { Some(a) => a, None => return make_error_reply("InvalidRequest", "Missing arguments") }; let args = match get_dictionary_field(root, "arguments") {
Some(a) => a,
None => return make_error_reply("InvalidRequest", "Missing arguments"),
};
let server_url = dict_get_str(args, "server_url"); let server_url = dict_get_str(args, "server_url");
let username = dict_get_str(args, "username"); let username = dict_get_str(args, "username");
let settings = Settings { server_url, username, token: None }; let settings = Settings {
match agent.send_event(|r| Event::UpdateSettings(settings, r)).await { server_url,
username,
token: None,
};
match agent
.send_event(|r| Event::UpdateSettings(settings, r))
.await
{
Ok(()) => make_ok_reply(), Ok(()) => make_ok_reply(),
Err(e) => make_error_reply("DaemonError", &format!("{}", e)), Err(e) => make_error_reply("DaemonError", &format!("{}", e)),
} }
@@ -224,7 +345,7 @@ pub async fn dispatch(
log::trace!(target: LOG_TARGET, "Client subscribed to signals (total subscribers: {})", list.len()); log::trace!(target: LOG_TARGET, "Client subscribed to signals (total subscribers: {})", list.len());
} }
make_ok_reply() make_ok_reply()
}, }
// Unknown method fallback // Unknown method fallback
other => make_error_reply("UnknownMethod", other), other => make_error_reply("UnknownMethod", other),

View File

@@ -5,19 +5,27 @@ use xpc_connection::Message;
pub type XpcMap = HashMap<CString, Message>; pub type XpcMap = HashMap<CString, Message>;
pub fn cstr(s: &str) -> CString { CString::new(s).unwrap_or_else(|_| CString::new("").unwrap()) } pub fn cstr(s: &str) -> CString {
CString::new(s).unwrap_or_else(|_| CString::new("").unwrap())
}
pub fn get_dictionary_field<'a>( pub fn get_dictionary_field<'a>(
map: &'a HashMap<CString, Message>, map: &'a HashMap<CString, Message>,
key: &str, key: &str,
) -> Option<&'a HashMap<CString, Message>> { ) -> Option<&'a HashMap<CString, Message>> {
let k = CString::new(key).ok()?; let k = CString::new(key).ok()?;
map.get(&k).and_then(|v| match v { Message::Dictionary(d) => Some(d), _ => None }) map.get(&k).and_then(|v| match v {
Message::Dictionary(d) => Some(d),
_ => None,
})
} }
pub fn dict_get_str(map: &HashMap<CString, Message>, key: &str) -> Option<String> { pub fn dict_get_str(map: &HashMap<CString, Message>, key: &str) -> Option<String> {
let k = CString::new(key).ok()?; let k = CString::new(key).ok()?;
match map.get(&k) { Some(Message::String(v)) => Some(v.to_string_lossy().into_owned()), _ => None } match map.get(&k) {
Some(Message::String(v)) => Some(v.to_string_lossy().into_owned()),
_ => None,
}
} }
pub fn dict_get_i64_from_str(map: &HashMap<CString, Message>, key: &str) -> Option<i64> { pub fn dict_get_i64_from_str(map: &HashMap<CString, Message>, key: &str) -> Option<i64> {
@@ -28,10 +36,15 @@ pub fn dict_put_str(map: &mut XpcMap, key: &str, value: impl AsRef<str>) {
map.insert(cstr(key), Message::String(cstr(value.as_ref()))); map.insert(cstr(key), Message::String(cstr(value.as_ref())));
} }
pub fn dict_put_i64_as_str(map: &mut XpcMap, key: &str, value: i64) { dict_put_str(map, key, value.to_string()); } pub fn dict_put_i64_as_str(map: &mut XpcMap, key: &str, value: i64) {
dict_put_str(map, key, value.to_string());
}
pub fn array_from_strs(values: impl IntoIterator<Item = String>) -> Message { pub fn array_from_strs(values: impl IntoIterator<Item = String>) -> Message {
let arr = values.into_iter().map(|s| Message::String(cstr(&s))).collect(); let arr = values
.into_iter()
.map(|s| Message::String(cstr(&s)))
.collect();
Message::Array(arr) Message::Array(arr)
} }

View File

@@ -495,7 +495,7 @@ impl DaemonInterface for XpcDaemonInterface {
} }
Message::Error(xpc_connection::MessageError::ConnectionInvalid) => { Message::Error(xpc_connection::MessageError::ConnectionInvalid) => {
eprintln!("[kpcli] XPC connection invalid"); eprintln!("[kpcli] XPC connection invalid");
break break;
} }
other => { other => {
eprintln!("[kpcli] Unexpected XPC message: {:?}", other); eprintln!("[kpcli] Unexpected XPC message: {:?}", other);