MAJOR error rewrite to track file/line

This commit is contained in:
Mike Dilger 2024-02-15 17:03:40 +13:00
parent 612ddb0ab3
commit fef0d7a542
17 changed files with 352 additions and 152 deletions

1
Cargo.lock generated
View File

@ -152,7 +152,6 @@ dependencies = [
"secp256k1",
"serde",
"tempfile",
"thiserror",
"tokio",
"tokio-rustls",
]

View File

@ -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"

View File

@ -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<usize>),
ChannelSend(tokio::sync::broadcast::error::SendError<usize>),
// 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<Error> for ChorusError {
#[track_caller]
fn into(self) -> Error {
Error {
inner: self,
location: std::panic::Location::caller(),
}
}
}
impl From<tokio::sync::broadcast::error::RecvError> 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<tokio::sync::broadcast::error::SendError<usize>> for Error {
#[track_caller]
fn from(err: tokio::sync::broadcast::error::SendError<usize>) -> Self {
Error {
inner: ChorusError::ChannelSend(err),
location: std::panic::Location::caller(),
}
}
}
impl From<ron::error::SpannedError> for Error {
#[track_caller]
fn from(err: ron::error::SpannedError) -> Self {
Error {
inner: ChorusError::Config(err),
location: std::panic::Location::caller(),
}
}
}
impl From<secp256k1::Error> for Error {
#[track_caller]
fn from(err: secp256k1::Error) -> Self {
Error {
inner: ChorusError::Crypto(err),
location: std::panic::Location::caller(),
}
}
}
impl From<hyper::http::Error> for Error {
#[track_caller]
fn from(err: hyper::http::Error) -> Self {
Error {
inner: ChorusError::Http(err),
location: std::panic::Location::caller(),
}
}
}
impl From<hyper::Error> for Error {
#[track_caller]
fn from(err: hyper::Error) -> Self {
Error {
inner: ChorusError::Hyper(err),
location: std::panic::Location::caller(),
}
}
}
impl From<std::io::Error> for Error {
#[track_caller]
fn from(err: std::io::Error) -> Self {
Error {
inner: ChorusError::Io(err),
location: std::panic::Location::caller(),
}
}
}
impl From<heed::Error> for Error {
#[track_caller]
fn from(err: heed::Error) -> Self {
Error {
inner: ChorusError::Lmdb(err),
location: std::panic::Location::caller(),
}
}
}
impl From<tokio_rustls::rustls::Error> for Error {
#[track_caller]
fn from(err: tokio_rustls::rustls::Error) -> Self {
Error {
inner: ChorusError::Rustls(err),
location: std::panic::Location::caller(),
}
}
}
impl From<hyper_tungstenite::tungstenite::error::Error> 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<std::str::Utf8Error> for Error {
#[track_caller]
fn from(err: std::str::Utf8Error) -> Self {
Error {
inner: ChorusError::Utf8(err),
location: std::panic::Location::caller(),
}
}
}
impl From<hyper_tungstenite::tungstenite::error::ProtocolError> for Error {
#[track_caller]
fn from(err: hyper_tungstenite::tungstenite::error::ProtocolError) -> Self {
Error {
inner: ChorusError::WebsocketProtocol(err),
location: std::panic::Location::caller(),
}
}
}

View File

@ -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::<crate::error::Error>::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;

View File

@ -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);
}
}

View File

@ -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?;

View File

@ -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)

View File

@ -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<TlsAcceptor, Error> {
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()

View File

@ -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())
}
}

View File

@ -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<Event<'a>, 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<usize, std::io::Error> {
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 = <sha256::Hash as AsRef<[u8]>>::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())?;

View File

@ -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

View File

@ -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<Filter<'a>, 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(())

View File

@ -1,4 +1,4 @@
use crate::Error;
use crate::error::Error;
use serde::{Deserialize, Serialize};
use std::fmt;

View File

@ -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<usize, Error> {
// 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<usize, Error> {
macro_rules! output_slice {
($slice:expr, $out:expr, $pos:expr) => {
if $out.len() < *$pos + $slice.len() {
Err(Error::BufferTooSmall)
Err(Into::<crate::error::Error>::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::<crate::error::Error>::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;
}

View File

@ -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<bool, Error
eat_whitespace(input, inposp);
// next comes either comma or end brace
if *inposp >= 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<bool, Error
*inposp += 1;
Ok(false)
} else {
Err(Error::JsonBad("Unexpected char", *inposp))
Err(ChorusError::JsonBad("Unexpected char", *inposp).into())
}
}
pub fn read_id(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 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<u64, Error> {
*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<u16, Error> {
*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<usize, Error> {
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<usize, Error> {
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())
}
}

View File

@ -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<Option<(u32, usize)>, 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<Option<(u32, usize)>, 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<Option<(u32, usize)>, 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<usize, Error> {
*dst.get_unchecked_mut(3) = (code & 0x3F) as u8 | TAG_CONT;
4
} else {
return Err(Error::BufferTooSmall);
return Err(ChorusError::BufferTooSmall.into());
}
};
Ok(len)

View File

@ -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<Tags<'a>, 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(())