From 60171aab61de3d40eadb9584e57634df6ca5ce3b Mon Sep 17 00:00:00 2001 From: b5 Date: Sun, 23 Oct 2022 12:13:52 -0400 Subject: [PATCH 1/2] feat: p2p api service calls with a p2p-specific error, better feedback --- iroh-api/Cargo.toml | 1 + iroh-api/src/error.rs | 27 +++++++++++++++++++++++++++ iroh-api/src/lib.rs | 2 ++ iroh-api/src/p2p.rs | 9 ++++++++- iroh/src/main.rs | 20 +++++++++++++++++++- 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 iroh-api/src/error.rs diff --git a/iroh-api/Cargo.toml b/iroh-api/Cargo.toml index 3d5c6cd826..c522f95ac9 100644 --- a/iroh-api/Cargo.toml +++ b/iroh-api/Cargo.toml @@ -28,6 +28,7 @@ async-stream = "0.3.3" mockall = { version = "0.11.2", optional = true } serde = { version = "1.0", features = ["derive"] } relative-path = "1.7.2" +thiserror = "1.0" [dev-dependencies] tempfile = "3.3.0" diff --git a/iroh-api/src/error.rs b/iroh-api/src/error.rs new file mode 100644 index 0000000000..a3d25bf85f --- /dev/null +++ b/iroh-api/src/error.rs @@ -0,0 +1,27 @@ +use std::io; +use thiserror::Error as ThisError; +// use std::error::Error; +use anyhow::{anyhow, Error}; + +/// LockError is the set of known program lock errors +#[derive(ThisError, Debug)] +pub enum ApiError<'a> { + #[error("Can't connect to {service}. Is the service running?")] + ConnectionRefused { service: &'a str }, + /// catchall error type + #[error("{source}")] + Uncategorized { + #[from] + source: anyhow::Error, + }, +} + +pub fn map_service_error(service: &'static str, e: Error) -> Error { + let io_error = e.root_cause().downcast_ref::(); + if let Some(io_error) = io_error { + if io_error.kind() == io::ErrorKind::ConnectionRefused { + return anyhow!(ApiError::ConnectionRefused { service }); + } + } + e +} diff --git a/iroh-api/src/lib.rs b/iroh-api/src/lib.rs index 240c2a796b..604619f94e 100644 --- a/iroh-api/src/lib.rs +++ b/iroh-api/src/lib.rs @@ -1,6 +1,7 @@ mod api; mod api_ext; mod config; +mod error; mod p2p; #[cfg(feature = "testing")] @@ -18,3 +19,4 @@ pub use iroh_resolver::unixfs_builder::AddEvent; pub use iroh_rpc_client::{Lookup, ServiceStatus, StatusRow, StatusTable}; pub use libp2p::gossipsub::MessageId; pub use libp2p::{Multiaddr, PeerId}; +pub use crate::error::ApiError; \ No newline at end of file diff --git a/iroh-api/src/p2p.rs b/iroh-api/src/p2p.rs index 1c3cbff5e8..1d804432e7 100644 --- a/iroh-api/src/p2p.rs +++ b/iroh-api/src/p2p.rs @@ -1,3 +1,4 @@ +use crate::error::map_service_error; use anyhow::Result; use async_trait::async_trait; use iroh_rpc_client::{Lookup, P2pClient}; @@ -32,7 +33,11 @@ pub trait P2p: Sync { #[async_trait] impl P2p for ClientP2p { async fn lookup_local(&self) -> Result { - let (_, listen_addrs) = self.client.get_listening_addrs().await?; + let (_, listen_addrs) = self + .client + .get_listening_addrs() + .await + .map_err(|e| map_service_error("p2p", e))?; Ok(Lookup { peer_id: self.client.local_peer_id().await?, listen_addrs, @@ -51,6 +56,7 @@ impl P2p for ClientP2p { self.client.lookup(peer_id, Some(addr.clone())).await } } + .map_err(|e| map_service_error("p2p", e)) } async fn connect(&self, addr: &PeerIdOrAddr) -> Result<()> { @@ -61,6 +67,7 @@ impl P2p for ClientP2p { self.client.connect(peer_id, vec![addr.clone()]).await } } + .map_err(|e| map_service_error("p2p", e)) } } diff --git a/iroh/src/main.rs b/iroh/src/main.rs index bea9abae9a..11648e7467 100644 --- a/iroh/src/main.rs +++ b/iroh/src/main.rs @@ -1,5 +1,7 @@ use anyhow::{anyhow, Result}; use clap::Parser; +use crossterm::style::Stylize; +use iroh_api::ApiError; use std::io; #[tokio::main(flavor = "multi_thread")] @@ -30,10 +32,26 @@ fn transform_error(r: Result<()>) -> Result<()> { if let Some(io_error) = io_error { if io_error.kind() == io::ErrorKind::ConnectionRefused { return Err(anyhow!( - "Connection refused. Are `iroh-p2p` and `iroh-store` running?" + "Connection refused. Are services running?\n{}", + "hint: see 'iroh start' for more on starting services".yellow(), )); } } + let api_error = e.root_cause().downcast_ref::(); + if let Some(api_error) = api_error { + match api_error { + ApiError::ConnectionRefused { service } => { + return Err(anyhow!( + "Connection refused. This command requires a running {} service.\n{}", + service, + format!("hint: try 'iroh start {}'", service).yellow(), + )); + } + _ => { + return Err(e); + } + } + } Err(e) } } From 1bc985ad82f7158bd607fd3bb3a7102710439abe Mon Sep 17 00:00:00 2001 From: b5 Date: Sun, 23 Oct 2022 12:38:31 -0400 Subject: [PATCH 2/2] fmt --- iroh-api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iroh-api/src/lib.rs b/iroh-api/src/lib.rs index 604619f94e..0b5a08481e 100644 --- a/iroh-api/src/lib.rs +++ b/iroh-api/src/lib.rs @@ -8,6 +8,7 @@ mod p2p; pub use crate::api::MockApi; pub use crate::api::{Api, Iroh, OutType}; pub use crate::api_ext::ApiExt; +pub use crate::error::ApiError; #[cfg(feature = "testing")] pub use crate::p2p::MockP2p; pub use crate::p2p::P2p as P2pApi; @@ -19,4 +20,3 @@ pub use iroh_resolver::unixfs_builder::AddEvent; pub use iroh_rpc_client::{Lookup, ServiceStatus, StatusRow, StatusTable}; pub use libp2p::gossipsub::MessageId; pub use libp2p::{Multiaddr, PeerId}; -pub use crate::error::ApiError; \ No newline at end of file