diff options
| -rw-r--r-- | niri-ipc/src/lib.rs | 22 | ||||
| -rw-r--r-- | src/ipc/client.rs | 27 | ||||
| -rw-r--r-- | src/ipc/server.rs | 24 |
3 files changed, 45 insertions, 28 deletions
diff --git a/niri-ipc/src/lib.rs b/niri-ipc/src/lib.rs index 5131388f..622435f4 100644 --- a/niri-ipc/src/lib.rs +++ b/niri-ipc/src/lib.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; pub const SOCKET_PATH_ENV: &str = "NIRI_SOCKET"; /// Request from client to niri. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum Request { /// Request information about connected outputs. Outputs, @@ -18,9 +18,21 @@ pub enum Request { Action(Action), } -/// Response from niri to client. -#[derive(Debug, Serialize, Deserialize)] +/// Reply from niri to client. +/// +/// Every request gets one reply. +/// +/// * If an error had occurred, it will be an `Reply::Err`. +/// * If the request does not need any particular response, it will be +/// `Reply::Ok(Response::Handled)`. Kind of like an `Ok(())`. +/// * Otherwise, it will be `Reply::Ok(response)` with one of the other [`Response`] variants. +pub type Reply = Result<Response, String>; + +/// Successful response from niri to client. +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum Response { + /// A request that does not need a response was handled successfully. + Handled, /// Information about connected outputs. /// /// Map from connector name to output info. @@ -30,7 +42,7 @@ pub enum Response { /// Actions that niri can perform. // Variants in this enum should match the spelling of the ones in niri-config. Most, but not all, // variants from niri-config should be present here. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Parser))] #[cfg_attr(feature = "clap", command(subcommand_value_name = "ACTION"))] #[cfg_attr(feature = "clap", command(subcommand_help_heading = "Actions"))] @@ -205,7 +217,7 @@ pub enum SizeChange { } /// Layout to switch to. -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] pub enum LayoutSwitchTarget { /// The next configured layout. Next, diff --git a/src/ipc/client.rs b/src/ipc/client.rs index b2004a7b..27e1eaa1 100644 --- a/src/ipc/client.rs +++ b/src/ipc/client.rs @@ -3,8 +3,8 @@ use std::io::{Read, Write}; use std::net::Shutdown; use std::os::unix::net::UnixStream; -use anyhow::{bail, Context}; -use niri_ipc::{Mode, Output, Request, Response}; +use anyhow::{anyhow, bail, Context}; +use niri_ipc::{Mode, Output, Reply, Request, Response}; use crate::cli::Msg; @@ -36,20 +36,15 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { .read_to_end(&mut buf) .context("error reading IPC response")?; - if matches!(msg, Msg::Action { .. }) { - if buf.is_empty() { - return Ok(()); - } else { - bail!("unexpected response: expected no response, got {buf:?}"); - } - } + let reply: Reply = serde_json::from_slice(&buf).context("error parsing IPC reply")?; + + let response = reply + .map_err(|msg| anyhow!(msg)) + .context("niri could not handle the request")?; - let response = serde_json::from_slice(&buf).context("error parsing IPC response")?; match msg { Msg::Outputs => { - #[allow(irrefutable_let_patterns)] - let Response::Outputs(outputs) = response - else { + let Response::Outputs(outputs) = response else { bail!("unexpected response: expected Outputs, got {response:?}"); }; @@ -109,7 +104,11 @@ pub fn handle_msg(msg: Msg, json: bool) -> anyhow::Result<()> { println!(); } } - Msg::Action { .. } => unreachable!(), + Msg::Action { .. } => { + let Response::Handled = response else { + bail!("unexpected response: expected Handled, got {response:?}"); + }; + } } Ok(()) diff --git a/src/ipc/server.rs b/src/ipc/server.rs index bedfe48f..a1f3905b 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -110,7 +110,19 @@ async fn handle_client(ctx: ClientCtx, stream: Async<'_, UnixStream>) -> anyhow: .await .context("error reading request")?; - let request: Request = serde_json::from_str(&buf).context("error parsing request")?; + let reply = process(&ctx, &buf).map_err(|err| { + warn!("error processing IPC request: {err:?}"); + err.to_string() + }); + + let buf = serde_json::to_vec(&reply).context("error formatting reply")?; + write.write_all(&buf).await.context("error writing reply")?; + + Ok(()) +} + +fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> { + let request: Request = serde_json::from_str(buf).context("error parsing request")?; let response = match request { Request::Outputs => { @@ -122,15 +134,9 @@ async fn handle_client(ctx: ClientCtx, stream: Async<'_, UnixStream>) -> anyhow: ctx.event_loop.insert_idle(move |state| { state.do_action(action); }); - return Ok(()); + Response::Handled } }; - let buf = serde_json::to_vec(&response).context("error formatting response")?; - write - .write_all(&buf) - .await - .context("error writing response")?; - - Ok(()) + Ok(response) } |
