use thiserror::Error;
use crate::types::policy::PolicyError;
use std::{borrow::Cow, fmt, sync::Arc};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum Pos {
None,
Unknown,
Invalid(usize),
Byte {
off: usize,
},
PosInLine {
line: usize,
byte: usize,
},
Raw {
ptr: *const u8,
},
}
unsafe impl Send for Pos {}
unsafe impl Sync for Pos {}
impl Pos {
pub fn from_offset(s: &str, off: usize) -> Self {
if off > s.len() || !s.is_char_boundary(off) {
Pos::Invalid(off)
} else {
let s = &s[..off];
let last_nl = s.rfind('\n');
match last_nl {
Some(pos) => {
let newlines = s.bytes().filter(|b| *b == b'\n').count();
Pos::PosInLine {
line: newlines + 1,
byte: off - pos,
}
}
None => Pos::PosInLine {
line: 1,
byte: off + 1,
},
}
}
}
pub fn at(s: &str) -> Self {
let ptr = s.as_ptr();
Pos::Raw { ptr }
}
pub fn at_end_of(s: &str) -> Self {
let ending = &s[s.len()..];
Pos::at(ending)
}
pub fn from_byte(off: usize) -> Self {
Pos::Byte { off }
}
pub fn from_line(line: usize, byte: usize) -> Self {
Pos::PosInLine { line, byte }
}
pub(crate) fn offset_within(&self, s: &str) -> Option<usize> {
match self {
Pos::Byte { off } => Some(*off),
Pos::Raw { ptr } => offset_in(*ptr, s),
_ => None,
}
}
#[must_use]
pub fn within(self, s: &str) -> Self {
match self {
Pos::Byte { off } => Self::from_offset(s, off),
Pos::Raw { ptr } => {
if let Some(off) = offset_in(ptr, s) {
Self::from_offset(s, off)
} else {
self
}
}
_ => self,
}
}
}
fn offset_in(ptr: *const u8, s: &str) -> Option<usize> {
let ptr_u = ptr as usize;
let start_u = s.as_ptr() as usize;
let end_u = (s.as_ptr() as usize) + s.len();
if start_u <= ptr_u && ptr_u < end_u {
Some(ptr_u - start_u)
} else {
None
}
}
impl fmt::Display for Pos {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Pos::*;
match self {
None => write!(f, ""),
Unknown => write!(f, " at unknown position"),
Invalid(off) => write!(f, " at invalid offset at index {}", off),
Byte { off } => write!(f, " at byte {}", off),
PosInLine { line, byte } => write!(f, " on line {}, byte {}", line, byte),
Raw { ptr } => write!(f, " at {:?}", ptr),
}
}
}
#[derive(Copy, Clone, Debug, derive_more::Display, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseErrorKind {
#[display(fmt = "internal error")]
Internal,
#[display(fmt = "bad API usage")]
BadApiUsage,
#[display(fmt = "no keyword for entry")]
MissingKeyword,
#[display(fmt = "line truncated before newline")]
TruncatedLine,
#[display(fmt = "invalid keyword")]
BadKeyword,
#[display(fmt = "invalid PEM BEGIN tag")]
BadObjectBeginTag,
#[display(fmt = "invalid PEM END tag")]
BadObjectEndTag,
#[display(fmt = "mismatched PEM tags")]
BadObjectMismatchedTag,
#[display(fmt = "invalid base64 in object")]
BadObjectBase64,
#[display(fmt = "duplicate entry")]
DuplicateToken,
#[display(fmt = "unexpected entry")]
UnexpectedToken,
#[display(fmt = "didn't find required entry")]
MissingToken,
#[display(fmt = "entry out of place")]
MisplacedToken,
#[display(fmt = "too many arguments")]
TooManyArguments,
#[display(fmt = "too few arguments")]
TooFewArguments,
#[display(fmt = "unexpected object")]
UnexpectedObject,
#[display(fmt = "missing object")]
MissingObject,
#[display(fmt = "wrong object type")]
WrongObject,
#[display(fmt = "missing argument")]
MissingArgument,
#[display(fmt = "bad argument for entry")]
BadArgument,
#[display(fmt = "bad object for entry")]
BadObjectVal,
#[display(fmt = "couldn't validate signature")]
BadSignature, #[display(fmt = "couldn't parse Tor version")]
BadTorVersion,
#[display(fmt = "invalid policy entry")]
BadPolicy,
#[display(fmt = "decoding error")]
Undecodable,
#[display(fmt = "unrecognized document version")]
BadDocumentVersion,
#[display(fmt = "unexpected document type")]
BadDocumentType,
#[display(fmt = "Wrong starting token")]
WrongStartingToken,
#[display(fmt = "Wrong ending token")]
WrongEndingToken,
#[display(fmt = "Incorrect sort order")]
WrongSortOrder,
#[display(fmt = "Invalid consensus lifetime")]
InvalidLifetime,
#[display(fmt = "Empty line")]
EmptyLine,
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub(crate) enum ParseErrorSource {
#[error("Error parsing binary object")]
Bytes(#[from] tor_bytes::Error),
#[error("Error parsing policy")]
Policy(#[from] PolicyError),
#[error("Couldn't parse integer")]
Int(#[from] std::num::ParseIntError),
#[error("Couldn't parse address")]
Address(#[from] std::net::AddrParseError),
#[error("Invalid signature")]
Signature(#[source] Arc<signature::Error>),
#[error("Invalid certificate")]
CertSignature(#[from] tor_cert::CertError),
#[error("Protocol versions")]
Protovers(#[from] tor_protover::ParseError),
#[error("Internal error or bug")]
Bug(#[from] tor_error::Bug),
}
impl ParseErrorKind {
#[must_use]
pub(crate) fn err(self) -> Error {
Error {
kind: self,
msg: None,
pos: Pos::Unknown,
source: None,
}
}
#[must_use]
pub(crate) fn at_pos(self, pos: Pos) -> Error {
self.err().at_pos(pos)
}
#[must_use]
pub(crate) fn with_msg<T>(self, msg: T) -> Error
where
T: Into<Cow<'static, str>>,
{
self.err().with_msg(msg)
}
}
impl From<signature::Error> for ParseErrorSource {
fn from(err: signature::Error) -> Self {
ParseErrorSource::Signature(Arc::new(err))
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Error {
kind: ParseErrorKind,
msg: Option<Cow<'static, str>>,
pos: Pos,
source: Option<ParseErrorSource>,
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.msg == other.msg && self.pos == other.pos
}
}
impl Error {
pub(crate) fn pos(&self) -> Pos {
self.pos
}
#[must_use]
pub fn within(mut self, s: &str) -> Error {
self.pos = self.pos.within(s);
self
}
#[must_use]
pub fn at_pos(mut self, p: Pos) -> Error {
self.pos = p;
self
}
#[must_use]
pub fn or_at_pos(mut self, p: Pos) -> Error {
match self.pos {
Pos::None | Pos::Unknown => {
self.pos = p;
}
_ => (),
}
self
}
#[must_use]
pub(crate) fn with_msg<T>(mut self, message: T) -> Error
where
T: Into<Cow<'static, str>>,
{
self.msg = Some(message.into());
self
}
#[must_use]
pub(crate) fn with_source<T>(mut self, source: T) -> Error
where
T: Into<ParseErrorSource>,
{
self.source = Some(source.into());
self
}
pub fn parse_error_kind(&self) -> ParseErrorKind {
self.kind
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.kind, self.pos)?;
if let Some(msg) = &self.msg {
write!(f, ": {}", msg)?;
}
Ok(())
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|s| s as _)
}
}
macro_rules! declare_into {
{$source:ty => $kind:ident} => {
impl From<$source> for Error {
fn from(source: $source) -> Error {
Error {
kind: ParseErrorKind::$kind,
msg: None,
pos: Pos::Unknown,
source: Some(source.into())
}
}
}
}
}
declare_into! { signature::Error => BadSignature }
declare_into! { tor_bytes::Error => Undecodable }
declare_into! { std::num::ParseIntError => BadArgument }
declare_into! { std::net::AddrParseError => BadArgument }
declare_into! { PolicyError => BadPolicy }
impl From<tor_error::Bug> for Error {
fn from(err: tor_error::Bug) -> Self {
use tor_error::HasKind;
let kind = match err.kind() {
tor_error::ErrorKind::BadApiUsage => ParseErrorKind::BadApiUsage,
_ => ParseErrorKind::Internal,
};
Error {
kind,
msg: None,
pos: Pos::Unknown,
source: Some(err.into()),
}
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum BuildError {
#[error("cannot build document: {0}")]
CannotBuild(&'static str),
#[error("unable to parse argument")]
Parse(#[from] crate::err::Error),
}