Document more of 'netdoc', including errors.
This commit is contained in:
parent
c4d0f59e7d
commit
07e41734ec
|
@ -1,16 +1,27 @@
|
|||
//! Types used to parse arguments of entries in a directory document.
|
||||
//!
|
||||
//! There are some types that are pretty common, like "ISOTime",
|
||||
//! "base64-encoded data", and so on.
|
||||
//!
|
||||
//! These types shouldn't be exposed outside of the netdoc crate.
|
||||
|
||||
pub use b64impl::*;
|
||||
pub use curve25519impl::*;
|
||||
pub use timeimpl::*;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// A problem that can occur when parsing an argument.
|
||||
#[derive(Error, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ArgError {
|
||||
/// Invalid base64.
|
||||
#[error("bad base64 encoding: {0}")]
|
||||
Base64(#[from] base64::DecodeError),
|
||||
/// A time that was in the wrong form, or out of range.
|
||||
#[error("invalid time: {0}")]
|
||||
BadTime(#[from] chrono::ParseError),
|
||||
/// Some other error, represented as a string.
|
||||
#[error("{0}")]
|
||||
Generic(&'static str),
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//! Error type from parsing a document, and the position where it occurred
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::policy::PolicyError;
|
||||
|
@ -5,17 +6,33 @@ use std::fmt;
|
|||
|
||||
/// A position within a directory object. Used to tell where an error
|
||||
/// occurred.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Position {
|
||||
/// The error did not occur at any particular position.
|
||||
///
|
||||
/// This can happen when the error is something like a missing entry:
|
||||
/// the entry is supposed to go _somewhere_, but we can't say where.
|
||||
None,
|
||||
/// The error occurred at an unknown position.
|
||||
///
|
||||
/// We should avoid using this case.
|
||||
Unknown,
|
||||
/// The error occurred at an invalid offset within the string, or
|
||||
/// outside the string entirely.
|
||||
///
|
||||
/// This can only occur because of an internal error of some kind.
|
||||
Invalid(usize),
|
||||
/// The error occurred at a particular byte within the string.
|
||||
///
|
||||
/// We try to conver these to a Pos before displaying them to the user.
|
||||
Byte { off: usize },
|
||||
/// The error occurred at a particular line (and possibly at a
|
||||
/// particular byte within the line.)
|
||||
Pos { line: usize, byte: usize },
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// Construct a Position from an offset within a slice.
|
||||
/// Construct a Position from an offset within a &str slice.
|
||||
pub fn from_offset(s: &str, off: usize) -> Self {
|
||||
if off > s.len() || !s.is_char_boundary(off) {
|
||||
Position::Invalid(off)
|
||||
|
@ -34,9 +51,18 @@ impl Position {
|
|||
}
|
||||
}
|
||||
}
|
||||
/// Construct a position from a byte offset.
|
||||
pub fn from_byte(off: usize) -> Self {
|
||||
Position::Byte { off }
|
||||
}
|
||||
/// Given a position, if it was at a byte offset, convert it to a
|
||||
/// line-and-byte position within `s`.
|
||||
///
|
||||
/// Requires that this position was actually generated from `s`.
|
||||
/// If it was not, the results here may be nonsensical.
|
||||
///
|
||||
/// TODO: I wish I knew an efficient safe way to do this that
|
||||
/// guaranteed that we we always talking about the right string.
|
||||
pub fn within(self, s: &str) -> Self {
|
||||
match self {
|
||||
Position::Byte { off } => Self::from_offset(s, off),
|
||||
|
@ -58,81 +84,122 @@ impl fmt::Display for Position {
|
|||
}
|
||||
}
|
||||
|
||||
/// An error that occurred while parsing a directory object of some kind.
|
||||
#[derive(Error, Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// An internal error in the parser: these should never happen.
|
||||
#[error("internal error{0}")]
|
||||
Internal(Position),
|
||||
Internal(Position), // TODO string.
|
||||
/// An entry was found with no keyword.
|
||||
#[error("no keyword for entry{0}")]
|
||||
MissingKeyword(Position),
|
||||
/// An entry was found with no newline at the end.
|
||||
#[error("line truncated before newline{0}")]
|
||||
TruncatedLine(Position),
|
||||
/// A bad string was found in the keyword position.
|
||||
#[error("invalid keyword{0}")]
|
||||
BadKeyword(Position),
|
||||
/// We found an ill-formed "BEGIN FOO" tag.
|
||||
#[error("invalid PEM BEGIN tag{0}")]
|
||||
BadObjectBeginTag(Position),
|
||||
/// We found an ill-formed "END FOO" tag.
|
||||
#[error("invalid PEM END tag{0}")]
|
||||
BadObjectEndTag(Position),
|
||||
/// We found a "BEGIN FOO" tag with an "END FOO" tag that didn't match.
|
||||
#[error("mismatched PEM tags{0}")]
|
||||
BadObjectMismatchedTag(Position),
|
||||
/// We found a base64 object with an invalid base64 encoding.
|
||||
#[error("invalid base64 in object around byte {0}")]
|
||||
BadObjectBase64(Position),
|
||||
/// The document is not supposed to contain more than one of some
|
||||
/// kind of entry, but we found one anyway.
|
||||
#[error("duplicate entry for {0}{1}")]
|
||||
DuplicateToken(&'static str, Position),
|
||||
/// The document is not supposed to contain any of some particular kind
|
||||
/// of entry, but we found one anyway.
|
||||
#[error("entry {0} unexpected{1}")]
|
||||
UnexpectedToken(&'static str, Position),
|
||||
/// The document is not supposed to contain any of some particular kind
|
||||
/// of entry, but we found one anyway.
|
||||
#[error("didn't find required entry {0}")]
|
||||
MissingToken(&'static str),
|
||||
/// We found more arguments on an entry than it is allowed to hav.e
|
||||
#[error("too many arguments for {0}{1}")]
|
||||
TooManyArguments(&'static str, Position),
|
||||
/// We didn't fine enough arguments for some entry.
|
||||
#[error("too few arguments for {0}{1}")]
|
||||
TooFewArguments(&'static str, Position),
|
||||
/// We found an object attached to an entry that isn't supposed to
|
||||
/// have one.
|
||||
#[error("unexpected object for {0}{1}")]
|
||||
UnexpectedObject(&'static str, Position),
|
||||
/// An entry was supposed to have an object, but it didn't.
|
||||
#[error("missing object for {0}{1}")]
|
||||
MissingObject(&'static str, Position),
|
||||
/// We found an object on an entry, but the type was wrong.
|
||||
#[error("wrong object type for entry{0}")]
|
||||
WrongObject(Position),
|
||||
/// We tried to find an argument that we were sure would be there,
|
||||
/// but it wasn't!
|
||||
///
|
||||
/// This error should never occur in correct code; it should be
|
||||
/// caught earlier by TooFewArguments.
|
||||
#[error("missing argument for entry{0}")]
|
||||
MissingArgument(Position),
|
||||
/// We found an argument that couldn't be parsed.
|
||||
#[error("bad argument {0} for entry{1}: {2}")]
|
||||
BadArgument(usize, Position, String), // converting to a string doesn't sit well with me. XXXX
|
||||
/// We found an object that couldn't be parsed after it was decoded.
|
||||
#[error("bad object for entry{0}: {1}")]
|
||||
BadObjectVal(Position, String), // converting to a string doesn't sit well with me. XXXX
|
||||
/// There was some signature that we couldn't validate.
|
||||
#[error("couldn't validate signature{0}")]
|
||||
BadSignature(Position),
|
||||
BadSignature(Position), // say which kind of signature. TODO
|
||||
/// There was a tor version we couldn't parse.
|
||||
#[error("couldn't parse Tor version{0}")]
|
||||
BadVersion(Position),
|
||||
BadVersion(Position), // collapse into something else.
|
||||
/// There was an ipv4 or ipv6 policy entry that we couldn't parse.
|
||||
#[error("invalid policy entry{0}: {1}")]
|
||||
BadPolicy(Position, #[source] PolicyError),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn within(self, s: &str) -> Error {
|
||||
/// Helper: return a mutable reference to this error's position (if any)
|
||||
fn pos_mut(&mut self) -> Option<&mut Position> {
|
||||
use Error::*;
|
||||
match self {
|
||||
Internal(p) => Internal(p.within(s)),
|
||||
MissingKeyword(p) => MissingKeyword(p.within(s)),
|
||||
TruncatedLine(p) => TruncatedLine(p.within(s)),
|
||||
BadKeyword(p) => BadKeyword(p.within(s)),
|
||||
BadObjectBeginTag(p) => BadObjectBeginTag(p.within(s)),
|
||||
BadObjectEndTag(p) => BadObjectEndTag(p.within(s)),
|
||||
BadObjectMismatchedTag(p) => BadObjectMismatchedTag(p.within(s)),
|
||||
BadObjectBase64(p) => BadObjectBase64(p.within(s)),
|
||||
DuplicateToken(s2, p) => DuplicateToken(s2, p.within(s)),
|
||||
UnexpectedToken(s2, p) => UnexpectedToken(s2, p.within(s)),
|
||||
MissingToken(s2) => MissingToken(s2),
|
||||
TooManyArguments(s2, p) => TooManyArguments(s2, p.within(s)),
|
||||
TooFewArguments(s2, p) => TooFewArguments(s2, p.within(s)),
|
||||
UnexpectedObject(s2, p) => UnexpectedObject(s2, p.within(s)),
|
||||
MissingObject(s2, p) => MissingObject(s2, p.within(s)),
|
||||
WrongObject(p) => WrongObject(p.within(s)),
|
||||
MissingArgument(p) => MissingArgument(p.within(s)),
|
||||
BadArgument(n, p, s2) => BadArgument(n, p.within(s), s2),
|
||||
BadObjectVal(p, s2) => BadObjectVal(p.within(s), s2),
|
||||
BadSignature(p) => BadSignature(p.within(s)),
|
||||
BadVersion(p) => BadVersion(p.within(s)),
|
||||
BadPolicy(p, e) => BadPolicy(p.within(s), e),
|
||||
Internal(p) => Some(p),
|
||||
MissingKeyword(p) => Some(p),
|
||||
TruncatedLine(p) => Some(p),
|
||||
BadKeyword(p) => Some(p),
|
||||
BadObjectBeginTag(p) => Some(p),
|
||||
BadObjectEndTag(p) => Some(p),
|
||||
BadObjectMismatchedTag(p) => Some(p),
|
||||
BadObjectBase64(p) => Some(p),
|
||||
DuplicateToken(_, p) => Some(p),
|
||||
UnexpectedToken(_, p) => Some(p),
|
||||
MissingToken(_) => None,
|
||||
TooManyArguments(_, p) => Some(p),
|
||||
TooFewArguments(_, p) => Some(p),
|
||||
UnexpectedObject(_, p) => Some(p),
|
||||
MissingObject(_, p) => Some(p),
|
||||
WrongObject(p) => Some(p),
|
||||
MissingArgument(p) => Some(p),
|
||||
BadArgument(_, p, _) => Some(p),
|
||||
BadObjectVal(p, _) => Some(p),
|
||||
BadSignature(p) => Some(p),
|
||||
BadVersion(p) => Some(p),
|
||||
BadPolicy(p, _) => Some(p),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a new error based on this one, with any byte-based
|
||||
/// position mapped to some line within a string.
|
||||
pub fn within(mut self, s: &str) -> Error {
|
||||
if let Some(p) = self.pos_mut() {
|
||||
*p = p.within(s);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
//! TODO: This crate has several pieces that should probably be split out
|
||||
//! into other smaller cases, including handling for version numbers
|
||||
//! and exit policies.
|
||||
//!
|
||||
//! TODO: Many parts of this crate that should be public aren't.
|
||||
//!
|
||||
//! TODO: this crate needs far more tests!
|
||||
|
||||
#![allow(dead_code)]
|
||||
//#![warn(missing_docs)]
|
||||
|
@ -27,7 +31,7 @@ mod rules;
|
|||
mod tokenize;
|
||||
mod util;
|
||||
#[macro_use]
|
||||
mod macros; // xxxx
|
||||
mod macros;
|
||||
pub mod policy;
|
||||
pub mod routerdesc;
|
||||
pub mod version;
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
//! Break a string into a set of directory-object Items.
|
||||
//!
|
||||
//! This module defines Item, which represents a basic entry in a
|
||||
//! directory document, and NetDocReader, which is used to break a
|
||||
//! string into Items.
|
||||
|
||||
use crate::{Error, Position, Result};
|
||||
use std::cell::{Ref, RefCell};
|
||||
|
@ -8,7 +12,9 @@ use std::str::FromStr;
|
|||
///
|
||||
/// This represents a single blob within a pair of "-----BEGIN
|
||||
/// FOO-----" and "-----END FOO-----". The data is not guaranteed to
|
||||
/// be actual base64 when this object is created.
|
||||
/// be actual base64 when this object is created: doing so would
|
||||
/// require either that we parse the base64 twice, or that we allocate
|
||||
/// a buffer to hold the data before it's needed.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Object<'a> {
|
||||
tag: &'a str,
|
||||
|
|
Loading…
Reference in New Issue