From fef0d7a5421b78afcc9cbe72e23df47c56f6fd54 Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Thu, 15 Feb 2024 17:03:40 +1300 Subject: [PATCH] MAJOR error rewrite to track file/line --- Cargo.lock | 1 - Cargo.toml | 1 - src/error.rs | 286 +++++++++++++++++++++++++++----- src/macros.rs | 8 +- src/main.rs | 18 +- src/nostr.rs | 11 +- src/store/mod.rs | 6 +- src/tls.rs | 4 +- src/types/event/json_event.rs | 22 +-- src/types/event/mod.rs | 12 +- src/types/filter/json_filter.rs | 27 ++- src/types/filter/mod.rs | 8 +- src/types/id.rs | 2 +- src/types/parse/json_escape.rs | 22 ++- src/types/parse/json_parse.rs | 58 +++---- src/types/parse/utf8.rs | 10 +- src/types/tags.rs | 8 +- 17 files changed, 352 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 160c0bb..f2f3518 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,7 +152,6 @@ dependencies = [ "secp256k1", "serde", "tempfile", - "thiserror", "tokio", "tokio-rustls", ] diff --git a/Cargo.toml b/Cargo.toml index 1481bd8..3cc008a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ ron = "0.8" rustls-pemfile = "1.0" secp256k1 = { version = "0.28", features = [ "hashes", "global-context", "rand-std" ] } serde = { version = "1.0", features = ["derive"] } -thiserror = "1.0" tokio = { version = "1", features = [ "full" ] } tokio-rustls = "0.24" diff --git a/src/error.rs b/src/error.rs index 875b81f..9de1953 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,112 +1,308 @@ -use thiserror::Error; +use std::error::Error as StdError; +use std::panic::Location; + +#[derive(Debug)] +pub struct Error { + pub inner: ChorusError, + location: &'static Location<'static>, +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(&self.inner) + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}, {}", self.inner, self.location) + } +} /// Errors that can occur in the chorus crate -#[derive(Error, Debug)] -pub enum Error { +#[derive(Debug)] +pub enum ChorusError { // Bad event id - #[error("Bad event id, does not match hash")] BadEventId, // Bad hex input - #[error("Bad hex input")] BadHexInput, // Output buffer too small - #[error("Output buffer too small")] BufferTooSmall, // Channel Recv - #[error("Channel receive: {0}")] - ChannelRecv(#[from] tokio::sync::broadcast::error::RecvError), + ChannelRecv(tokio::sync::broadcast::error::RecvError), // Channel Send - #[error("Channel send: {0}")] - ChannelSend(#[from] tokio::sync::broadcast::error::SendError), + ChannelSend(tokio::sync::broadcast::error::SendError), // Config - #[error("Config: {0}")] - Config(#[from] ron::error::SpannedError), + Config(ron::error::SpannedError), // Crypto - #[error("Crypto: {0}")] - Crypto(#[from] secp256k1::Error), + Crypto(secp256k1::Error), // Duplicate event - #[error("Duplicate")] Duplicate, // End of Input - #[error("End of input")] EndOfInput, // Http - #[error("HTTP: {0}")] - Http(#[from] hyper::http::Error), + Http(hyper::http::Error), // Hyper - #[error("Hyper: {0}")] - Hyper(#[from] hyper::Error), + Hyper(hyper::Error), - // I/O Error - #[error("I/O: {0}")] - Io(#[from] std::io::Error), + // I/O + Io(std::io::Error), // JSON Bad (general) - #[error("JSON bad: {0} at position {1}")] JsonBad(&'static str, usize), // JSON Bad Character - #[error("JSON bad character: {0} at position {1}, {2} was expected")] JsonBadCharacter(char, usize, char), // JSON Bad Event - #[error("JSON bad event: {0} at position {1}")] JsonBadEvent(&'static str, usize), // JSON Bad Filter - #[error("JSON bad filter: {0} at position {1}")] JsonBadFilter(&'static str, usize), // JSON Bad String Character - #[error("JSON string bad character: codepoint {0}")] JsonBadStringChar(u32), // JSON Escape - #[error("JSON string escape error")] JsonEscape, // JSON Escape Surrogate - #[error("JSON string escape surrogate (ancient style) is not supported")] JsonEscapeSurrogate, // LMDB - #[error("LMDB: {0}")] - Lmdb(#[from] heed::Error), + Lmdb(heed::Error), - #[error("Private Key Not Found")] + // No private key NoPrivateKey, // Rustls - #[error("TLS: {0}")] - Rustls(#[from] tokio_rustls::rustls::Error), + Rustls(tokio_rustls::rustls::Error), - // Tunstenite - #[error("Websocket: {0}")] - Tungstenite(#[from] hyper_tungstenite::tungstenite::error::Error), + // Tungstenite + Tungstenite(hyper_tungstenite::tungstenite::error::Error), // Filter is underspecified - #[error("Filter is underspecified. Scrapers are not allowed")] Scraper, // UTF-8 - #[error("UTF-8: {0}")] - Utf8(#[from] std::str::Utf8Error), + Utf8(std::str::Utf8Error), // UTF-8 - #[error("UTF-8 error")] Utf8Error, - // Tunstenite Protocol - #[error("Websocket Protocol: {0}")] - WebsocketProtocol(#[from] hyper_tungstenite::tungstenite::error::ProtocolError), + // Tungstenite Protocol + WebsocketProtocol(hyper_tungstenite::tungstenite::error::ProtocolError), +} + +impl std::fmt::Display for ChorusError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ChorusError::BadEventId => write!(f, "Bad event id, does not match hash"), + ChorusError::BadHexInput => write!(f, "Bad hex input"), + ChorusError::BufferTooSmall => write!(f, "Output buffer too small"), + ChorusError::ChannelRecv(e) => write!(f, "{e}"), + ChorusError::ChannelSend(e) => write!(f, "{e}"), + ChorusError::Config(e) => write!(f, "{e}"), + ChorusError::Crypto(e) => write!(f, "{e}"), + ChorusError::Duplicate => write!(f, "Duplicate"), + ChorusError::EndOfInput => write!(f, "End of input"), + ChorusError::Http(e) => write!(f, "{e}"), + ChorusError::Hyper(e) => write!(f, "{e}"), + ChorusError::Io(e) => write!(f, "{e}"), + ChorusError::JsonBad(err, pos) => write!(f, "JSON bad: {err} at position {pos}"), + ChorusError::JsonBadCharacter(c, pos, ec) => write!( + f, + "JSON bad character: {c} at position {pos}, {ec} was expected" + ), + ChorusError::JsonBadEvent(err, pos) => { + write!(f, "JSON bad event: {err} at position {pos}") + } + ChorusError::JsonBadFilter(err, pos) => { + write!(f, "JSON bad filter: {err} at position {pos}") + } + ChorusError::JsonBadStringChar(ch) => { + write!(f, "JSON string bad character: codepoint {ch}") + } + ChorusError::JsonEscape => write!(f, "JSON string escape error"), + ChorusError::JsonEscapeSurrogate => write!( + f, + "JSON string escape surrogate (ancient style) is not supported" + ), + ChorusError::Lmdb(e) => write!(f, "{e}"), + ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"), + ChorusError::Rustls(e) => write!(f, "{e}"), + ChorusError::Tungstenite(e) => write!(f, "{e}"), + ChorusError::Scraper => write!(f, "Filter is underspecified. Scrapers are not allowed"), + ChorusError::Utf8(e) => write!(f, "{e}"), + ChorusError::Utf8Error => write!(f, "UTF-8 error"), + ChorusError::WebsocketProtocol(e) => write!(f, "{e}"), + } + } +} + +impl StdError for ChorusError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + ChorusError::ChannelRecv(e) => Some(e), + ChorusError::ChannelSend(e) => Some(e), + ChorusError::Config(e) => Some(e), + ChorusError::Crypto(e) => Some(e), + ChorusError::Http(e) => Some(e), + ChorusError::Hyper(e) => Some(e), + ChorusError::Io(e) => Some(e), + ChorusError::Lmdb(e) => Some(e), + ChorusError::Rustls(e) => Some(e), + ChorusError::Tungstenite(e) => Some(e), + ChorusError::Utf8(e) => Some(e), + ChorusError::WebsocketProtocol(e) => Some(e), + _ => None, + } + } +} + +// Note: we impl Into because our typical pattern is ChorusError::Variant.into() +// when we tried implementing From, the location was deep in rust code's +// blanket into implementation, which wasn't the line number we wanted. +// +// As for converting other error types (below) the try! macro uses From so it +// is correct. +#[allow(clippy::from_over_into)] +impl Into for ChorusError { + #[track_caller] + fn into(self) -> Error { + Error { + inner: self, + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: tokio::sync::broadcast::error::RecvError) -> Self { + Error { + inner: ChorusError::ChannelRecv(err), + location: std::panic::Location::caller(), + } + } +} + +impl From> for Error { + #[track_caller] + fn from(err: tokio::sync::broadcast::error::SendError) -> Self { + Error { + inner: ChorusError::ChannelSend(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: ron::error::SpannedError) -> Self { + Error { + inner: ChorusError::Config(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: secp256k1::Error) -> Self { + Error { + inner: ChorusError::Crypto(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: hyper::http::Error) -> Self { + Error { + inner: ChorusError::Http(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: hyper::Error) -> Self { + Error { + inner: ChorusError::Hyper(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: std::io::Error) -> Self { + Error { + inner: ChorusError::Io(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: heed::Error) -> Self { + Error { + inner: ChorusError::Lmdb(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: tokio_rustls::rustls::Error) -> Self { + Error { + inner: ChorusError::Rustls(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: hyper_tungstenite::tungstenite::error::Error) -> Self { + Error { + inner: ChorusError::Tungstenite(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: std::str::Utf8Error) -> Self { + Error { + inner: ChorusError::Utf8(err), + location: std::panic::Location::caller(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(err: hyper_tungstenite::tungstenite::error::ProtocolError) -> Self { + Error { + inner: ChorusError::WebsocketProtocol(err), + location: std::panic::Location::caller(), + } + } } diff --git a/src/macros.rs b/src/macros.rs index 8317922..a24862b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,7 +19,7 @@ macro_rules! write_hex { ($input:expr, $output:expr, $bytelen:expr) => {{ assert_eq!($input.len(), $bytelen); if $output.len() != $bytelen * 2 { - Err(Error::BufferTooSmall) + Err(crate::error::ChorusError::BufferTooSmall.into()) } else { for (i, byte) in $input.iter().enumerate() { $output[i * 2] = crate::HEX_CHARS[((byte & 0xF0) >> 4) as usize]; @@ -34,17 +34,17 @@ macro_rules! read_hex { ($input:expr, $output:expr, $bytelen:expr) => {{ assert_eq!($output.len(), $bytelen); if $input.len() != $bytelen * 2 { - Err(Error::EndOfInput) + Err(Into::::into(crate::error::ChorusError::EndOfInput)) } else { let mut i = 0; loop { let high = crate::HEX_INVERSE[$input[i * 2] as usize]; if high == 255 { - break Err(Error::BadHexInput); + break Err(crate::error::ChorusError::BadHexInput.into()); } let low = crate::HEX_INVERSE[$input[i * 2 + 1] as usize]; if low == 255 { - break Err(Error::BadHexInput); + break Err(crate::error::ChorusError::BadHexInput.into()); } $output[i] = high * 16 + low; i += 1; diff --git a/src/main.rs b/src/main.rs index 330aba4..cd56ee4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ pub mod types; pub mod web; use crate::config::{Config, FriendlyConfig}; -use crate::error::Error; +use crate::error::{ChorusError, Error}; use crate::globals::GLOBALS; use crate::reply::NostrReply; use crate::store::Store; @@ -172,13 +172,15 @@ async fn handle_http_request( // Handle the websocket if let Err(e) = ws_service.handle_websocket_stream().await { - match e { - Error::Tungstenite(tungstenite::error::Error::Protocol( - tungstenite::error::ProtocolError::ResetWithoutClosingHandshake, - )) => { - // swallow - } - e => log::error!("{}: {}", peer, e), + if matches!( + e.inner, + ChorusError::Tungstenite(tungstenite::error::Error::Protocol( + tungstenite::error::ProtocolError::ResetWithoutClosingHandshake + )) + ) { + // Swallow the boring error + } else { + log::error!("{}: {}", peer, e); } } diff --git a/src/nostr.rs b/src/nostr.rs index d0a55b0..e70dd7a 100644 --- a/src/nostr.rs +++ b/src/nostr.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::error::{ChorusError, Error}; use crate::globals::GLOBALS; use crate::reply::NostrReply; use crate::types::parse::json_escape::json_unescape; @@ -140,8 +140,13 @@ impl WebSocketService { GLOBALS.new_events.send(offset)?; // advertise the new event NostrReply::Ok(event.id(), true, "".to_owned()) } - Err(Error::Duplicate) => NostrReply::Ok(event.id(), true, "duplicate:".to_owned()), - Err(e) => NostrReply::Ok(event.id(), false, format!("{e}")), + Err(e) => { + if matches!(e.inner, ChorusError::Duplicate) { + NostrReply::Ok(event.id(), true, "duplicate:".to_owned()) + } else { + NostrReply::Ok(event.id(), false, format!("{e}")) + } + } }; self.websocket.send(Message::text(reply.as_json())).await?; diff --git a/src/store/mod.rs b/src/store/mod.rs index 88f7eea..90e9ed1 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -1,7 +1,7 @@ pub mod event_store; pub use event_store::EventStore; -use crate::error::Error; +use crate::error::{ChorusError, Error}; use crate::types::{Event, Filter, Id, Kind, Pubkey, Time}; use heed::types::{OwnedType, UnalignedSlice}; use heed::{Database, Env, EnvFlags, EnvOpenOptions}; @@ -133,7 +133,7 @@ impl Store { txn.commit()?; } else { - return Err(Error::Duplicate); + return Err(ChorusError::Duplicate.into()); } Ok(offset) @@ -294,7 +294,7 @@ impl Store { } } } else { - return Err(Error::Scraper); + return Err(ChorusError::Scraper.into()); } Ok(output) diff --git a/src/tls.rs b/src/tls.rs index 1f04084..1e6ca52 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,5 +1,5 @@ use crate::config::Config; -use crate::error::Error; +use crate::error::{ChorusError, Error}; use rustls::{Certificate, PrivateKey}; use std::fs::File; use std::io::BufReader; @@ -25,7 +25,7 @@ pub fn tls_acceptor(config: &Config) -> Result { let key = match keys.pop() { Some(k) => k, - None => return Err(Error::NoPrivateKey), + None => return Err(ChorusError::NoPrivateKey.into()), }; let tls_config = rustls::ServerConfig::builder() diff --git a/src/types/event/json_event.rs b/src/types/event/json_event.rs index e4e9055..0ca56e0 100644 --- a/src/types/event/json_event.rs +++ b/src/types/event/json_event.rs @@ -1,5 +1,5 @@ +use crate::error::{ChorusError, Error}; use crate::types::parse::json_parse::*; -use crate::Error; /// Parses a JSON event from the `input` buffer. Places the parsed event into the `output` buffer. /// Returns the count of consumed bytes and output bytes @@ -7,7 +7,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize // Minimum-sized JSON event is 204 characters long // NOTE: 152 is the minimum binary event if input.len() < 204 { - return Err(Error::JsonBadEvent("Too Short", 0)); + return Err(ChorusError::JsonBadEvent("Too Short", 0).into()); } // This tracks where we are currently looking in the input as we scan forward. @@ -49,12 +49,12 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize // field and value: kind":1 // This allows us to skip length tests below that are shorter than inpos+7 if inpos + 7 > input.len() { - return Err(Error::JsonBadEvent("Too Short or Missing Fields", inpos)); + return Err(ChorusError::JsonBadEvent("Too Short or Missing Fields", inpos).into()); } if &input[inpos..inpos + 3] == b"id\"" { if complete & HAVE_ID == HAVE_ID { - return Err(Error::JsonBadEvent("Duplicate id field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate id field", inpos).into()); } inpos += 3; eat_colon_with_whitespace(input, &mut inpos)?; @@ -62,7 +62,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize complete |= HAVE_ID; } else if &input[inpos..inpos + 4] == b"sig\"" { if complete & HAVE_SIG == HAVE_SIG { - return Err(Error::JsonBadEvent("Duplicate sig field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate sig field", inpos).into()); } inpos += 4; eat_colon_with_whitespace(input, &mut inpos)?; @@ -70,7 +70,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize complete |= HAVE_SIG; } else if &input[inpos..inpos + 5] == b"kind\"" { if complete & HAVE_KIND == HAVE_KIND { - return Err(Error::JsonBadEvent("Duplicate kind field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate kind field", inpos).into()); } inpos += 5; eat_colon_with_whitespace(input, &mut inpos)?; @@ -79,7 +79,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize complete |= HAVE_KIND; } else if &input[inpos..inpos + 5] == b"tags\"" { if complete & HAVE_TAGS == HAVE_TAGS { - return Err(Error::JsonBadEvent("Duplicate tags field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate tags field", inpos).into()); } inpos += 5; eat_colon_with_whitespace(input, &mut inpos)?; @@ -93,7 +93,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize } } else if &input[inpos..inpos + 7] == b"pubkey\"" { if complete & HAVE_PUBKEY == HAVE_PUBKEY { - return Err(Error::JsonBadEvent("Duplicate pubkey field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate pubkey field", inpos).into()); } inpos += 7; eat_colon_with_whitespace(input, &mut inpos)?; @@ -101,7 +101,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize complete |= HAVE_PUBKEY; } else if inpos + 8 <= input.len() && &input[inpos..inpos + 8] == b"content\"" { if complete & HAVE_CONTENT == HAVE_CONTENT { - return Err(Error::JsonBadEvent("Duplicate pubkey field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate pubkey field", inpos).into()); } inpos += 8; eat_colon_with_whitespace(input, &mut inpos)?; @@ -119,7 +119,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize } } else if inpos + 11 <= input.len() && &input[inpos..inpos + 11] == b"created_at\"" { if complete & HAVE_CREATED_AT == HAVE_CREATED_AT { - return Err(Error::JsonBadEvent("Duplicate created_at field", inpos)); + return Err(ChorusError::JsonBadEvent("Duplicate created_at field", inpos).into()); } inpos += 11; eat_colon_with_whitespace(input, &mut inpos)?; @@ -142,7 +142,7 @@ pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize u32::from_ne_bytes(output[0..4].try_into().unwrap()) as usize, )) } else { - Err(Error::JsonBadEvent("Missing Fields", inpos)) + Err(ChorusError::JsonBadEvent("Missing Fields", inpos).into()) } } diff --git a/src/types/event/mod.rs b/src/types/event/mod.rs index bd850c7..4858d46 100644 --- a/src/types/event/mod.rs +++ b/src/types/event/mod.rs @@ -1,5 +1,5 @@ use super::{Id, Kind, Pubkey, Sig, Tags, Time}; -use crate::Error; +use crate::error::{ChorusError, Error}; use std::fmt; mod json_event; @@ -35,11 +35,11 @@ impl<'a> Event<'a> { // this marks off the slice of bytes that represent an event from a potentially longer input pub fn delineate(input: &'a [u8]) -> Result, Error> { if input.len() < 144 + 4 + 4 { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } let len = parse_u32!(input, 0) as usize; if input.len() < len { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } Ok(Event(&input[0..len])) } @@ -47,7 +47,7 @@ impl<'a> Event<'a> { // This copies pub fn copy(&self, output: &mut [u8]) -> Result<(), Error> { if output.len() < self.0.len() { - return Err(Error::BufferTooSmall); + return Err(ChorusError::BufferTooSmall.into()); } output[..self.0.len()].copy_from_slice(self.0); Ok(()) @@ -56,7 +56,7 @@ impl<'a> Event<'a> { // This copies, using the event_store mmap-append api pub fn macopy(&self, output: &mut [u8]) -> Result { if output.len() < self.0.len() { - return Err(std::io::Error::other(Error::BufferTooSmall)); + return Err(std::io::Error::other(ChorusError::BufferTooSmall)); } output[..self.0.len()].copy_from_slice(self.0); Ok(self.0.len()) @@ -144,7 +144,7 @@ impl<'a> Event<'a> { let hashref = >::as_ref(&hash); if hashref != self.id().as_slice() { - return Err(Error::BadEventId); + return Err(ChorusError::BadEventId.into()); } let pubkey = XOnlyPublicKey::from_slice(self.pubkey().as_slice())?; diff --git a/src/types/filter/json_filter.rs b/src/types/filter/json_filter.rs index ac77a91..e167af9 100644 --- a/src/types/filter/json_filter.rs +++ b/src/types/filter/json_filter.rs @@ -1,12 +1,12 @@ +use crate::error::{ChorusError, Error}; use crate::types::parse::json_escape::json_unescape; use crate::types::parse::json_parse::*; -use crate::Error; /// Parses a JSON filter from the `input` buffer. Places the parsed filter into the `output` buffer. /// Returns the count of consumed bytes and output bytes pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usize), Error> { if input.len() < 2 { - return Err(Error::JsonBadFilter("Too short", 0)); + return Err(ChorusError::JsonBadFilter("Too short", 0).into()); } // This tracks where we are currently looking in the input as we scan forward. @@ -76,7 +76,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz if inpos + 4 <= input.len() && &input[inpos..inpos + 4] == b"ids\"" { // Check for duplicate if found & HAVE_IDS == HAVE_IDS { - return Err(Error::JsonBadFilter("Duplicate id field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate id field", inpos).into()); } inpos += 4; @@ -97,7 +97,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } else if inpos + 8 <= input.len() && &input[inpos..inpos + 8] == b"authors\"" { // Check for duplicate if found & HAVE_AUTHORS == HAVE_AUTHORS { - return Err(Error::JsonBadFilter("Duplicate authors field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate authors field", inpos).into()); } inpos += 8; @@ -117,7 +117,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"kinds\"" { // Check for duplicate if found & HAVE_KINDS == HAVE_KINDS { - return Err(Error::JsonBadFilter("Duplicate kinds field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate kinds field", inpos).into()); } inpos += 6; @@ -137,7 +137,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"since\"" { // Check for duplicate if found & HAVE_SINCE == HAVE_SINCE { - return Err(Error::JsonBadFilter("Duplicate since field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate since field", inpos).into()); } inpos += 6; @@ -149,7 +149,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"until\"" { // Check for duplicate if found & HAVE_UNTIL == HAVE_UNTIL { - return Err(Error::JsonBadFilter("Duplicate until field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate until field", inpos).into()); } inpos += 6; @@ -161,7 +161,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"limit\"" { // Check for duplicate if found & HAVE_LIMIT == HAVE_LIMIT { - return Err(Error::JsonBadFilter("Duplicate limit field", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate limit field", inpos).into()); } inpos += 6; @@ -189,7 +189,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz // Remember we found this tag in the `found_tags` bitfield if let Some(bit) = letter_to_tag_bit(letter) { if found_tags & bit == bit { - return Err(Error::JsonBadFilter("Duplicate tag", inpos)); + return Err(ChorusError::JsonBadFilter("Duplicate tag", inpos).into()); } found_tags |= bit; } @@ -250,10 +250,9 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } let u = read_u64(input, &mut inpos)?; if u > 65535 { - return Err(Error::JsonBadFilter( - "Filter has kind number too large", - inpos, - )); + return Err( + ChorusError::JsonBadFilter("Filter has kind number too large", inpos).into(), + ); } output[end..end + 2].copy_from_slice((u as u16).to_ne_bytes().as_slice()); num_kinds += 1; @@ -321,7 +320,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } if end > 65535 { - return Err(Error::JsonBadFilter("Filter is too long", end)); + return Err(ChorusError::JsonBadFilter("Filter is too long", end).into()); } // Write length of filter diff --git a/src/types/filter/mod.rs b/src/types/filter/mod.rs index 0b52957..25be62d 100644 --- a/src/types/filter/mod.rs +++ b/src/types/filter/mod.rs @@ -1,5 +1,5 @@ use super::{Event, Id, Kind, Pubkey, Tags, Time}; -use crate::Error; +use crate::error::{ChorusError, Error}; use std::fmt; mod json_filter; @@ -47,18 +47,18 @@ impl<'a> Filter<'a> { pub fn delineate(input: &'a [u8]) -> Result, Error> { if input.len() < ARRAYS_OFFSET { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } let len = parse_u16!(input, 0) as usize; if input.len() < len { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } Ok(Filter(&input[0..len])) } pub fn copy(&self, output: &mut [u8]) -> Result<(), Error> { if output.len() < self.0.len() { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } output[..self.0.len()].copy_from_slice(self.0); Ok(()) diff --git a/src/types/id.rs b/src/types/id.rs index bff1b78..6027e11 100644 --- a/src/types/id.rs +++ b/src/types/id.rs @@ -1,4 +1,4 @@ -use crate::Error; +use crate::error::Error; use serde::{Deserialize, Serialize}; use std::fmt; diff --git a/src/types/parse/json_escape.rs b/src/types/parse/json_escape.rs index 539bcab..c911324 100644 --- a/src/types/parse/json_escape.rs +++ b/src/types/parse/json_escape.rs @@ -1,5 +1,5 @@ use super::utf8::{encode_utf8, next_code_point}; -use crate::Error; +use crate::error::{ChorusError, Error}; // LITERAL UNESCAPED: 0x20-0x21, 0x23-0x5B, 0x5D-10FFFF // ESCAPES: \" \\ \/ /b /f /n /r /t @@ -13,7 +13,7 @@ pub fn json_escape(input: &[u8], out: &mut [u8]) -> Result { // closure to output bytes let mut output = |s: &[u8]| -> Result<(), Error> { if out.len() < write_pos + s.len() { - Err(Error::BufferTooSmall) + Err(ChorusError::BufferTooSmall.into()) } else { out[write_pos..write_pos + s.len()].copy_from_slice(s); write_pos += s.len(); @@ -51,7 +51,9 @@ pub fn json_escape(input: &[u8], out: &mut [u8]) -> Result { macro_rules! output_slice { ($slice:expr, $out:expr, $pos:expr) => { if $out.len() < *$pos + $slice.len() { - Err(Error::BufferTooSmall) + Err(Into::::into( + crate::error::ChorusError::BufferTooSmall, + )) } else { $out[*$pos..*$pos + $slice.len()].copy_from_slice($slice); *$pos += $slice.len(); @@ -63,7 +65,9 @@ macro_rules! output_slice { macro_rules! output_byte { ($byte:expr, $out:expr, $pos:expr) => { if $out.len() < *$pos + 1 { - Err(Error::BufferTooSmall) + Err(Into::::into( + crate::error::ChorusError::BufferTooSmall, + )) } else { unsafe { *$out.get_unchecked_mut(*$pos) = $byte }; *$pos += 1; @@ -99,7 +103,7 @@ pub fn json_unescape(input: &[u8], out: &mut [u8]) -> Result<(usize, usize), Err if inescape { inescape = false; if codepoint > 255 { - return Err(Error::JsonEscape); + return Err(ChorusError::JsonEscape.into()); } match codepoint as u8 { QUOTE | BACKSLASH | SLASH => { @@ -111,17 +115,17 @@ pub fn json_unescape(input: &[u8], out: &mut [u8]) -> Result<(usize, usize), Err b'r' => output_byte!(CR, out, &mut write_pos)?, b't' => output_byte!(TAB, out, &mut write_pos)?, b'u' => uescape = Some((0, 0)), - _ => return Err(Error::JsonEscape), // nothing else is a legal escape + _ => return Err(ChorusError::JsonEscape.into()), // nothing else is a legal escape } } else if let Some((digit, total)) = uescape { // must be a digit if !(48..=57).contains(&codepoint) { - return Err(Error::JsonEscape); + return Err(ChorusError::JsonEscape.into()); } let total = total + ((codepoint - 48) << (4 * (3 - digit))); if digit >= 3 { if (0xD800..=0xDFFF).contains(&total) { - return Err(Error::JsonEscapeSurrogate); + return Err(ChorusError::JsonEscapeSurrogate.into()); } let s = encode_utf8(total, &mut out[write_pos..])?; write_pos += s; @@ -138,7 +142,7 @@ pub fn json_unescape(input: &[u8], out: &mut [u8]) -> Result<(usize, usize), Err // ending double quote break; } else { - return Err(Error::JsonBadStringChar(codepoint)); + return Err(ChorusError::JsonBadStringChar(codepoint).into()); } p += size; } diff --git a/src/types/parse/json_parse.rs b/src/types/parse/json_parse.rs index 3c91520..d83e91c 100644 --- a/src/types/parse/json_parse.rs +++ b/src/types/parse/json_parse.rs @@ -1,5 +1,5 @@ use super::json_escape::json_unescape; -use crate::Error; +use crate::error::{ChorusError, Error}; #[inline] pub fn eat_whitespace(input: &[u8], inposp: &mut usize) { @@ -18,16 +18,12 @@ pub fn eat_whitespace_and_commas(input: &[u8], inposp: &mut usize) { #[inline] pub fn verify_char(input: &[u8], ch: u8, inposp: &mut usize) -> Result<(), Error> { if *inposp >= input.len() { - Err(Error::JsonBad("Too Short or Missing Fields", *inposp)) + Err(ChorusError::JsonBad("Too Short or Missing Fields", *inposp).into()) } else if input[*inposp] == ch { *inposp += 1; Ok(()) } else { - Err(Error::JsonBadCharacter( - input[*inposp] as char, - *inposp, - ch as char, - )) + Err(ChorusError::JsonBadCharacter(input[*inposp] as char, *inposp, ch as char).into()) } } @@ -42,7 +38,7 @@ pub fn next_object_field(input: &[u8], inposp: &mut usize) -> Result= input.len() { - return Err(Error::JsonBad("Too short", *inposp)); + return Err(ChorusError::JsonBad("Too short", *inposp).into()); } if input[*inposp] == b'}' { *inposp += 1; @@ -51,14 +47,14 @@ pub fn next_object_field(input: &[u8], inposp: &mut usize) -> Result Result<(), Error> { verify_char(input, b'"', inposp)?; if *inposp + 64 >= input.len() { - return Err(Error::JsonBad("Too short reading id", *inposp)); + return Err(ChorusError::JsonBad("Too short reading id", *inposp).into()); } // Read the hex ID and write the binary ID into the output event structure read_hex!(&input[*inposp..*inposp + 64], &mut output[..32], 32)?; @@ -70,7 +66,7 @@ pub fn read_id(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<() pub fn read_pubkey(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<(), Error> { verify_char(input, b'"', inposp)?; if *inposp + 64 >= input.len() { - return Err(Error::JsonBad("Too short reading pubkey", *inposp)); + return Err(ChorusError::JsonBad("Too short reading pubkey", *inposp).into()); } // Read the hex pubkey and write the binary pubkey into the output event structure read_hex!(&input[*inposp..*inposp + 64], &mut output[..32], 32)?; @@ -88,10 +84,11 @@ pub fn read_u64(input: &[u8], inposp: &mut usize) -> Result { *inposp += 1; } if !any { - return Err(Error::JsonBad( + return Err(ChorusError::JsonBad( "Created at must be a positive or zero valued number", *inposp, - )); + ) + .into()); } Ok(value) } @@ -105,13 +102,14 @@ pub fn read_kind(input: &[u8], inposp: &mut usize) -> Result { *inposp += 1; } if !any { - return Err(Error::JsonBad( + return Err(ChorusError::JsonBad( "Kind at must be a positive or zero valued number", *inposp, - )); + ) + .into()); } if value > 65535 { - Err(Error::JsonBad("Kind larger than 65535", *inposp)) + Err(ChorusError::JsonBad("Kind larger than 65535", *inposp).into()) } else { Ok(value as u16) } @@ -173,7 +171,7 @@ pub fn read_tags_array( } eat_whitespace(input, inposp); } - _ => return Err(Error::JsonBad("Tag array bad character", *inposp)), + _ => return Err(ChorusError::JsonBad("Tag array bad character", *inposp).into()), } } @@ -191,7 +189,7 @@ pub fn count_tags(input: &[u8], mut inpos: usize) -> Result { match input[inpos] { b']' => return Ok(0), // no tags b'[' => (), // expected - _ => return Err(Error::JsonBad("Tag array bad initial character", inpos)), + _ => return Err(ChorusError::JsonBad("Tag array bad initial character", inpos).into()), } let mut count = 1; @@ -210,7 +208,7 @@ pub fn count_tags(input: &[u8], mut inpos: usize) -> Result { burn_tag(input, &mut inpos)?; eat_whitespace(input, &mut inpos); } - _ => return Err(Error::JsonBad("Tag array bad character", inpos)), + _ => return Err(ChorusError::JsonBad("Tag array bad character", inpos).into()), } } } @@ -249,7 +247,7 @@ pub fn read_tag( *inposp += 1; break; } - _ => return Err(Error::JsonBad("Tag array bad character", *inposp)), + _ => return Err(ChorusError::JsonBad("Tag array bad character", *inposp).into()), } } @@ -284,7 +282,7 @@ pub fn read_content( pub fn read_sig(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<(), Error> { verify_char(input, b'"', inposp)?; if *inposp + 128 >= input.len() { - return Err(Error::JsonBad("Too short reading sig", *inposp)); + return Err(ChorusError::JsonBad("Too short reading sig", *inposp).into()); } // Read the hex sig and write the binary sig into the output event structure read_hex!(&input[*inposp..*inposp + 128], &mut output[80..144], 64)?; @@ -307,7 +305,7 @@ pub fn burn_string(input: &[u8], inposp: &mut usize) -> Result<(), Error> { *inposp += 1; Ok(()) } else { - Err(Error::JsonBad("Unterminated string", *inposp)) + Err(ChorusError::JsonBad("Unterminated string", *inposp).into()) } } @@ -372,10 +370,7 @@ pub fn burn_array(input: &[u8], inposp: &mut usize) -> Result<(), Error> { pub fn burn_value(input: &[u8], inposp: &mut usize) -> Result<(), Error> { if *inposp >= input.len() { - return Err(Error::JsonBad( - "Too short burning an unused JSON value", - *inposp, - )); + return Err(ChorusError::JsonBad("Too short burning an unused JSON value", *inposp).into()); } match input[*inposp] { b'"' => { @@ -398,10 +393,11 @@ pub fn burn_value(input: &[u8], inposp: &mut usize) -> Result<(), Error> { if b"123456789".contains(&input[*inposp]) { burn_number(input, inposp)? } else { - return Err(Error::JsonBad( + return Err(ChorusError::JsonBad( "Too short burning an unused JSON value", *inposp, - )); + ) + .into()); } } } @@ -414,7 +410,7 @@ pub fn burn_null(input: &[u8], inposp: &mut usize) -> Result<(), Error> { *inposp += 4; Ok(()) } else { - Err(Error::JsonBad("Expected null", *inposp)) + Err(ChorusError::JsonBad("Expected null", *inposp).into()) } } @@ -423,7 +419,7 @@ pub fn burn_true(input: &[u8], inposp: &mut usize) -> Result<(), Error> { *inposp += 4; Ok(()) } else { - Err(Error::JsonBad("Expected true", *inposp)) + Err(ChorusError::JsonBad("Expected true", *inposp).into()) } } @@ -432,7 +428,7 @@ pub fn burn_false(input: &[u8], inposp: &mut usize) -> Result<(), Error> { *inposp += 5; Ok(()) } else { - Err(Error::JsonBad("Expected false", *inposp)) + Err(ChorusError::JsonBad("Expected false", *inposp).into()) } } diff --git a/src/types/parse/utf8.rs b/src/types/parse/utf8.rs index 6b7704a..eb4383e 100644 --- a/src/types/parse/utf8.rs +++ b/src/types/parse/utf8.rs @@ -1,4 +1,4 @@ -use crate::Error; +use crate::error::{ChorusError, Error}; // Reads the next code point if UTF-8, and returns it along with the number of characters // that make it up. @@ -18,7 +18,7 @@ pub fn next_code_point(input: &[u8]) -> Result, Error> { // Decode from a byte combination out of: [[[x y] z] w] let init = utf8_first_byte(x, 2); if len < 2 { - return Err(Error::Utf8Error); + return Err(ChorusError::Utf8Error.into()); } let y = input[1]; let mut ch = utf8_acc_cont_byte(init, y); @@ -26,7 +26,7 @@ pub fn next_code_point(input: &[u8]) -> Result, Error> { // [[x y z] w] case // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid if len < 3 { - return Err(Error::Utf8Error); + return Err(ChorusError::Utf8Error.into()); } let z = input[2]; let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z); @@ -35,7 +35,7 @@ pub fn next_code_point(input: &[u8]) -> Result, Error> { // [x y z w] case // use only the lower 3 bits of `init` if len < 4 { - return Err(Error::Utf8Error); + return Err(ChorusError::Utf8Error.into()); } let w = input[3]; ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w); @@ -78,7 +78,7 @@ pub fn encode_utf8(code: u32, dst: &mut [u8]) -> Result { *dst.get_unchecked_mut(3) = (code & 0x3F) as u8 | TAG_CONT; 4 } else { - return Err(Error::BufferTooSmall); + return Err(ChorusError::BufferTooSmall.into()); } }; Ok(len) diff --git a/src/types/tags.rs b/src/types/tags.rs index 64250e9..bb8f94b 100644 --- a/src/types/tags.rs +++ b/src/types/tags.rs @@ -1,4 +1,4 @@ -use crate::Error; +use crate::error::{ChorusError, Error}; use std::fmt; /* @@ -23,11 +23,11 @@ impl<'a> Tags<'a> { // this marks off the slice of bytes that represent the tags from a potentially longer input pub fn delineate(input: &'a [u8]) -> Result, Error> { if input.len() < 2 { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } let len = parse_u16!(input, 0) as usize; if input.len() < len { - return Err(Error::EndOfInput); + return Err(ChorusError::EndOfInput.into()); } Ok(Tags(&input[0..len])) } @@ -35,7 +35,7 @@ impl<'a> Tags<'a> { // This copies pub fn copy(&self, output: &mut [u8]) -> Result<(), Error> { if output.len() < self.0.len() { - return Err(Error::BufferTooSmall); + return Err(ChorusError::BufferTooSmall.into()); } output[..self.0.len()].copy_from_slice(self.0); Ok(())