//! Everything that talks to something outside this process: the chain's //! JSON-RPC, the substrate-telemetry feed, and Postgres. //! //! The split is deliberate. `blackbeard-core` decodes and tallies with no I/O //! at all and is exercised entirely by unit tests; this crate does the talking //! and holds the retry, reconnect and schema concerns. `blackbeard-api` wires //! the two together. #![deny(missing_docs)] pub mod rpc; pub mod store; pub mod telemetry; /// Failures reaching a node, the telemetry feed, or the database. #[derive(Debug, thiserror::Error)] pub enum DataError { /// The node answered with a JSON-RPC error, or an unusable result. #[error("rpc {method}: {message}")] Rpc { /// Which method. method: String, /// What the node said. message: String, }, /// The HTTP request itself failed — connection refused, timeout, TLS. #[error("http transport: {0}")] Http(#[from] reqwest::Error), /// A WebSocket connection failed or dropped. /// /// Boxed: tungstenite's error is by far the largest variant here, and an /// unboxed copy would be paid for on the stack by every `Result` in this /// crate — including the RPC calls made several times a second. #[error("websocket: {0}")] WebSocket(#[from] Box), /// A response did not have the shape we expected. #[error("decoding a response: {0}")] Decode(#[from] serde_json::Error), /// A node answered with something that was not a JSON-RPC response at all. /// /// Distinct from [`Self::Rpc`], which is the node answering *correctly* /// with an error. This one is a host misbehaving, and so is worth trying /// another endpoint for — the difference is what keeps a caller's own /// mistake from walking the whole endpoint list. #[error("malformed response to {method}: {message}")] Malformed { /// Which method. method: String, /// What was wrong with it. message: String, }, /// A database query failed. #[error("database: {0}")] Database(#[from] sqlx::Error), /// A migration failed to apply. #[error("migration: {0}")] Migration(#[from] sqlx::migrate::MigrateError), } impl From for DataError { fn from(e: tokio_tungstenite::tungstenite::Error) -> Self { DataError::WebSocket(Box::new(e)) } }