Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
chanced committed Jan 30, 2025
1 parent 52ce3fd commit 254426b
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 35 deletions.
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adds `Pointer::starts_with` and `Pointer::ends_with` for prefix and suffix matching.
- Adds new `ParseIndexError` variant to express the presence non-digit characters in the token.
- Adds `Token::is_next` for checking if a token represents the `-` character.
- Adds `ParseBufError`, returned as the `Err` variant of `PointerBuf::parse`, which includes the input `String` (from `Into::into`).
- Adds `InvalidEncoding`
- Adds `InvalidEncoding` to represent the two possible encoding errors when decoding a token.
- Adds `diagnostic` mod, including:
- Adds `diagnotic::Diagnostic` trait to facilitate error reporting and
`miette` integration. All errors intended for usage with `assign::Assign` or
`resolve::Resolve` must implement this trait.
- Adds `"miette"` featureflag to enable `miette` integration for error reporting.

### Changed

Expand All @@ -25,11 +29,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Pointer::get` now accepts ranges and can produce `Pointer` segments as output (similar to
`slice::get`).
- `PointerBuf::parse` now returns `BufParseError` instead of `ParseError`.
- Adds field `position` to variants of `resolve::Error` and `assign::Error` to indicate the
token index of where the error occurred.
- Renames `ParseError::NoLeadingBackslash` to `ParseError::NoLeadingSlash`.

### Fixed

- Make validation of array indices conform to RFC 6901 in the presence of non-digit characters.

### Deprecated

- `assign::AssignError` renamed to `assign::Error`
- `resolve::ResolveError` renamed to `resolve::Error`
- `InvalidEncodingError` renamed to `EncodingError`

## [0.6.2] 2024-09-30

### Added
Expand Down
38 changes: 29 additions & 9 deletions src/assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,38 @@ impl Error {
Self::OutOfBounds { offset, .. } | Self::FailedToParseIndex { offset, .. } => *offset,
}
}

/// Returns `true` if the error is [`OutOfBounds`].
///
/// [`OutOfBounds`]: Error::OutOfBounds
#[must_use]
pub fn is_out_of_bounds(&self) -> bool {
matches!(self, Self::OutOfBounds { .. })
}

/// Returns `true` if the error is [`FailedToParseIndex`].
///
/// [`FailedToParseIndex`]: Error::FailedToParseIndex
#[must_use]
pub fn is_failed_to_parse_index(&self) -> bool {
matches!(self, Self::FailedToParseIndex { .. })
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"value failed to be assigned, caused by token (position: {}, offset: {}) of the json pointer",
self.position(),
self.offset()
)
match self {
Self::FailedToParseIndex { offset, .. } => {
write!(
f,
"assign failed: json pointer token at offset {offset} failed to parse as an array index"
)
}
Self::OutOfBounds { offset, .. } => write!(
f,
"assign failed: json pointer token at offset {offset} is out of bounds",
),
}
}
}

Expand All @@ -203,9 +225,7 @@ impl Diagnostic for Error {
};
let len = token.encoded().len();
let text = match self {
Error::FailedToParseIndex { .. } => {
format!("expected array index or '-', found \"{}\"", token.decoded())
}
Error::FailedToParseIndex { .. } => "expected array index or '-'".to_string(),
Error::OutOfBounds { source, .. } => {
format!("{} is out of bounds (len: {})", source.index, source.length)
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl fmt::Display for OutOfBoundsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"index {} out of bounds (limit: {})",
"index {} out of bounds (len: {})",
self.index, self.length
)
}
Expand Down
29 changes: 15 additions & 14 deletions src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,11 @@ impl Pointer {
}

/// Splits the `Pointer` at the given index if the character at the index is
/// a separator backslash (`'/'`), returning `Some((head, tail))`. Otherwise,
/// a separator slash (`'/'`), returning `Some((head, tail))`. Otherwise,
/// returns `None`.
///
/// For the following JSON Pointer, the following splits are possible (0, 4, 8):
/// For the following JSON Pointer, the following splits are possible (0, 4,
/// 8):
/// ```text
/// /foo/bar/baz
/// ↑ ↑ ↑
Expand Down Expand Up @@ -1166,8 +1167,8 @@ impl fmt::Display for NoLeadingSlash {
/// Indicates that a `Pointer` was malformed and unable to be parsed.
#[derive(Debug, PartialEq)]
pub enum ParseError {
/// `Pointer` did not start with a backslash (`'/'`).
NoLeadingBackslash,
/// `Pointer` did not start with a slash (`'/'`).
NoLeadingSlash,

/// `Pointer` contained invalid encoding (e.g. `~` not followed by `0` or
/// `1`).
Expand All @@ -1185,14 +1186,14 @@ impl ParseError {
/// invalid encoding
pub fn offset(&self) -> usize {
match self {
Self::NoLeadingBackslash => 0,
Self::NoLeadingSlash => 0,
Self::InvalidEncoding { offset, .. } => *offset,
}
}
/// Length of the invalid encoding
pub fn invalid_encoding_len(&self, subject: &str) -> usize {
match self {
Self::NoLeadingBackslash => 0,
Self::NoLeadingSlash => 0,
Self::InvalidEncoding { offset, .. } => {
if *offset < subject.len() - 1 {
2
Expand All @@ -1215,7 +1216,7 @@ impl Diagnostic for ParseError {
let offset = self.complete_offset();
let len = self.invalid_encoding_len(subject);
let text = match self {
ParseError::NoLeadingBackslash => "must start with a backslash ('/')",
ParseError::NoLeadingSlash => "must start with a backslash ('/')",
ParseError::InvalidEncoding { .. } => "'~' must be followed by '0' or '1'",
}
.to_string();
Expand All @@ -1229,7 +1230,7 @@ impl miette::Diagnostic for ParseError {}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoLeadingBackslash { .. } => {
Self::NoLeadingSlash { .. } => {
write!(
f,
"json pointer failed to parse; does not start with a backslash ('/') and is not empty"
Expand All @@ -1248,7 +1249,7 @@ impl fmt::Display for ParseError {
impl ParseError {
/// Returns `true` if this error is `NoLeadingBackslash`
pub fn is_no_leading_backslash(&self) -> bool {
matches!(self, Self::NoLeadingBackslash { .. })
matches!(self, Self::NoLeadingSlash { .. })
}

/// Returns `true` if this error is `InvalidEncoding`
Expand All @@ -1270,7 +1271,7 @@ impl ParseError {
/// ```
pub fn pointer_offset(&self) -> usize {
match *self {
Self::NoLeadingBackslash { .. } => 0,
Self::NoLeadingSlash { .. } => 0,
Self::InvalidEncoding { offset, .. } => offset,
}
}
Expand All @@ -1290,7 +1291,7 @@ impl ParseError {
/// ```
pub fn source_offset(&self) -> usize {
match self {
Self::NoLeadingBackslash { .. } => 0,
Self::NoLeadingSlash { .. } => 0,
Self::InvalidEncoding { source, .. } => source.offset,
}
}
Expand All @@ -1316,7 +1317,7 @@ impl std::error::Error for ParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InvalidEncoding { source, .. } => Some(source),
Self::NoLeadingBackslash => None,
Self::NoLeadingSlash => None,
}
}
}
Expand Down Expand Up @@ -1355,7 +1356,7 @@ const fn validate(value: &str) -> Result<&str, ParseError> {

const fn validate_bytes(bytes: &[u8], offset: usize) -> Result<(), ParseError> {
if bytes[0] != b'/' && offset == 0 {
return Err(ParseError::NoLeadingBackslash);
return Err(ParseError::NoLeadingSlash);
}

let mut ptr_offset = offset; // offset within the pointer of the most recent '/' separator
Expand Down Expand Up @@ -1521,7 +1522,7 @@ mod tests {
("/foo/bar/baz/~10", Ok("/foo/bar/baz/~10")),
("/foo/bar/baz/~11", Ok("/foo/bar/baz/~11")),
("/foo/bar/baz/~1/~0", Ok("/foo/bar/baz/~1/~0")),
("missing-slash", Err(ParseError::NoLeadingBackslash)),
("missing-slash", Err(ParseError::NoLeadingSlash)),
(
"/~",
Err(ParseError::InvalidEncoding {
Expand Down
19 changes: 11 additions & 8 deletions src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,20 +235,23 @@ impl Diagnostic for Error {
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::FailedToParseIndex { .. } => {
write!(f, "value failed to resolve by json pointer because an array index is not a valid integer")
Self::FailedToParseIndex { offset, .. } => {
write!(f, "resolve failed: json pointer token at offset {offset} failed to parse as an index")
}
Self::OutOfBounds { .. } => {
Self::OutOfBounds { offset, .. } => {
write!(
f,
"value failed to resolve by json pointer because an array index is out of bounds"
"resolve failed: json pointer token at offset {offset} is out of bounds"
)
}
Self::NotFound { .. } => {
write!(f, "value failed to resolve by json pointer because a value within the path is not present")
Self::NotFound { offset, .. } => {
write!(
f,
"resolve failed: json pointer token at {offset} was not found in value"
)
}
Self::Unreachable { .. } => {
write!(f, "value failed to resolve by json pointer because a scalar or null value was encountered before exhausting the path")
Self::Unreachable { offset, .. } => {
write!(f, "resolve failed: json pointer token at {offset} is unreachable (previous token resolved to a scalar or null value)")
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ pub struct EncodingError {

#[cfg(feature = "std")]
impl std::error::Error for EncodingError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
Expand Down

0 comments on commit 254426b

Please sign in to comment.