use miniz_oxide::inflate::TINFLStatus;
use std::fmt;
const NEAR: usize = 60;
const SHOWN: usize = 2000;
#[derive(Debug)]
pub enum Error {
Socket(zmq::Error),
Decompress(TINFLStatus),
Parse {
source: serde_json::Error,
json: String,
},
}
impl Error {
pub fn near(&self) -> Option<&str> {
let Error::Parse { source, json } = self else {
return None;
};
if source.line() == 0 {
return None;
}
let line = source.line().saturating_sub(1);
let sol: usize =
json.split_inclusive('\n').take(line).map(str::len).sum();
let at = sol
.saturating_add(source.column().saturating_sub(1))
.min(json.len());
let from = (at.saturating_sub(NEAR)..=at)
.find(|i| json.is_char_boundary(*i))
.unwrap_or(at);
let to = ((at + NEAR).min(json.len())..=json.len())
.find(|i| json.is_char_boundary(*i))
.unwrap_or(json.len());
Some(&json[from..to])
}
pub fn json(&self) -> Option<&str> {
match self {
Error::Parse { json, .. } => Some(json),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Socket(err) => write!(f, "socket: {}", err),
Error::Decompress(status) => {
write!(f, "decompress: {:?}", status)
}
Error::Parse { source, json } => {
write!(f, "parse: {}", source)?;
if let Some(near) = self.near() {
return write!(f, ", near: {}", near);
}
let to = (SHOWN..=json.len())
.find(|i| json.is_char_boundary(*i))
.unwrap_or(json.len());
write!(f, ", message: {}", &json[..to])?;
if to < json.len() {
write!(f, "... ({} characters in all)", json.len())?;
}
Ok(())
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Socket(err) => Some(err),
Error::Decompress(_) => None,
Error::Parse { source, .. } => Some(source),
}
}
}