Private
Public Access
1
0

xpc: kpcli: clean up client interface

This commit is contained in:
2025-08-23 19:41:12 -07:00
parent 8ff95f4bf9
commit 885c96172d
2 changed files with 46 additions and 38 deletions

View File

@@ -9,7 +9,8 @@ mod dbus;
#[cfg(target_os = "macos")]
mod xpc;
#[async_trait]
#[cfg_attr(target_os = "macos", async_trait(?Send))]
#[cfg_attr(not(target_os = "macos"), async_trait)]
pub trait DaemonInterface {
async fn print_version(&mut self) -> Result<()>;
async fn print_conversations(&mut self) -> Result<()>;
@@ -40,7 +41,8 @@ impl StubDaemonInterface {
}
}
#[async_trait]
#[cfg_attr(target_os = "macos", async_trait(?Send))]
#[cfg_attr(not(target_os = "macos"), async_trait)]
impl DaemonInterface for StubDaemonInterface {
async fn print_version(&mut self) -> Result<()> {
Err(anyhow::anyhow!(

View File

@@ -111,49 +111,55 @@ impl XpcDaemonInterface {
pub fn new() -> Result<Self> {
Ok(Self)
}
fn build_service_name() -> Result<CString> {
let service_name = SERVICE_NAME.trim_end_matches('\0');
Ok(CString::new(service_name)?)
}
fn build_request(method: &str, args: Option<HashMap<CString, Message>>) -> HashMap<CString, Message> {
let mut request = HashMap::new();
request.insert(CString::new("method").unwrap(), Message::String(CString::new(method).unwrap()));
if let Some(arguments) = args {
request.insert(CString::new("arguments").unwrap(), Message::Dictionary(arguments));
}
request
}
async fn call_method(&self, client: &mut XPCClient, method: &str, args: Option<HashMap<CString, Message>>) -> anyhow::Result<HashMap<CString, Message>> {
let request = Self::build_request(method, args);
client.send_message(Message::Dictionary(request));
match client.next().await {
Some(Message::Dictionary(map)) => Ok(map),
Some(other) => Err(anyhow::anyhow!("Unexpected XPC reply: {:?}", other)),
None => Err(anyhow::anyhow!("No reply received from XPC daemon")),
}
}
fn get_string<'a>(map: &'a HashMap<CString, Message>, key: &str) -> Option<&'a CStr> {
map.get(&CString::new(key).ok()?).and_then(|v| match v { Message::String(s) => Some(s.as_c_str()), _ => None })
}
}
#[async_trait]
#[async_trait(?Send)]
impl DaemonInterface for XpcDaemonInterface {
async fn print_version(&mut self) -> Result<()> {
// Build service name CString (trim trailing NUL from const)
let service_name = SERVICE_NAME.trim_end_matches('\0');
let mach_port_name = CString::new(service_name)?;
// Open an XPC connection to the daemon service
// Build service name and connect
let mach_port_name = Self::build_service_name()?;
let mut client = XPCClient::connect(&mach_port_name);
// Send a GetVersion request as a dictionary message: { method: "GetVersion" }
{
let mut request = HashMap::new();
request.insert(
CString::new("method").unwrap(),
Message::String(CString::new(GET_VERSION_METHOD).unwrap()),
);
client.send_message(Message::Dictionary(request));
}
// Await a single reply and print the version
match client.next().await {
Some(Message::Dictionary(map)) => {
if let Some(Message::String(ver)) = map.get(&CString::new("version").unwrap()) {
// Call generic method and parse reply
let map = self.call_method(&mut client, GET_VERSION_METHOD, None).await?;
if let Some(ver) = Self::get_string(&map, "version") {
println!("Server version: {}", ver.to_string_lossy());
} else if let Some(Message::String(ty)) = map.get(&CString::new("type").unwrap())
{
println!("XPC replied with type: {}", ty.to_string_lossy());
} else {
eprintln!("Unexpected XPC reply payload for GetVersion");
}
}
Some(other) => {
eprintln!("Unexpected XPC reply: {:?}", other);
}
None => {
eprintln!("No reply received from XPC daemon");
}
}
Ok(())
} else if let Some(ty) = Self::get_string(&map, "type") {
println!("XPC replied with type: {}", ty.to_string_lossy());
Ok(())
} else {
Err(anyhow::anyhow!("Unexpected XPC reply payload for GetVersion"))
}
}
// Remaining methods unimplemented on macOS